1 package de.juplo.plugins.hibernate;
4 import com.pyx4j.log4j.MavenLogAppender;
6 import java.io.FileInputStream;
7 import java.io.FileOutputStream;
8 import java.io.IOException;
9 import java.io.InputStream;
10 import java.net.MalformedURLException;
12 import java.security.NoSuchAlgorithmException;
13 import java.util.Collections;
14 import java.util.EnumSet;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.Iterator;
18 import java.util.LinkedHashSet;
19 import java.util.List;
21 import java.util.Map.Entry;
22 import java.util.Properties;
24 import java.util.regex.Matcher;
25 import java.util.regex.Pattern;
26 import javax.persistence.Embeddable;
27 import javax.persistence.Entity;
28 import javax.persistence.MappedSuperclass;
29 import javax.persistence.spi.PersistenceUnitTransactionType;
30 import org.apache.maven.artifact.Artifact;
31 import org.apache.maven.model.Resource;
32 import org.apache.maven.plugin.AbstractMojo;
33 import org.apache.maven.plugin.MojoExecutionException;
34 import org.apache.maven.plugin.MojoFailureException;
35 import org.apache.maven.project.MavenProject;
36 import org.hibernate.boot.MetadataBuilder;
37 import org.hibernate.boot.MetadataSources;
38 import org.hibernate.boot.cfgxml.internal.ConfigLoader;
39 import org.hibernate.boot.cfgxml.spi.LoadedConfig;
40 import org.hibernate.boot.cfgxml.spi.MappingReference;
41 import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
42 import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
43 import org.hibernate.boot.registry.BootstrapServiceRegistry;
44 import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
45 import org.hibernate.boot.registry.StandardServiceRegistry;
46 import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
47 import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
48 import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
49 import org.hibernate.boot.registry.selector.spi.StrategySelector;
50 import org.hibernate.boot.spi.MetadataImplementor;
51 import org.hibernate.cfg.AvailableSettings;
52 import static org.hibernate.cfg.AvailableSettings.DIALECT;
53 import static org.hibernate.cfg.AvailableSettings.DRIVER;
54 import static org.hibernate.cfg.AvailableSettings.FORMAT_SQL;
55 import static org.hibernate.cfg.AvailableSettings.HBM2DDL_DELIMITER;
56 import static org.hibernate.cfg.AvailableSettings.HBM2DLL_CREATE_NAMESPACES;
57 import static org.hibernate.cfg.AvailableSettings.IMPLICIT_NAMING_STRATEGY;
58 import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_DRIVER;
59 import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_PASSWORD;
60 import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_URL;
61 import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_USER;
62 import static org.hibernate.cfg.AvailableSettings.PASS;
63 import static org.hibernate.cfg.AvailableSettings.PHYSICAL_NAMING_STRATEGY;
64 import static org.hibernate.cfg.AvailableSettings.SHOW_SQL;
65 import static org.hibernate.cfg.AvailableSettings.USER;
66 import static org.hibernate.cfg.AvailableSettings.URL;
67 import org.hibernate.engine.config.spi.ConfigurationService;
68 import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
69 import org.hibernate.internal.util.config.ConfigurationException;
70 import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor;
71 import org.hibernate.jpa.boot.internal.PersistenceXmlParser;
72 import org.hibernate.tool.schema.TargetType;
73 import org.hibernate.tool.schema.internal.ExceptionHandlerCollectingImpl;
74 import org.hibernate.tool.schema.internal.exec.ScriptTargetOutputToFile;
75 import org.hibernate.tool.schema.spi.ExecutionOptions;
76 import org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator;
77 import org.hibernate.tool.schema.spi.ScriptTargetOutput;
78 import org.hibernate.tool.schema.spi.TargetDescriptor;
79 import org.scannotation.AnnotationDB;
83 * Baseclass with common attributes and methods.
85 * @phase process-classes
87 * @requiresDependencyResolution runtime
89 public abstract class AbstractSchemaMojo extends AbstractMojo
91 public final static String EXPORT = "hibernate.schema.export";
92 public final static String OUTPUTDIRECTORY = "project.build.outputDirectory";
93 public final static String SCAN_CLASSES = "hibernate.schema.scan.classes";
94 public final static String SCAN_DEPENDENCIES = "hibernate.schema.scan.dependencies";
95 public final static String SCAN_TESTCLASSES = "hibernate.schema.scan.test_classes";
96 public final static String TEST_OUTPUTDIRECTORY = "project.build.testOutputDirectory";
97 public final static String SKIPPED = "hibernate.schema.skipped";
99 private final static Pattern SPLIT = Pattern.compile("[^,\\s]+");
101 private final Set<String> packages = new HashSet<String>();
107 * Only needed internally.
109 * @parameter property="project"
113 private MavenProject project;
118 * Only needed internally.
120 * @parameter property="project.build.directory"
124 private String buildDirectory;
127 /** Parameters to configure the genaration of the SQL *********************/
130 * Export the database-schma to the database.
131 * If set to <code>false</code>, only the SQL-script is created and the
132 * database is not touched.
134 * <strong>Important:</strong>
135 * This configuration value can only be configured through the
136 * <code>pom.xml</code>, or by the definition of a system-property, because
137 * it is not known by Hibernate nor JPA and, hence, not picked up from
138 * their configuration!
140 * @parameter property="hibernate.schema.export" default-value="true"
143 private Boolean export;
148 * If set to <code>true</code>, the execution is skipped.
150 * A skipped execution is signaled via the maven-property
151 * <code>${hibernate.schema.skipped}</code>.
153 * The execution is skipped automatically, if no modified or newly added
154 * annotated classes are found and the dialect was not changed.
156 * <strong>Important:</strong>
157 * This configuration value can only be configured through the
158 * <code>pom.xml</code>, or by the definition of a system-property, because
159 * it is not known by Hibernate nor JPA and, hence, not picked up from
160 * their configuration!
162 * @parameter property="hibernate.schema.skip" default-value="${maven.test.skip}"
165 private boolean skip;
170 * Force execution, even if no modified or newly added annotated classes
171 * where found and the dialect was not changed.
173 * <code>skip</code> takes precedence over <code>force</code>.
175 * <strong>Important:</strong>
176 * This configuration value can only be configured through the
177 * <code>pom.xml</code>, or by the definition of a system-property, because
178 * it is not known by Hibernate nor JPA and, hence, not picked up from
179 * their configuration!
181 * @parameter property="hibernate.schema.force" default-value="false"
184 private boolean force;
189 * @parameter property="hibernate.dialect"
192 private String dialect;
195 * Delimiter in output-file.
197 * <strong>Important:</strong>
198 * This configuration value can only be configured through the
199 * <code>pom.xml</code>, or by the definition of a system-property, because
200 * it is not known by Hibernate nor JPA and, hence, not picked up from
201 * their configuration!
203 * @parameter property="hibernate.hbm2ddl.delimiter" default-value=";"
206 private String delimiter;
209 * Show the generated SQL in the command-line output.
211 * @parameter property="hibernate.show_sql"
214 private Boolean show;
217 * Format output-file.
219 * @parameter property="hibernate.format_sql"
222 private Boolean format;
225 * Specifies whether to automatically create also the database schema/catalog.
227 * @parameter property="hibernate.hbm2dll.create_namespaces" default-value="false"
230 private Boolean createNamespaces;
233 * Implicit naming strategy
235 * @parameter property="hibernate.implicit_naming_strategy"
238 private String implicitNamingStrategy;
241 * Physical naming strategy
243 * @parameter property="hibernate.physical_naming_strategy"
246 private String physicalNamingStrategy;
249 * Wether the project should be scanned for annotated-classes, or not
251 * This parameter is intended to allow overwriting of the parameter
252 * <code>exclude-unlisted-classes</code> of a <code>persistence-unit</code>.
253 * If not specified, it defaults to <code>true</code>
255 * @parameter property="hibernate.schema.scan.classes"
258 private Boolean scanClasses;
261 * Classes-Directory to scan.
263 * This parameter defaults to the maven build-output-directory for classes.
264 * Additionally, all dependencies are scanned for annotated classes.
266 * <strong>Important:</strong>
267 * This configuration value can only be configured through the
268 * <code>pom.xml</code>, or by the definition of a system-property, because
269 * it is not known by Hibernate nor JPA and, hence, not picked up from
270 * their configuration!
272 * @parameter property="project.build.outputDirectory"
275 private String outputDirectory;
278 * Dependency-Scopes, that should be scanned for annotated classes.
280 * By default, only dependencies in the scope <code>compile</code> are
281 * scanned for annotated classes. Multiple scopes can be seperated by
282 * white space or commas.
284 * If you do not want any dependencies to be scanned for annotated
285 * classes, set this parameter to <code>none</code>.
287 * The plugin does not scan for annotated classes in transitive
288 * dependencies. If some of your annotated classes are hidden in a
289 * transitive dependency, you can simply add that dependency explicitly.
291 * @parameter property="hibernate.schema.scan.dependencies" default-value="compile"
294 private String scanDependencies;
297 * Whether to scan the test-branch of the project for annotated classes, or
300 * If this parameter is set to <code>true</code> the test-classes of the
301 * artifact will be scanned for hibernate-annotated classes additionally.
303 * <strong>Important:</strong>
304 * This configuration value can only be configured through the
305 * <code>pom.xml</code>, or by the definition of a system-property, because
306 * it is not known by Hibernate nor JPA and, hence, not picked up from
307 * their configuration!
309 * @parameter property="hibernate.schema.scan.test_classes" default-value="false"
312 private Boolean scanTestClasses;
315 * Test-Classes-Directory to scan.
317 * This parameter defaults to the maven build-output-directory for
320 * This parameter is only used, when <code>scanTestClasses</code> is set
321 * to <code>true</code>!
323 * <strong>Important:</strong>
324 * This configuration value can only be configured through the
325 * <code>pom.xml</code>, or by the definition of a system-property, because
326 * it is not known by Hibernate nor JPA and, hence, not picked up from
327 * their configuration!
329 * @parameter property="project.build.testOutputDirectory"
332 private String testOutputDirectory;
335 /** Conection parameters *************************************************/
340 * @parameter property="hibernate.connection.driver_class"
343 private String driver;
348 * @parameter property="hibernate.connection.url"
356 * @parameter property="hibernate.connection.username"
359 private String username;
364 * @parameter property="hibernate.connection.password"
367 private String password;
370 /** Parameters to locate configuration sources ****************************/
373 * Path to a file or name of a ressource with hibernate properties.
374 * If this parameter is specified, the plugin will try to load configuration
375 * values from a file with the given path or a ressource on the classpath with
376 * the given name. If both fails, the execution of the plugin will fail.
378 * If this parameter is not set the plugin will load configuration values
379 * from a ressource named <code>hibernate.properties</code> on the classpath,
380 * if it is present, but will not fail if there is no such ressource.
382 * During ressource-lookup, the test-classpath takes precedence.
387 private String hibernateProperties;
390 * Path to Hibernate configuration file (.cfg.xml).
391 * If this parameter is specified, the plugin will try to load configuration
392 * values from a file with the given path or a ressource on the classpath with
393 * the given name. If both fails, the execution of the plugin will fail.
395 * If this parameter is not set the plugin will load configuration values
396 * from a ressource named <code>hibernate.cfg.xml</code> on the classpath,
397 * if it is present, but will not fail if there is no such ressource.
399 * During ressource-lookup, the test-classpath takes precedence.
401 * Settings in this file will overwrite settings in the properties file.
406 private String hibernateConfig;
409 * Name of the persistence-unit.
410 * If this parameter is specified, the plugin will try to load configuration
411 * values from a persistence-unit with the specified name. If no such
412 * persistence-unit can be found, the plugin will throw an exception.
414 * If this parameter is not set and there is only one persistence-unit
415 * available, that unit will be used automatically. But if this parameter is
416 * not set and there are multiple persistence-units available on,
417 * the class-path, the execution of the plugin will fail.
419 * Settings in this file will overwrite settings in the properties or the
420 * configuration file.
425 private String persistenceUnit;
428 * List of Hibernate-Mapping-Files (XML).
429 * Multiple files can be separated with white-spaces and/or commas.
431 * @parameter property="hibernate.mapping"
434 private String mappings;
438 public final void execute(String filename)
440 MojoFailureException,
441 MojoExecutionException
445 getLog().info("Execution of hibernate-maven-plugin was skipped!");
446 project.getProperties().setProperty(SKIPPED, "true");
450 ModificationTracker tracker;
453 tracker = new ModificationTracker(buildDirectory, filename, getLog());
455 catch (NoSuchAlgorithmException e)
457 throw new MojoFailureException("Digest-Algorithm MD5 is missing!", e);
460 final SimpleConnectionProvider connectionProvider =
461 new SimpleConnectionProvider(getLog());
465 /** Start extended logging */
466 MavenLogAppender.startPluginLog(this);
468 /** Load checksums for old mapping and configuration */
471 /** Create the ClassLoader */
472 MutableClassLoader classLoader = createClassLoader();
474 /** Create a BootstrapServiceRegistry with the created ClassLoader */
475 BootstrapServiceRegistry bootstrapServiceRegitry =
476 new BootstrapServiceRegistryBuilder()
477 .applyClassLoader(classLoader)
479 ClassLoaderService classLoaderService =
480 bootstrapServiceRegitry.getService(ClassLoaderService.class);
482 Properties properties = new Properties();
483 ConfigLoader configLoader = new ConfigLoader(bootstrapServiceRegitry);
485 /** Loading and merging configuration */
486 properties.putAll(loadProperties(configLoader));
487 LoadedConfig config = loadConfig(configLoader);
489 properties.putAll(config.getConfigurationValues());
490 ParsedPersistenceXmlDescriptor unit =
491 loadPersistenceUnit(classLoaderService, properties);
493 properties.putAll(unit.getProperties());
495 /** Overwriting/Completing configuration */
496 configure(properties, tracker);
498 /** Check configuration for modifications */
499 if(tracker.track(properties))
500 getLog().debug("Configuration has changed.");
502 getLog().debug("Configuration unchanged.");
504 /** Check, that the outputfile is writable */
505 final File output = getOutputFile(filename);
507 /** Configure Hibernate */
508 final StandardServiceRegistry serviceRegistry =
509 new StandardServiceRegistryBuilder(bootstrapServiceRegitry)
510 .applySettings(properties)
511 .addService(ConnectionProvider.class, connectionProvider)
513 final MetadataSources sources = new MetadataSources(serviceRegistry);
515 /** Add the remaining class-path-elements */
516 completeClassPath(classLoader);
518 /** Apply mappings from hibernate-configuration, if present */
521 for (MappingReference mapping : config.getMappingReferences())
522 mapping.apply(sources);
528 /** No persistent unit: default behaviour */
529 if (scanClasses == null)
531 Set<URL> urls = new HashSet<URL>();
533 addRoot(urls, outputDirectory);
535 addRoot(urls, testOutputDirectory);
536 addDependencies(urls);
537 classes = scanUrls(urls);
541 /** Follow configuration in persisten unit */
542 if (scanClasses == null)
543 scanClasses = !unit.isExcludeUnlistedClasses();
544 Set<URL> urls = new HashSet<URL>();
548 * Scan the root of the persiten unit and configured jars for
551 urls.add(unit.getPersistenceUnitRootUrl());
552 for (URL url : unit.getJarFileUrls())
556 addRoot(urls, testOutputDirectory);
557 classes = scanUrls(urls);
558 for (String className : unit.getManagedClassNames())
559 classes.add(className);
561 * Add mappings from the default mapping-file
562 * <code>META-INF/orm.xml</code>, if present
564 boolean error = false;
566 is = classLoader.getResourceAsStream("META-INF/orm.xml");
569 getLog().info("Adding default JPA-XML-mapping from META-INF/orm.xml");
572 tracker.track("META-INF/orm.xml", is);
573 sources.addResource("META-INF/orm.xml");
575 catch (IOException e)
577 getLog().error("cannot read META-INF/orm.xml: " + e);
583 getLog().debug("no META-INF/orm.xml found");
586 * Add mappings from files, that are explicitly configured in the
589 for (String mapping : unit.getMappingFileNames())
591 getLog().info("Adding explicitly configured mapping from " + mapping);
592 is = classLoader.getResourceAsStream(mapping);
597 tracker.track(mapping, is);
598 sources.addResource(mapping);
600 catch (IOException e)
602 getLog().info("cannot read mapping-file " + mapping + ": " + e);
608 getLog().error("cannot find mapping-file " + mapping);
613 throw new MojoFailureException(
614 "error, while reading mappings configured in persistence-unit \"" +
620 /** Add the configured/collected annotated classes */
621 for (String className : classes)
622 addAnnotated(className, sources, classLoaderService, tracker);
624 /** Add explicitly configured classes */
625 addMappings(sources, tracker);
627 /** Skip execution, if mapping and configuration is unchanged */
628 if (!tracker.modified())
631 "Mapping and configuration unchanged."
634 getLog().info("Schema generation is forced!");
637 getLog().info("Skipping schema generation!");
638 project.getProperties().setProperty(SKIPPED, "true");
644 /** Truncate output file */
647 new FileOutputStream(output).getChannel().truncate(0).close();
649 catch (IOException e)
652 "Error while truncating " + output.getAbsolutePath() + ": "
654 getLog().warn(error);
655 throw new MojoExecutionException(error);
658 /** Create a connection, if sufficient configuration infromation is available */
659 connectionProvider.open(classLoaderService, properties);
661 MetadataBuilder metadataBuilder = sources.getMetadataBuilder();
663 StrategySelector strategySelector =
664 serviceRegistry.getService(StrategySelector.class);
666 if (properties.containsKey(IMPLICIT_NAMING_STRATEGY))
668 metadataBuilder.applyImplicitNamingStrategy(
669 strategySelector.resolveStrategy(
670 ImplicitNamingStrategy.class,
671 properties.getProperty(IMPLICIT_NAMING_STRATEGY)
676 if (properties.containsKey(PHYSICAL_NAMING_STRATEGY))
678 metadataBuilder.applyPhysicalNamingStrategy(
679 strategySelector.resolveStrategy(
680 PhysicalNamingStrategy.class,
681 properties.getProperty(PHYSICAL_NAMING_STRATEGY)
686 /** Prepare the generation of the SQL */
687 Map settings = new HashMap();
690 .getService(ConfigurationService.class)
693 ExceptionHandlerCollectingImpl handler =
694 new ExceptionHandlerCollectingImpl();
695 ExecutionOptions options =
696 SchemaManagementToolCoordinator
697 .buildExecutionOptions(settings, handler);
698 final EnumSet<TargetType> targetTypes = EnumSet.of(TargetType.SCRIPT);
700 targetTypes.add(TargetType.DATABASE);
701 TargetDescriptor target = new TargetDescriptor()
704 public EnumSet<TargetType> getTargetTypes()
710 public ScriptTargetOutput getScriptTargetOutput()
715 .getService(ConfigurationService.class)
717 .get(AvailableSettings.HBM2DDL_CHARSET_NAME);
718 return new ScriptTargetOutputToFile(output, charset);
723 * Change class-loader of current thread.
724 * This is necessary, because still not all parts of Hibernate 5 use
725 * the newly introduced ClassLoaderService and will fail otherwise!
727 Thread thread = Thread.currentThread();
728 ClassLoader contextClassLoader = thread.getContextClassLoader();
731 thread.setContextClassLoader(classLoader);
732 build((MetadataImplementor)metadataBuilder.build(), options, target);
736 thread.setContextClassLoader(contextClassLoader);
737 for (Exception e : handler.getExceptions())
738 getLog().error(e.getMessage());
741 catch (MojoExecutionException e)
746 catch (MojoFailureException e)
751 catch (RuntimeException e)
758 /** Remember mappings and configuration */
761 /** Close the connection - if one was opened */
762 connectionProvider.close();
764 /** Stop Log-Capturing */
765 MavenLogAppender.endPluginLog(this);
771 MetadataImplementor metadata,
772 ExecutionOptions options,
773 TargetDescriptor target
776 MojoFailureException,
777 MojoExecutionException;
780 private MutableClassLoader createClassLoader() throws MojoExecutionException
784 getLog().debug("Creating ClassLoader for project-dependencies...");
785 LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
788 file = new File(testOutputDirectory);
791 getLog().info("Creating test-output-directory: " + testOutputDirectory);
794 urls.add(file.toURI().toURL());
796 file = new File(outputDirectory);
799 getLog().info("Creating output-directory: " + outputDirectory);
802 urls.add(file.toURI().toURL());
804 return new MutableClassLoader(urls, getLog());
808 getLog().error("Error while creating ClassLoader!", e);
809 throw new MojoExecutionException(e.getMessage());
813 private void completeClassPath(MutableClassLoader classLoader)
815 MojoExecutionException
819 getLog().debug("Completing class-paths of the ClassLoader for project-dependencies...");
820 List<String> classpathFiles = project.getCompileClasspathElements();
822 classpathFiles.addAll(project.getTestClasspathElements());
823 LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
824 for (String pathElement : classpathFiles)
826 getLog().debug("Dependency: " + pathElement);
827 urls.add(new File(pathElement).toURI().toURL());
829 classLoader.add(urls);
833 getLog().error("Error while creating ClassLoader!", e);
834 throw new MojoExecutionException(e.getMessage());
838 private Map loadProperties(ConfigLoader configLoader)
840 MojoExecutionException
842 /** Try to read configuration from properties-file */
843 if (hibernateProperties == null)
847 return configLoader.loadProperties("hibernate.properties");
849 catch (ConfigurationException e)
851 getLog().debug(e.getMessage());
852 return Collections.EMPTY_MAP;
859 File file = new File(hibernateProperties);
862 getLog().info("Reading settings from file " + hibernateProperties + "...");
863 return configLoader.loadProperties(file);
866 return configLoader.loadProperties(hibernateProperties);
868 catch (ConfigurationException e)
870 getLog().error("Error while reading properties!", e);
871 throw new MojoExecutionException(e.getMessage());
876 private LoadedConfig loadConfig(ConfigLoader configLoader)
877 throws MojoExecutionException
879 /** Try to read configuration from configuration-file */
880 if (hibernateConfig == null)
884 return configLoader.loadConfigXmlResource("hibernate.cfg.xml");
886 catch (ConfigurationException e)
888 getLog().debug(e.getMessage());
896 File file = new File(hibernateConfig);
899 getLog().info("Reading configuration from file " + hibernateConfig + "...");
900 return configLoader.loadConfigXmlFile(file);
904 return configLoader.loadConfigXmlResource(hibernateConfig);
907 catch (ConfigurationException e)
909 getLog().error("Error while reading configuration!", e);
910 throw new MojoExecutionException(e.getMessage());
915 private void configure(Properties properties, ModificationTracker tracker)
916 throws MojoFailureException
919 * Special treatment for the configuration-value "export": if it is
920 * switched to "true", the genearation fo the schema should be forced!
922 if (tracker.check(EXPORT, export.toString()) && export)
926 * Configure the generation of the SQL.
927 * Overwrite values from properties-file if the configuration parameter is
928 * known to Hibernate.
930 configure(properties, dialect, DIALECT);
931 configure(properties, delimiter, HBM2DDL_DELIMITER);
932 configure(properties, format, FORMAT_SQL);
933 configure(properties, createNamespaces, HBM2DLL_CREATE_NAMESPACES);
934 configure(properties, implicitNamingStrategy, IMPLICIT_NAMING_STRATEGY);
935 configure(properties, physicalNamingStrategy, PHYSICAL_NAMING_STRATEGY);
936 tracker.track(OUTPUTDIRECTORY, outputDirectory); // << not reflected in hibernate configuration!
937 tracker.track(SCAN_DEPENDENCIES, scanDependencies); // << not reflected in hibernate configuration!
938 tracker.track(SCAN_TESTCLASSES, scanTestClasses.toString()); // << not reflected in hibernate configuration!
939 tracker.track(TEST_OUTPUTDIRECTORY, testOutputDirectory); // << not reflected in hibernate configuration!
942 * Special treatment for the configuration-value "show": a change of its
943 * configured value should not lead to a regeneration of the database
947 show = Boolean.valueOf(properties.getProperty(SHOW_SQL));
949 properties.setProperty(SHOW_SQL, show.toString());
952 * Configure the connection parameters.
953 * Overwrite values from properties-file.
955 configure(properties, driver, DRIVER, JPA_JDBC_DRIVER);
956 configure(properties, url, URL, JPA_JDBC_URL);
957 configure(properties, username, USER, JPA_JDBC_USER);
958 configure(properties, password, PASS, JPA_JDBC_PASSWORD);
960 if (properties.isEmpty())
962 getLog().error("No properties set!");
963 throw new MojoFailureException("Hibernate configuration is missing!");
966 getLog().info("Gathered hibernate-configuration (turn on debugging for details):");
967 for (Entry<Object,Object> entry : properties.entrySet())
968 getLog().info(" " + entry.getKey() + " = " + entry.getValue());
971 private void configure(
972 Properties properties,
975 String alternativeKey
978 configure(properties, value, key);
980 if (properties.containsKey(alternativeKey))
982 if (properties.containsKey(key))
985 "Ignoring property " + alternativeKey + "=\"" +
986 properties.getProperty(alternativeKey) +
987 "\" in favour for property " + key + "=\"" +
988 properties.getProperty(key) + "\""
990 properties.remove(alternativeKey);
994 value = properties.getProperty(alternativeKey);
995 properties.remove(alternativeKey);
997 "Using value \"" + value + "\" from property " + alternativeKey +
998 " for property " + key
1000 properties.setProperty(key, value);
1005 private void configure(Properties properties, String value, String key)
1009 if (properties.containsKey(key))
1011 "Overwriting property " + key + "=\"" +
1012 properties.getProperty(key) +
1013 "\" with value \"" + value + "\""
1016 getLog().debug("Using value \"" + value + "\" for property " + key);
1017 properties.setProperty(key, value);
1021 private void configure(Properties properties, Boolean value, String key)
1023 configure(properties, value == null ? null : value.toString(), key);
1026 private File getOutputFile(String filename)
1028 MojoExecutionException
1030 File output = new File(filename);
1032 if (!output.isAbsolute())
1034 // Interpret relative file path relative to build directory
1035 output = new File(buildDirectory, filename);
1037 getLog().debug("Output file: " + output.getPath());
1039 // Ensure that directory path for specified file exists
1040 File outFileParentDir = output.getParentFile();
1041 if (null != outFileParentDir && !outFileParentDir.exists())
1046 "Creating directory path for output file:" +
1047 outFileParentDir.getPath()
1049 outFileParentDir.mkdirs();
1054 "Error creating directory path for output file: " + e.getMessage();
1055 getLog().error(error);
1056 throw new MojoExecutionException(error);
1062 output.createNewFile();
1064 catch (IOException e)
1066 String error = "Error creating output file: " + e.getMessage();
1067 getLog().error(error);
1068 throw new MojoExecutionException(error);
1071 if (!output.canWrite())
1074 "Output file " + output.getAbsolutePath() + " is not writable!";
1075 getLog().error(error);
1076 throw new MojoExecutionException(error);
1082 private void addMappings(MetadataSources sources, ModificationTracker tracker)
1083 throws MojoFailureException
1085 getLog().debug("Adding explicitly configured mappings...");
1086 if (mappings != null)
1090 for (String filename : mappings.split("[\\s,]+"))
1092 // First try the filename as absolute/relative path
1093 File file = new File(filename);
1096 // If the file was not found, search for it in the resource-directories
1097 for (Resource resource : project.getResources())
1099 file = new File(resource.getDirectory() + File.separator + filename);
1106 if (file.isDirectory())
1107 // TODO: add support to read all mappings under a directory
1108 throw new MojoFailureException(file.getAbsolutePath() + " is a directory");
1109 if (tracker.track(filename, new FileInputStream(file)))
1110 getLog().debug("Found new or modified mapping-file: " + filename);
1112 getLog().debug("Mapping-file unchanged: " + filename);
1114 sources.addFile(file);
1117 throw new MojoFailureException("File " + filename + " could not be found in any of the configured resource-directories!");
1120 catch (IOException e)
1122 throw new MojoFailureException("Cannot calculate MD5 sums!", e);
1127 private void addRoot(Set<URL> urls, String path) throws MojoFailureException
1131 File dir = new File(outputDirectory);
1134 getLog().info("Adding " + dir.getAbsolutePath() + " to the list of roots to scan...");
1135 urls.add(dir.toURI().toURL());
1138 catch (MalformedURLException e)
1140 getLog().error("error while adding the project-root to the list of roots to scan!", e);
1141 throw new MojoFailureException(e.getMessage());
1145 private void addDependencies(Set<URL> urls) throws MojoFailureException
1149 if (scanDependencies != null)
1151 Matcher matcher = SPLIT.matcher(scanDependencies);
1152 while (matcher.find())
1154 getLog().info("Adding dependencies from scope " + matcher.group() + " to the list of roots to scan");
1155 for (Artifact artifact : project.getDependencyArtifacts())
1157 if (!artifact.getScope().equalsIgnoreCase(matcher.group()))
1159 if (artifact.getFile() == null)
1161 getLog().warn("Cannot add dependency " + artifact.getId() + ": no JAR-file available!");
1164 getLog().info("Adding dependencies from scope " + artifact.getId() + " to the list of roots to scan");
1165 urls.add(artifact.getFile().toURI().toURL());
1170 catch (MalformedURLException e)
1172 getLog().error("Error while adding dependencies to the list of roots to scan!", e);
1173 throw new MojoFailureException(e.getMessage());
1177 private Set<String> scanUrls(Set<URL> scanRoots)
1179 MojoFailureException
1183 AnnotationDB db = new AnnotationDB();
1184 for (URL root : scanRoots)
1185 db.scanArchives(root);
1187 Set<String> classes = new HashSet<String>();
1188 if (db.getAnnotationIndex().containsKey(Entity.class.getName()))
1189 classes.addAll(db.getAnnotationIndex().get(Entity.class.getName()));
1190 if (db.getAnnotationIndex().containsKey(MappedSuperclass.class.getName()))
1191 classes.addAll(db.getAnnotationIndex().get(MappedSuperclass.class.getName()));
1192 if (db.getAnnotationIndex().containsKey(Embeddable.class.getName()))
1193 classes.addAll(db.getAnnotationIndex().get(Embeddable.class.getName()));
1199 getLog().error("Error while scanning!", e);
1200 throw new MojoFailureException(e.getMessage());
1204 private void addAnnotated(
1206 MetadataSources sources,
1207 ClassLoaderService classLoaderService,
1208 ModificationTracker tracker
1211 MojoFailureException,
1212 MojoExecutionException
1216 getLog().info("Adding annotated resource: " + name);
1217 String packageName = null;
1219 boolean error = false;
1222 Class<?> annotatedClass = classLoaderService.classForName(name);
1223 String resourceName = annotatedClass.getName();
1225 resourceName.substring(
1226 resourceName.lastIndexOf(".") + 1,
1227 resourceName.length()
1229 InputStream is = annotatedClass.getResourceAsStream(resourceName);
1232 if (tracker.track(name, is))
1233 getLog().debug("New or modified class: " + name);
1235 getLog().debug("Unchanged class: " + name);
1236 sources.addAnnotatedClass(annotatedClass);
1237 packageName = annotatedClass.getPackage().getName();
1241 getLog().error("cannot find ressource " + resourceName + " for class " + name);
1245 catch(ClassLoadingException e)
1251 throw new MojoExecutionException("error while inspecting annotated class " + name);
1254 while (packageName != null)
1256 if (packages.contains(packageName))
1258 String resource = packageName.replace('.', '/') + "/package-info.class";
1259 InputStream is = classLoaderService.locateResourceStream(resource);
1262 // No compiled package-info available: no package-level annotations!
1263 getLog().debug("Package " + packageName + " is not annotated.");
1267 if (tracker.track(packageName, is))
1268 getLog().debug("New or modified package: " + packageName);
1270 getLog().debug("Unchanged package: " + packageName);
1271 getLog().info("Adding annotations from package " + packageName);
1272 sources.addPackage(packageName);
1274 packages.add(packageName);
1275 int i = packageName.lastIndexOf('.');
1279 packageName = packageName.substring(0,i);
1284 getLog().error("Error while adding the annotated class " + name, e);
1285 throw new MojoFailureException(e.getMessage());
1289 private ParsedPersistenceXmlDescriptor loadPersistenceUnit(
1290 ClassLoaderService classLoaderService,
1291 Properties properties
1294 MojoFailureException
1296 PersistenceXmlParser parser =
1297 new PersistenceXmlParser(
1299 PersistenceUnitTransactionType.RESOURCE_LOCAL
1302 Map<String, ParsedPersistenceXmlDescriptor> units =
1303 parser.doResolve(properties);
1305 if (persistenceUnit == null)
1307 Iterator<String> names = units.keySet().iterator();
1308 if (!names.hasNext())
1310 getLog().info("Found no META-INF/persistence.xml.");
1314 String name = names.next();
1315 if (!names.hasNext())
1317 getLog().info("Using persistence-unit " + name);
1318 return units.get(name);
1321 StringBuilder builder = new StringBuilder();
1322 builder.append("No name provided and multiple persistence units found: ");
1323 builder.append(name);
1324 while(names.hasNext())
1326 builder.append(", ");
1327 builder.append(names.next());
1329 builder.append('.');
1330 throw new MojoFailureException(builder.toString());
1333 if (units.containsKey(persistenceUnit))
1335 getLog().info("Using configured persistence-unit " + persistenceUnit);
1336 return units.get(persistenceUnit);
1339 throw new MojoFailureException("Could not find persistence-unit " + persistenceUnit);