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.time.ZonedDateTime;
14 import java.util.Collections;
15 import java.util.EnumSet;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.Iterator;
19 import java.util.LinkedHashSet;
20 import java.util.List;
22 import java.util.Map.Entry;
23 import java.util.Properties;
25 import java.util.regex.Matcher;
26 import java.util.regex.Pattern;
27 import javax.persistence.Embeddable;
28 import javax.persistence.Entity;
29 import javax.persistence.MappedSuperclass;
30 import javax.persistence.spi.PersistenceUnitTransactionType;
31 import org.apache.maven.artifact.Artifact;
32 import org.apache.maven.model.Resource;
33 import org.apache.maven.plugin.AbstractMojo;
34 import org.apache.maven.plugin.MojoExecutionException;
35 import org.apache.maven.plugin.MojoFailureException;
36 import org.apache.maven.project.MavenProject;
37 import org.hibernate.boot.MetadataBuilder;
38 import org.hibernate.boot.MetadataSources;
39 import org.hibernate.boot.cfgxml.internal.ConfigLoader;
40 import org.hibernate.boot.cfgxml.spi.LoadedConfig;
41 import org.hibernate.boot.cfgxml.spi.MappingReference;
42 import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
43 import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
44 import org.hibernate.boot.registry.BootstrapServiceRegistry;
45 import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
46 import org.hibernate.boot.registry.StandardServiceRegistry;
47 import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
48 import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
49 import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
50 import org.hibernate.boot.registry.selector.spi.StrategySelector;
51 import org.hibernate.boot.spi.MetadataImplementor;
52 import org.hibernate.cfg.AvailableSettings;
53 import static org.hibernate.cfg.AvailableSettings.DIALECT;
54 import static org.hibernate.cfg.AvailableSettings.DRIVER;
55 import static org.hibernate.cfg.AvailableSettings.FORMAT_SQL;
56 import static org.hibernate.cfg.AvailableSettings.HBM2DDL_DELIMITER;
57 import static org.hibernate.cfg.AvailableSettings.HBM2DLL_CREATE_NAMESPACES;
58 import static org.hibernate.cfg.AvailableSettings.IMPLICIT_NAMING_STRATEGY;
59 import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_DRIVER;
60 import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_PASSWORD;
61 import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_URL;
62 import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_USER;
63 import static org.hibernate.cfg.AvailableSettings.PASS;
64 import static org.hibernate.cfg.AvailableSettings.PHYSICAL_NAMING_STRATEGY;
65 import static org.hibernate.cfg.AvailableSettings.SHOW_SQL;
66 import static org.hibernate.cfg.AvailableSettings.USER;
67 import static org.hibernate.cfg.AvailableSettings.URL;
68 import org.hibernate.engine.config.spi.ConfigurationService;
69 import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
70 import org.hibernate.internal.util.config.ConfigurationException;
71 import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor;
72 import org.hibernate.jpa.boot.internal.PersistenceXmlParser;
73 import org.hibernate.tool.schema.TargetType;
74 import org.hibernate.tool.schema.internal.ExceptionHandlerCollectingImpl;
75 import org.hibernate.tool.schema.internal.exec.ScriptTargetOutputToFile;
76 import org.hibernate.tool.schema.spi.ExecutionOptions;
77 import org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator;
78 import org.hibernate.tool.schema.spi.ScriptTargetOutput;
79 import org.hibernate.tool.schema.spi.TargetDescriptor;
80 import org.scannotation.AnnotationDB;
84 * Baseclass with common attributes and methods.
86 * @phase process-classes
88 * @requiresDependencyResolution runtime
90 public abstract class AbstractSchemaMojo extends AbstractMojo
92 public final static String EXECUTE = "hibernate.schema.execute";
93 public final static String OUTPUTDIRECTORY = "project.build.outputDirectory";
94 public final static String SCAN_CLASSES = "hibernate.schema.scan.classes";
95 public final static String SCAN_DEPENDENCIES = "hibernate.schema.scan.dependencies";
96 public final static String SCAN_TESTCLASSES = "hibernate.schema.scan.test_classes";
97 public final static String TEST_OUTPUTDIRECTORY = "project.build.testOutputDirectory";
98 public final static String SKIPPED = "hibernate.schema.skipped";
99 public final static String SCRIPT = "hibernate.schema.script";
101 private final static Pattern SPLIT = Pattern.compile("[^,\\s]+");
103 private final Set<String> packages = new HashSet<String>();
109 * Only needed internally.
111 * @parameter property="project"
115 private MavenProject project;
120 * Only needed internally.
122 * @parameter property="project.build.directory"
126 private String buildDirectory;
129 /** Parameters to configure the genaration of the SQL *********************/
132 * Excecute the generated SQL.
133 * If set to <code>false</code>, only the SQL-script is created and the
134 * database is not touched.
136 * <strong>Important:</strong>
137 * This configuration value can only be configured through the
138 * <code>pom.xml</code>, or by the definition of a system-property, because
139 * it is not known by Hibernate nor JPA and, hence, not picked up from
140 * their configuration!
142 * @parameter property="hibernate.schema.execute" default-value="true"
145 private Boolean execute;
150 * If set to <code>true</code>, the execution is skipped.
152 * A skipped execution is signaled via the maven-property
153 * <code>${hibernate.schema.skipped}</code>.
155 * The execution is skipped automatically, if no modified or newly added
156 * annotated classes are found and the dialect was not changed.
158 * <strong>Important:</strong>
159 * This configuration value can only be configured through the
160 * <code>pom.xml</code>, or by the definition of a system-property, because
161 * it is not known by Hibernate nor JPA and, hence, not picked up from
162 * their configuration!
164 * @parameter property="hibernate.schema.skip" default-value="${maven.test.skip}"
167 private boolean skip;
170 * Force generation/execution
172 * Force the generation and (if configured) the execution of the SQL, even if
173 * no modified or newly added annotated classes where found and the
174 * configuration was not changed.
176 * <code>skip</code> takes precedence over <code>force</code>.
178 * <strong>Important:</strong>
179 * This configuration value can only be configured through the
180 * <code>pom.xml</code>, or by the definition of a system-property, because
181 * it is not known by Hibernate nor JPA and, hence, not picked up from
182 * their configuration!
184 * @parameter property="hibernate.schema.force" default-value="false"
187 private boolean force;
192 * @parameter property="hibernate.dialect"
195 private String dialect;
198 * Delimiter in output-file.
200 * <strong>Important:</strong>
201 * This configuration value can only be configured through the
202 * <code>pom.xml</code>, or by the definition of a system-property, because
203 * it is not known by Hibernate nor JPA and, hence, not picked up from
204 * their configuration!
206 * @parameter property="hibernate.hbm2ddl.delimiter" default-value=";"
209 private String delimiter;
212 * Show the generated SQL in the command-line output.
214 * @parameter property="hibernate.show_sql"
217 private Boolean show;
220 * Format output-file.
222 * @parameter property="hibernate.format_sql"
225 private Boolean format;
228 * Specifies whether to automatically create also the database schema/catalog.
230 * @parameter property="hibernate.hbm2dll.create_namespaces" default-value="false"
233 private Boolean createNamespaces;
236 * Implicit naming strategy
238 * @parameter property="hibernate.implicit_naming_strategy"
241 private String implicitNamingStrategy;
244 * Physical naming strategy
246 * @parameter property="hibernate.physical_naming_strategy"
249 private String physicalNamingStrategy;
252 * Wether the project should be scanned for annotated-classes, or not
254 * This parameter is intended to allow overwriting of the parameter
255 * <code>exclude-unlisted-classes</code> of a <code>persistence-unit</code>.
256 * If not specified, it defaults to <code>true</code>
258 * @parameter property="hibernate.schema.scan.classes"
261 private Boolean scanClasses;
264 * Classes-Directory to scan.
266 * This parameter defaults to the maven build-output-directory for classes.
267 * Additionally, all dependencies are scanned for annotated classes.
269 * <strong>Important:</strong>
270 * This configuration value can only be configured through the
271 * <code>pom.xml</code>, or by the definition of a system-property, because
272 * it is not known by Hibernate nor JPA and, hence, not picked up from
273 * their configuration!
275 * @parameter property="project.build.outputDirectory"
278 private String outputDirectory;
281 * Dependency-Scopes, that should be scanned for annotated classes.
283 * By default, only dependencies in the scope <code>compile</code> are
284 * scanned for annotated classes. Multiple scopes can be seperated by
285 * white space or commas.
287 * If you do not want any dependencies to be scanned for annotated
288 * classes, set this parameter to <code>none</code>.
290 * The plugin does not scan for annotated classes in transitive
291 * dependencies. If some of your annotated classes are hidden in a
292 * transitive dependency, you can simply add that dependency explicitly.
294 * @parameter property="hibernate.schema.scan.dependencies" default-value="compile"
297 private String scanDependencies;
300 * Whether to scan the test-branch of the project for annotated classes, or
303 * If this parameter is set to <code>true</code> the test-classes of the
304 * artifact will be scanned for hibernate-annotated classes additionally.
306 * <strong>Important:</strong>
307 * This configuration value can only be configured through the
308 * <code>pom.xml</code>, or by the definition of a system-property, because
309 * it is not known by Hibernate nor JPA and, hence, not picked up from
310 * their configuration!
312 * @parameter property="hibernate.schema.scan.test_classes" default-value="false"
315 private Boolean scanTestClasses;
318 * Test-Classes-Directory to scan.
320 * This parameter defaults to the maven build-output-directory for
323 * This parameter is only used, when <code>scanTestClasses</code> is set
324 * to <code>true</code>!
326 * <strong>Important:</strong>
327 * This configuration value can only be configured through the
328 * <code>pom.xml</code>, or by the definition of a system-property, because
329 * it is not known by Hibernate nor JPA and, hence, not picked up from
330 * their configuration!
332 * @parameter property="project.build.testOutputDirectory"
335 private String testOutputDirectory;
338 /** Conection parameters *************************************************/
343 * @parameter property="hibernate.connection.driver_class"
346 private String driver;
351 * @parameter property="hibernate.connection.url"
359 * @parameter property="hibernate.connection.username"
362 private String username;
367 * @parameter property="hibernate.connection.password"
370 private String password;
373 /** Parameters to locate configuration sources ****************************/
376 * Path to a file or name of a ressource with hibernate properties.
377 * If this parameter is specified, the plugin will try to load configuration
378 * values from a file with the given path or a ressource on the classpath with
379 * the given name. If both fails, the execution of the plugin will fail.
381 * If this parameter is not set the plugin will load configuration values
382 * from a ressource named <code>hibernate.properties</code> on the classpath,
383 * if it is present, but will not fail if there is no such ressource.
385 * During ressource-lookup, the test-classpath takes precedence.
390 private String hibernateProperties;
393 * Path to Hibernate configuration file (.cfg.xml).
394 * If this parameter is specified, the plugin will try to load configuration
395 * values from a file with the given path or a ressource on the classpath with
396 * the given name. If both fails, the execution of the plugin will fail.
398 * If this parameter is not set the plugin will load configuration values
399 * from a ressource named <code>hibernate.cfg.xml</code> on the classpath,
400 * if it is present, but will not fail if there is no such ressource.
402 * During ressource-lookup, the test-classpath takes precedence.
404 * Settings in this file will overwrite settings in the properties file.
409 private String hibernateConfig;
412 * Name of the persistence-unit.
413 * If this parameter is specified, the plugin will try to load configuration
414 * values from a persistence-unit with the specified name. If no such
415 * persistence-unit can be found, the plugin will throw an exception.
417 * If this parameter is not set and there is only one persistence-unit
418 * available, that unit will be used automatically. But if this parameter is
419 * not set and there are multiple persistence-units available on,
420 * the class-path, the execution of the plugin will fail.
422 * Settings in this file will overwrite settings in the properties or the
423 * configuration file.
428 private String persistenceUnit;
431 * List of Hibernate-Mapping-Files (XML).
432 * Multiple files can be separated with white-spaces and/or commas.
434 * @parameter property="hibernate.mapping"
437 private String mappings;
441 public final void execute(String filename)
443 MojoFailureException,
444 MojoExecutionException
448 getLog().info("Execution of hibernate-maven-plugin was skipped!");
449 project.getProperties().setProperty(SKIPPED, "true");
453 ModificationTracker tracker;
456 tracker = new ModificationTracker(buildDirectory, filename, getLog());
458 catch (NoSuchAlgorithmException e)
460 throw new MojoFailureException("Digest-Algorithm MD5 is missing!", e);
463 final SimpleConnectionProvider connectionProvider =
464 new SimpleConnectionProvider(getLog());
468 /** Start extended logging */
469 MavenLogAppender.startPluginLog(this);
471 /** Load checksums for old mapping and configuration */
474 /** Create the ClassLoader */
475 MutableClassLoader classLoader = createClassLoader();
477 /** Create a BootstrapServiceRegistry with the created ClassLoader */
478 BootstrapServiceRegistry bootstrapServiceRegitry =
479 new BootstrapServiceRegistryBuilder()
480 .applyClassLoader(classLoader)
482 ClassLoaderService classLoaderService =
483 bootstrapServiceRegitry.getService(ClassLoaderService.class);
485 Properties properties = new Properties();
486 ConfigLoader configLoader = new ConfigLoader(bootstrapServiceRegitry);
488 /** Loading configuration */
489 properties.putAll(loadProperties(configLoader));
490 LoadedConfig config = loadConfig(configLoader);
492 properties.putAll(config.getConfigurationValues());
494 /** Add the remaining class-path-elements */
495 addDirectDependenciesClassPath(classLoader);
497 /** Loading and merging configuration from persistence-unit(s) */
498 ParsedPersistenceXmlDescriptor unit =
499 loadPersistenceUnit(classLoaderService, properties);
501 properties.putAll(unit.getProperties());
503 /** Overwriting/Completing configuration */
504 configure(properties, tracker);
506 /** Check configuration for modifications */
507 if(tracker.track(properties))
508 getLog().debug("Configuration has changed.");
510 getLog().debug("Configuration unchanged.");
512 /** Check, that the outputfile is writable */
513 final File output = getOutputFile(filename);
514 /** Check, if the outputfile is missing or was changed */
515 checkOutputFile(output, tracker);
517 /** Configure Hibernate */
518 final StandardServiceRegistry serviceRegistry =
519 new StandardServiceRegistryBuilder(bootstrapServiceRegitry)
520 .applySettings(properties)
521 .addService(ConnectionProvider.class, connectionProvider)
523 final MetadataSources sources = new MetadataSources(serviceRegistry);
525 /** Add the remaining class-path-elements */
526 addAllDependenciesToClassPath(classLoader);
528 /** Apply mappings from hibernate-configuration, if present */
531 for (MappingReference mapping : config.getMappingReferences())
532 mapping.apply(sources);
538 /** No persistent unit: default behaviour */
539 if (scanClasses == null)
541 Set<URL> urls = new HashSet<URL>();
542 getLog().debug("Compiling the dependencies, that are scanned for annotated classes");
544 addRoot(urls, outputDirectory);
546 addRoot(urls, testOutputDirectory);
547 addDependencies(urls);
548 classes = scanUrls(urls);
552 /** Follow configuration in persisten unit */
553 if (scanClasses == null)
554 scanClasses = !unit.isExcludeUnlistedClasses();
555 getLog().debug("Compiling the dependencies, that are scanned for annotated classes");
557 Set<URL> urls = new HashSet<URL>();
560 getLog().debug("Only dependencies relative to persistent-unit " + unit.getName() + " are scanned!");
562 * Scan the root of the persiten unit and configured jars for
565 getLog().debug(" - adding " + unit.getPersistenceUnitRootUrl());
566 urls.add(unit.getPersistenceUnitRootUrl());
567 for (URL url : unit.getJarFileUrls())
569 getLog().debug(" - adding " + url);
573 addRoot(urls, testOutputDirectory);
576 getLog().debug("Scanning of unlisted classes is prohibited in persistent-unit " + unit.getName());
577 classes = scanUrls(urls);
578 for (String className : unit.getManagedClassNames())
579 classes.add(className);
581 * Add mappings from the default mapping-file
582 * <code>META-INF/orm.xml</code>, if present
584 boolean error = false;
586 is = classLoader.getResourceAsStream("META-INF/orm.xml");
589 getLog().info("Adding default JPA-XML-mapping from META-INF/orm.xml");
592 tracker.track("META-INF/orm.xml", is);
593 sources.addResource("META-INF/orm.xml");
595 catch (IOException e)
597 getLog().error("cannot read META-INF/orm.xml: " + e);
603 getLog().debug("no META-INF/orm.xml found");
606 * Add mappings from files, that are explicitly configured in the
609 getLog().info("Adding mappings from persistence-unit " + unit.getName());
610 for (String mapping : unit.getMappingFileNames())
612 getLog().info(" - adding " + mapping);
613 is = classLoader.getResourceAsStream(mapping);
618 tracker.track(mapping, is);
619 sources.addResource(mapping);
621 catch (IOException e)
623 getLog().info("cannot read mapping-file " + mapping + ": " + e);
629 getLog().error("cannot find mapping-file " + mapping);
634 throw new MojoFailureException(
635 "error, while reading mappings configured in persistence-unit \"" +
641 /** Add the configured/collected annotated classes */
642 for (String className : classes)
643 addAnnotated(className, sources, classLoaderService, tracker);
645 /** Add explicitly configured classes */
646 addMappings(sources, tracker);
648 /** Skip execution, if mapping and configuration is unchanged */
649 if (!tracker.modified())
651 getLog().info("Mapping and configuration unchanged.");
653 getLog().info("Generation/execution is forced!");
656 getLog().info("Skipping schema generation!");
657 project.getProperties().setProperty(SKIPPED, "true");
663 /** Truncate output file */
666 new FileOutputStream(output).getChannel().truncate(0).close();
668 catch (IOException e)
671 "Error while truncating " + output.getAbsolutePath() + ": "
673 getLog().warn(error);
674 throw new MojoExecutionException(error);
677 /** Create a connection, if sufficient configuration infromation is available */
678 connectionProvider.open(classLoaderService, properties);
680 MetadataBuilder metadataBuilder = sources.getMetadataBuilder();
682 StrategySelector strategySelector =
683 serviceRegistry.getService(StrategySelector.class);
685 if (properties.containsKey(IMPLICIT_NAMING_STRATEGY))
687 metadataBuilder.applyImplicitNamingStrategy(
688 strategySelector.resolveStrategy(
689 ImplicitNamingStrategy.class,
690 properties.getProperty(IMPLICIT_NAMING_STRATEGY)
695 if (properties.containsKey(PHYSICAL_NAMING_STRATEGY))
697 metadataBuilder.applyPhysicalNamingStrategy(
698 strategySelector.resolveStrategy(
699 PhysicalNamingStrategy.class,
700 properties.getProperty(PHYSICAL_NAMING_STRATEGY)
705 /** Prepare the generation of the SQL */
706 Map settings = new HashMap();
709 .getService(ConfigurationService.class)
712 ExceptionHandlerCollectingImpl handler =
713 new ExceptionHandlerCollectingImpl();
714 ExecutionOptions options =
715 SchemaManagementToolCoordinator
716 .buildExecutionOptions(settings, handler);
717 final EnumSet<TargetType> targetTypes = EnumSet.of(TargetType.SCRIPT);
719 targetTypes.add(TargetType.DATABASE);
720 TargetDescriptor target = new TargetDescriptor()
723 public EnumSet<TargetType> getTargetTypes()
729 public ScriptTargetOutput getScriptTargetOutput()
734 .getService(ConfigurationService.class)
736 .get(AvailableSettings.HBM2DDL_CHARSET_NAME);
737 return new ScriptTargetOutputToFile(output, charset);
742 * Change class-loader of current thread.
743 * This is necessary, because still not all parts of Hibernate 5 use
744 * the newly introduced ClassLoaderService and will fail otherwise!
746 Thread thread = Thread.currentThread();
747 ClassLoader contextClassLoader = thread.getContextClassLoader();
750 thread.setContextClassLoader(classLoader);
751 build((MetadataImplementor)metadataBuilder.build(), options, target);
752 if (handler.getExceptions().size() > 0)
754 StringBuilder builder = new StringBuilder();
755 builder.append("Hibernate failed:");
756 for (Exception e : handler.getExceptions())
758 builder.append("\n * ");
759 builder.append(e.getMessage());
760 AbstractSchemaMojo.printStrackTrace(builder, e);
761 builder.append("\n");
763 String error = builder.toString();
764 getLog().error(error);
765 throw new MojoFailureException(error);
770 thread.setContextClassLoader(contextClassLoader);
771 /** Track, the content of the generated script */
772 checkOutputFile(output, tracker);
775 catch (MojoExecutionException e)
780 catch (MojoFailureException e)
785 catch (RuntimeException e)
792 /** Remember mappings and configuration */
795 /** Close the connection - if one was opened */
796 connectionProvider.close();
798 /** Stop Log-Capturing */
799 MavenLogAppender.endPluginLog(this);
805 MetadataImplementor metadata,
806 ExecutionOptions options,
807 TargetDescriptor target
810 MojoFailureException,
811 MojoExecutionException;
814 private MutableClassLoader createClassLoader() throws MojoExecutionException
818 getLog().debug("Creating ClassLoader for project-dependencies...");
819 LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
822 file = new File(testOutputDirectory);
825 getLog().info("Creating test-output-directory: " + testOutputDirectory);
828 urls.add(file.toURI().toURL());
830 file = new File(outputDirectory);
833 getLog().info("Creating output-directory: " + outputDirectory);
836 urls.add(file.toURI().toURL());
838 return new MutableClassLoader(urls, getLog());
842 getLog().error("Error while creating ClassLoader!", e);
843 throw new MojoExecutionException(e.getMessage());
847 private void addDirectDependenciesClassPath(MutableClassLoader classLoader)
849 MojoExecutionException
853 getLog().debug("Adding all direct project-dependencies to the ClassLoader...");
854 LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
855 addDependencies(urls);
857 addRoot(urls, testOutputDirectory);
858 classLoader.add(urls);
862 getLog().error("Error while creating ClassLoader!", e);
863 throw new MojoExecutionException(e.getMessage());
867 private void addAllDependenciesToClassPath(MutableClassLoader classLoader)
869 MojoExecutionException
873 getLog().debug("Adding all project-dependencies to the ClassLoader...");
874 List<String> classpathFiles = project.getCompileClasspathElements();
875 classpathFiles.addAll(project.getTestClasspathElements());
876 LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
877 for (String pathElement : classpathFiles)
879 getLog().debug(" - adding " + pathElement);
880 urls.add(new File(pathElement).toURI().toURL());
882 classLoader.add(urls);
886 getLog().error("Error while creating ClassLoader!", e);
887 throw new MojoExecutionException(e.getMessage());
891 private Map loadProperties(ConfigLoader configLoader)
893 MojoExecutionException
895 /** Try to read configuration from properties-file */
896 if (hibernateProperties == null)
900 return configLoader.loadProperties("hibernate.properties");
902 catch (ConfigurationException e)
904 getLog().debug(e.getMessage());
905 return Collections.EMPTY_MAP;
912 File file = new File(hibernateProperties);
915 getLog().info("Reading settings from file " + hibernateProperties + "...");
916 return configLoader.loadProperties(file);
919 return configLoader.loadProperties(hibernateProperties);
921 catch (ConfigurationException e)
923 getLog().error("Error while reading properties!", e);
924 throw new MojoExecutionException(e.getMessage());
929 private LoadedConfig loadConfig(ConfigLoader configLoader)
930 throws MojoExecutionException
932 /** Try to read configuration from configuration-file */
933 if (hibernateConfig == null)
937 return configLoader.loadConfigXmlResource("hibernate.cfg.xml");
939 catch (ConfigurationException e)
941 getLog().debug(e.getMessage());
949 File file = new File(hibernateConfig);
952 getLog().info("Reading configuration from file " + hibernateConfig + "...");
953 return configLoader.loadConfigXmlFile(file);
957 return configLoader.loadConfigXmlResource(hibernateConfig);
960 catch (ConfigurationException e)
962 getLog().error("Error while reading configuration!", e);
963 throw new MojoExecutionException(e.getMessage());
968 private void configure(Properties properties, ModificationTracker tracker)
969 throws MojoFailureException
972 * Special treatment for the configuration-value "execute": if it is
973 * switched to "true", the genearation fo the schema should be forced!
975 if (tracker.check(EXECUTE, execute.toString()) && execute)
978 "hibernate.schema.execute was switched on: " +
979 "forcing generation/execution of SQL"
983 configure(properties, execute, EXECUTE);
986 * Configure the generation of the SQL.
987 * Overwrite values from properties-file if the configuration parameter is
988 * known to Hibernate.
990 configure(properties, dialect, DIALECT);
991 configure(properties, delimiter, HBM2DDL_DELIMITER);
992 configure(properties, format, FORMAT_SQL);
993 configure(properties, createNamespaces, HBM2DLL_CREATE_NAMESPACES);
994 configure(properties, implicitNamingStrategy, IMPLICIT_NAMING_STRATEGY);
995 configure(properties, physicalNamingStrategy, PHYSICAL_NAMING_STRATEGY);
996 configure(properties, outputDirectory, OUTPUTDIRECTORY);
997 configure(properties, scanDependencies, SCAN_DEPENDENCIES);
998 configure(properties, scanTestClasses, SCAN_TESTCLASSES);
999 configure(properties, testOutputDirectory, TEST_OUTPUTDIRECTORY);
1002 * Special treatment for the configuration-value "show": a change of its
1003 * configured value should not lead to a regeneration of the database
1007 show = Boolean.valueOf(properties.getProperty(SHOW_SQL));
1009 properties.setProperty(SHOW_SQL, show.toString());
1012 * Configure the connection parameters.
1013 * Overwrite values from properties-file.
1015 configure(properties, driver, DRIVER, JPA_JDBC_DRIVER);
1016 configure(properties, url, URL, JPA_JDBC_URL);
1017 configure(properties, username, USER, JPA_JDBC_USER);
1018 configure(properties, password, PASS, JPA_JDBC_PASSWORD);
1020 if (properties.isEmpty())
1022 getLog().error("No properties set!");
1023 throw new MojoFailureException("Hibernate configuration is missing!");
1026 getLog().info("Gathered configuration:");
1027 for (Entry<Object,Object> entry : properties.entrySet())
1028 getLog().info(" " + entry.getKey() + " = " + entry.getValue());
1031 private void configure(
1032 Properties properties,
1035 String alternativeKey
1038 configure(properties, value, key);
1040 if (properties.containsKey(alternativeKey))
1042 if (properties.containsKey(key))
1045 "Ignoring property " + alternativeKey + "=\"" +
1046 properties.getProperty(alternativeKey) +
1047 "\" in favour for property " + key + "=\"" +
1048 properties.getProperty(key) + "\""
1050 properties.remove(alternativeKey);
1054 value = properties.getProperty(alternativeKey);
1055 properties.remove(alternativeKey);
1057 "Using value \"" + value + "\" from property " + alternativeKey +
1058 " for property " + key
1060 properties.setProperty(key, value);
1065 private void configure(Properties properties, String value, String key)
1069 if (properties.containsKey(key))
1071 if (!properties.getProperty(key).equals(value))
1074 "Overwriting property " + key + "=\"" +
1075 properties.getProperty(key) +
1076 "\" with value \"" + value + "\""
1078 properties.setProperty(key, value);
1083 getLog().debug("Using value \"" + value + "\" for property " + key);
1084 properties.setProperty(key, value);
1089 private void configure(Properties properties, Boolean value, String key)
1091 configure(properties, value == null ? null : value.toString(), key);
1094 private File getOutputFile(String filename)
1096 MojoExecutionException
1098 File output = new File(filename);
1100 if (!output.isAbsolute())
1102 // Interpret relative file path relative to build directory
1103 output = new File(buildDirectory, filename);
1105 getLog().debug("Output file: " + output.getPath());
1107 // Ensure that directory path for specified file exists
1108 File outFileParentDir = output.getParentFile();
1109 if (null != outFileParentDir && !outFileParentDir.exists())
1114 "Creating directory path for output file:" +
1115 outFileParentDir.getPath()
1117 outFileParentDir.mkdirs();
1122 "Error creating directory path for output file: " + e.getMessage();
1123 getLog().error(error);
1124 throw new MojoExecutionException(error);
1130 output.createNewFile();
1132 catch (IOException e)
1134 String error = "Error creating output file: " + e.getMessage();
1135 getLog().error(error);
1136 throw new MojoExecutionException(error);
1139 if (!output.canWrite())
1142 "Output file " + output.getAbsolutePath() + " is not writable!";
1143 getLog().error(error);
1144 throw new MojoExecutionException(error);
1150 private void checkOutputFile(File output, ModificationTracker tracker)
1152 MojoExecutionException
1156 if (output.exists())
1157 tracker.track(SCRIPT, new FileInputStream(output));
1159 tracker.track(SCRIPT, ZonedDateTime.now().toString());
1161 catch (IOException e)
1164 "Error while checking the generated script: " + e.getMessage();
1165 getLog().error(error);
1166 throw new MojoExecutionException(error);
1170 private void addMappings(MetadataSources sources, ModificationTracker tracker)
1171 throws MojoFailureException
1173 getLog().debug("Adding explicitly configured mappings...");
1174 if (mappings != null)
1178 for (String filename : mappings.split("[\\s,]+"))
1180 // First try the filename as absolute/relative path
1181 File file = new File(filename);
1184 // If the file was not found, search for it in the resource-directories
1185 for (Resource resource : project.getResources())
1187 file = new File(resource.getDirectory() + File.separator + filename);
1194 if (file.isDirectory())
1195 // TODO: add support to read all mappings under a directory
1196 throw new MojoFailureException(file.getAbsolutePath() + " is a directory");
1197 if (tracker.track(filename, new FileInputStream(file)))
1198 getLog().debug(" - found new or modified mapping-file: " + filename);
1200 getLog().debug(" - mapping-file unchanged: " + filename);
1202 sources.addFile(file);
1205 throw new MojoFailureException("File " + filename + " could not be found in any of the configured resource-directories!");
1208 catch (IOException e)
1210 throw new MojoFailureException("Cannot calculate MD5 sums!", e);
1215 private void addRoot(Set<URL> urls, String path) throws MojoFailureException
1219 File dir = new File(path);
1222 getLog().info(" - adding " + dir.getAbsolutePath());
1223 urls.add(dir.toURI().toURL());
1227 "The directory cannot be scanned for annotated classes, " +
1228 "because it does not exist: " +
1229 dir.getAbsolutePath()
1232 catch (MalformedURLException e)
1234 getLog().error("error while adding the project-root to the list of roots to scan!", e);
1235 throw new MojoFailureException(e.getMessage());
1239 private void addDependencies(Set<URL> urls) throws MojoFailureException
1243 if (scanDependencies != null)
1245 Matcher matcher = SPLIT.matcher(scanDependencies);
1246 while (matcher.find())
1248 getLog().debug("Adding dependencies from scope " + matcher.group() + " to the list of roots to scan");
1249 for (Artifact artifact : project.getDependencyArtifacts())
1251 if (!artifact.getScope().equalsIgnoreCase(matcher.group()))
1253 if (artifact.getFile() == null)
1255 getLog().warn("Cannot add dependency " + artifact.getId() + ": no JAR-file available!");
1258 getLog().debug(" - adding " + artifact.getId());
1259 urls.add(artifact.getFile().toURI().toURL());
1264 catch (MalformedURLException e)
1266 getLog().error("Error while adding dependencies to the list of roots to scan!", e);
1267 throw new MojoFailureException(e.getMessage());
1271 private Set<String> scanUrls(Set<URL> scanRoots)
1273 MojoFailureException
1277 AnnotationDB db = new AnnotationDB();
1278 for (URL root : scanRoots)
1279 db.scanArchives(root);
1281 Set<String> classes = new HashSet<String>();
1282 if (db.getAnnotationIndex().containsKey(Entity.class.getName()))
1283 classes.addAll(db.getAnnotationIndex().get(Entity.class.getName()));
1284 if (db.getAnnotationIndex().containsKey(MappedSuperclass.class.getName()))
1285 classes.addAll(db.getAnnotationIndex().get(MappedSuperclass.class.getName()));
1286 if (db.getAnnotationIndex().containsKey(Embeddable.class.getName()))
1287 classes.addAll(db.getAnnotationIndex().get(Embeddable.class.getName()));
1293 getLog().error("Error while scanning!", e);
1294 throw new MojoFailureException(e.getMessage());
1298 private void addAnnotated(
1300 MetadataSources sources,
1301 ClassLoaderService classLoaderService,
1302 ModificationTracker tracker
1305 MojoFailureException,
1306 MojoExecutionException
1310 getLog().info("Adding annotated resource: " + name);
1311 String packageName = null;
1313 boolean error = false;
1316 Class<?> annotatedClass = classLoaderService.classForName(name);
1317 String resourceName = annotatedClass.getName();
1319 resourceName.substring(
1320 resourceName.lastIndexOf(".") + 1,
1321 resourceName.length()
1323 InputStream is = annotatedClass.getResourceAsStream(resourceName);
1326 if (tracker.track(name, is))
1327 getLog().debug("New or modified class: " + name);
1329 getLog().debug("Unchanged class: " + name);
1330 sources.addAnnotatedClass(annotatedClass);
1331 packageName = annotatedClass.getPackage().getName();
1335 getLog().error("cannot find ressource " + resourceName + " for class " + name);
1339 catch(ClassLoadingException e)
1345 throw new MojoExecutionException("error while inspecting annotated class " + name);
1348 while (packageName != null)
1350 if (packages.contains(packageName))
1352 String resource = packageName.replace('.', '/') + "/package-info.class";
1353 InputStream is = classLoaderService.locateResourceStream(resource);
1356 // No compiled package-info available: no package-level annotations!
1357 getLog().debug("Package " + packageName + " is not annotated.");
1361 if (tracker.track(packageName, is))
1362 getLog().debug("New or modified package: " + packageName);
1364 getLog().debug("Unchanged package: " + packageName);
1365 getLog().info("Adding annotations from package " + packageName);
1366 sources.addPackage(packageName);
1368 packages.add(packageName);
1369 int i = packageName.lastIndexOf('.');
1373 packageName = packageName.substring(0,i);
1378 getLog().error("Error while adding the annotated class " + name, e);
1379 throw new MojoFailureException(e.getMessage());
1383 private ParsedPersistenceXmlDescriptor loadPersistenceUnit(
1384 ClassLoaderService classLoaderService,
1385 Properties properties
1388 MojoFailureException
1390 PersistenceXmlParser parser =
1391 new PersistenceXmlParser(
1393 PersistenceUnitTransactionType.RESOURCE_LOCAL
1396 Map<String, ParsedPersistenceXmlDescriptor> units =
1397 parser.doResolve(properties);
1399 if (persistenceUnit == null)
1401 Iterator<String> names = units.keySet().iterator();
1402 if (!names.hasNext())
1404 getLog().info("Found no META-INF/persistence.xml.");
1408 String name = names.next();
1409 if (!names.hasNext())
1411 getLog().info("Using persistence-unit " + name);
1412 return units.get(name);
1415 StringBuilder builder = new StringBuilder();
1416 builder.append("No name provided and multiple persistence units found: ");
1417 builder.append(name);
1418 while(names.hasNext())
1420 builder.append(", ");
1421 builder.append(names.next());
1423 builder.append('.');
1424 throw new MojoFailureException(builder.toString());
1427 if (units.containsKey(persistenceUnit))
1429 getLog().info("Using configured persistence-unit " + persistenceUnit);
1430 return units.get(persistenceUnit);
1433 throw new MojoFailureException("Could not find persistence-unit " + persistenceUnit);
1437 public static void printStrackTrace(StringBuilder builder, Throwable t)
1441 builder.append("\n\tCause: ");
1442 builder.append(t.getMessage() == null ? "" : t.getMessage().replaceAll("\\s+", " "));
1443 for (StackTraceElement trace : t.getStackTrace())
1445 builder.append("\n\t");
1446 builder.append(trace.getClassName());
1447 builder.append(".");
1448 builder.append(trace.getMethodName());
1449 builder.append("():");
1450 builder.append(trace.getLineNumber());