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 EXECUTE = "hibernate.schema.execute";
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 * Excecute the generated SQL.
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.execute" default-value="true"
143 private Boolean execute;
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;
168 * Force generation/execution
170 * Force the generation and (if configured) the execution of the SQL, even if
171 * no modified or newly added annotated classes where found and the
172 * configuration was not changed.
174 * <code>skip</code> takes precedence over <code>force</code>.
176 * <strong>Important:</strong>
177 * This configuration value can only be configured through the
178 * <code>pom.xml</code>, or by the definition of a system-property, because
179 * it is not known by Hibernate nor JPA and, hence, not picked up from
180 * their configuration!
182 * @parameter property="hibernate.schema.force" default-value="false"
185 private boolean force;
190 * @parameter property="hibernate.dialect"
193 private String dialect;
196 * Delimiter in output-file.
198 * <strong>Important:</strong>
199 * This configuration value can only be configured through the
200 * <code>pom.xml</code>, or by the definition of a system-property, because
201 * it is not known by Hibernate nor JPA and, hence, not picked up from
202 * their configuration!
204 * @parameter property="hibernate.hbm2ddl.delimiter" default-value=";"
207 private String delimiter;
210 * Show the generated SQL in the command-line output.
212 * @parameter property="hibernate.show_sql"
215 private Boolean show;
218 * Format output-file.
220 * @parameter property="hibernate.format_sql"
223 private Boolean format;
226 * Specifies whether to automatically create also the database schema/catalog.
228 * @parameter property="hibernate.hbm2dll.create_namespaces" default-value="false"
231 private Boolean createNamespaces;
234 * Implicit naming strategy
236 * @parameter property="hibernate.implicit_naming_strategy"
239 private String implicitNamingStrategy;
242 * Physical naming strategy
244 * @parameter property="hibernate.physical_naming_strategy"
247 private String physicalNamingStrategy;
250 * Wether the project should be scanned for annotated-classes, or not
252 * This parameter is intended to allow overwriting of the parameter
253 * <code>exclude-unlisted-classes</code> of a <code>persistence-unit</code>.
254 * If not specified, it defaults to <code>true</code>
256 * @parameter property="hibernate.schema.scan.classes"
259 private Boolean scanClasses;
262 * Classes-Directory to scan.
264 * This parameter defaults to the maven build-output-directory for classes.
265 * Additionally, all dependencies are scanned for annotated classes.
267 * <strong>Important:</strong>
268 * This configuration value can only be configured through the
269 * <code>pom.xml</code>, or by the definition of a system-property, because
270 * it is not known by Hibernate nor JPA and, hence, not picked up from
271 * their configuration!
273 * @parameter property="project.build.outputDirectory"
276 private String outputDirectory;
279 * Dependency-Scopes, that should be scanned for annotated classes.
281 * By default, only dependencies in the scope <code>compile</code> are
282 * scanned for annotated classes. Multiple scopes can be seperated by
283 * white space or commas.
285 * If you do not want any dependencies to be scanned for annotated
286 * classes, set this parameter to <code>none</code>.
288 * The plugin does not scan for annotated classes in transitive
289 * dependencies. If some of your annotated classes are hidden in a
290 * transitive dependency, you can simply add that dependency explicitly.
292 * @parameter property="hibernate.schema.scan.dependencies" default-value="compile"
295 private String scanDependencies;
298 * Whether to scan the test-branch of the project for annotated classes, or
301 * If this parameter is set to <code>true</code> the test-classes of the
302 * artifact will be scanned for hibernate-annotated classes additionally.
304 * <strong>Important:</strong>
305 * This configuration value can only be configured through the
306 * <code>pom.xml</code>, or by the definition of a system-property, because
307 * it is not known by Hibernate nor JPA and, hence, not picked up from
308 * their configuration!
310 * @parameter property="hibernate.schema.scan.test_classes" default-value="false"
313 private Boolean scanTestClasses;
316 * Test-Classes-Directory to scan.
318 * This parameter defaults to the maven build-output-directory for
321 * This parameter is only used, when <code>scanTestClasses</code> is set
322 * to <code>true</code>!
324 * <strong>Important:</strong>
325 * This configuration value can only be configured through the
326 * <code>pom.xml</code>, or by the definition of a system-property, because
327 * it is not known by Hibernate nor JPA and, hence, not picked up from
328 * their configuration!
330 * @parameter property="project.build.testOutputDirectory"
333 private String testOutputDirectory;
336 /** Conection parameters *************************************************/
341 * @parameter property="hibernate.connection.driver_class"
344 private String driver;
349 * @parameter property="hibernate.connection.url"
357 * @parameter property="hibernate.connection.username"
360 private String username;
365 * @parameter property="hibernate.connection.password"
368 private String password;
371 /** Parameters to locate configuration sources ****************************/
374 * Path to a file or name of a ressource with hibernate properties.
375 * If this parameter is specified, the plugin will try to load configuration
376 * values from a file with the given path or a ressource on the classpath with
377 * the given name. If both fails, the execution of the plugin will fail.
379 * If this parameter is not set the plugin will load configuration values
380 * from a ressource named <code>hibernate.properties</code> on the classpath,
381 * if it is present, but will not fail if there is no such ressource.
383 * During ressource-lookup, the test-classpath takes precedence.
388 private String hibernateProperties;
391 * Path to Hibernate configuration file (.cfg.xml).
392 * If this parameter is specified, the plugin will try to load configuration
393 * values from a file with the given path or a ressource on the classpath with
394 * the given name. If both fails, the execution of the plugin will fail.
396 * If this parameter is not set the plugin will load configuration values
397 * from a ressource named <code>hibernate.cfg.xml</code> on the classpath,
398 * if it is present, but will not fail if there is no such ressource.
400 * During ressource-lookup, the test-classpath takes precedence.
402 * Settings in this file will overwrite settings in the properties file.
407 private String hibernateConfig;
410 * Name of the persistence-unit.
411 * If this parameter is specified, the plugin will try to load configuration
412 * values from a persistence-unit with the specified name. If no such
413 * persistence-unit can be found, the plugin will throw an exception.
415 * If this parameter is not set and there is only one persistence-unit
416 * available, that unit will be used automatically. But if this parameter is
417 * not set and there are multiple persistence-units available on,
418 * the class-path, the execution of the plugin will fail.
420 * Settings in this file will overwrite settings in the properties or the
421 * configuration file.
426 private String persistenceUnit;
429 * List of Hibernate-Mapping-Files (XML).
430 * Multiple files can be separated with white-spaces and/or commas.
432 * @parameter property="hibernate.mapping"
435 private String mappings;
439 public final void execute(String filename)
441 MojoFailureException,
442 MojoExecutionException
446 getLog().info("Execution of hibernate-maven-plugin was skipped!");
447 project.getProperties().setProperty(SKIPPED, "true");
451 ModificationTracker tracker;
454 tracker = new ModificationTracker(buildDirectory, filename, getLog());
456 catch (NoSuchAlgorithmException e)
458 throw new MojoFailureException("Digest-Algorithm MD5 is missing!", e);
461 final SimpleConnectionProvider connectionProvider =
462 new SimpleConnectionProvider(getLog());
466 /** Start extended logging */
467 MavenLogAppender.startPluginLog(this);
469 /** Load checksums for old mapping and configuration */
472 /** Create the ClassLoader */
473 MutableClassLoader classLoader = createClassLoader();
475 /** Create a BootstrapServiceRegistry with the created ClassLoader */
476 BootstrapServiceRegistry bootstrapServiceRegitry =
477 new BootstrapServiceRegistryBuilder()
478 .applyClassLoader(classLoader)
480 ClassLoaderService classLoaderService =
481 bootstrapServiceRegitry.getService(ClassLoaderService.class);
483 Properties properties = new Properties();
484 ConfigLoader configLoader = new ConfigLoader(bootstrapServiceRegitry);
486 /** Loading and merging configuration */
487 properties.putAll(loadProperties(configLoader));
488 LoadedConfig config = loadConfig(configLoader);
490 properties.putAll(config.getConfigurationValues());
491 ParsedPersistenceXmlDescriptor unit =
492 loadPersistenceUnit(classLoaderService, properties);
494 properties.putAll(unit.getProperties());
496 /** Overwriting/Completing configuration */
497 configure(properties, tracker);
499 /** Check configuration for modifications */
500 if(tracker.track(properties))
501 getLog().debug("Configuration has changed.");
503 getLog().debug("Configuration unchanged.");
505 /** Check, that the outputfile is writable */
506 final File output = getOutputFile(filename);
508 /** Configure Hibernate */
509 final StandardServiceRegistry serviceRegistry =
510 new StandardServiceRegistryBuilder(bootstrapServiceRegitry)
511 .applySettings(properties)
512 .addService(ConnectionProvider.class, connectionProvider)
514 final MetadataSources sources = new MetadataSources(serviceRegistry);
516 /** Add the remaining class-path-elements */
517 completeClassPath(classLoader);
519 /** Apply mappings from hibernate-configuration, if present */
522 for (MappingReference mapping : config.getMappingReferences())
523 mapping.apply(sources);
529 /** No persistent unit: default behaviour */
530 if (scanClasses == null)
532 Set<URL> urls = new HashSet<URL>();
534 addRoot(urls, outputDirectory);
536 addRoot(urls, testOutputDirectory);
537 addDependencies(urls);
538 classes = scanUrls(urls);
542 /** Follow configuration in persisten unit */
543 if (scanClasses == null)
544 scanClasses = !unit.isExcludeUnlistedClasses();
545 Set<URL> urls = new HashSet<URL>();
549 * Scan the root of the persiten unit and configured jars for
552 urls.add(unit.getPersistenceUnitRootUrl());
553 for (URL url : unit.getJarFileUrls())
557 addRoot(urls, testOutputDirectory);
558 classes = scanUrls(urls);
559 for (String className : unit.getManagedClassNames())
560 classes.add(className);
562 * Add mappings from the default mapping-file
563 * <code>META-INF/orm.xml</code>, if present
565 boolean error = false;
567 is = classLoader.getResourceAsStream("META-INF/orm.xml");
570 getLog().info("Adding default JPA-XML-mapping from META-INF/orm.xml");
573 tracker.track("META-INF/orm.xml", is);
574 sources.addResource("META-INF/orm.xml");
576 catch (IOException e)
578 getLog().error("cannot read META-INF/orm.xml: " + e);
584 getLog().debug("no META-INF/orm.xml found");
587 * Add mappings from files, that are explicitly configured in the
590 for (String mapping : unit.getMappingFileNames())
592 getLog().info("Adding explicitly configured mapping from " + mapping);
593 is = classLoader.getResourceAsStream(mapping);
598 tracker.track(mapping, is);
599 sources.addResource(mapping);
601 catch (IOException e)
603 getLog().info("cannot read mapping-file " + mapping + ": " + e);
609 getLog().error("cannot find mapping-file " + mapping);
614 throw new MojoFailureException(
615 "error, while reading mappings configured in persistence-unit \"" +
621 /** Add the configured/collected annotated classes */
622 for (String className : classes)
623 addAnnotated(className, sources, classLoaderService, tracker);
625 /** Add explicitly configured classes */
626 addMappings(sources, tracker);
628 /** Skip execution, if mapping and configuration is unchanged */
629 if (!tracker.modified())
631 getLog().info("Mapping and configuration unchanged.");
633 getLog().info("Generation/execution is forced!");
636 getLog().info("Skipping schema generation!");
637 project.getProperties().setProperty(SKIPPED, "true");
643 /** Truncate output file */
646 new FileOutputStream(output).getChannel().truncate(0).close();
648 catch (IOException e)
651 "Error while truncating " + output.getAbsolutePath() + ": "
653 getLog().warn(error);
654 throw new MojoExecutionException(error);
657 /** Create a connection, if sufficient configuration infromation is available */
658 connectionProvider.open(classLoaderService, properties);
660 MetadataBuilder metadataBuilder = sources.getMetadataBuilder();
662 StrategySelector strategySelector =
663 serviceRegistry.getService(StrategySelector.class);
665 if (properties.containsKey(IMPLICIT_NAMING_STRATEGY))
667 metadataBuilder.applyImplicitNamingStrategy(
668 strategySelector.resolveStrategy(
669 ImplicitNamingStrategy.class,
670 properties.getProperty(IMPLICIT_NAMING_STRATEGY)
675 if (properties.containsKey(PHYSICAL_NAMING_STRATEGY))
677 metadataBuilder.applyPhysicalNamingStrategy(
678 strategySelector.resolveStrategy(
679 PhysicalNamingStrategy.class,
680 properties.getProperty(PHYSICAL_NAMING_STRATEGY)
685 /** Prepare the generation of the SQL */
686 Map settings = new HashMap();
689 .getService(ConfigurationService.class)
692 ExceptionHandlerCollectingImpl handler =
693 new ExceptionHandlerCollectingImpl();
694 ExecutionOptions options =
695 SchemaManagementToolCoordinator
696 .buildExecutionOptions(settings, handler);
697 final EnumSet<TargetType> targetTypes = EnumSet.of(TargetType.SCRIPT);
699 targetTypes.add(TargetType.DATABASE);
700 TargetDescriptor target = new TargetDescriptor()
703 public EnumSet<TargetType> getTargetTypes()
709 public ScriptTargetOutput getScriptTargetOutput()
714 .getService(ConfigurationService.class)
716 .get(AvailableSettings.HBM2DDL_CHARSET_NAME);
717 return new ScriptTargetOutputToFile(output, charset);
722 * Change class-loader of current thread.
723 * This is necessary, because still not all parts of Hibernate 5 use
724 * the newly introduced ClassLoaderService and will fail otherwise!
726 Thread thread = Thread.currentThread();
727 ClassLoader contextClassLoader = thread.getContextClassLoader();
730 thread.setContextClassLoader(classLoader);
731 build((MetadataImplementor)metadataBuilder.build(), options, target);
735 thread.setContextClassLoader(contextClassLoader);
736 for (Exception e : handler.getExceptions())
737 getLog().error(e.getMessage());
740 catch (MojoExecutionException e)
745 catch (MojoFailureException e)
750 catch (RuntimeException e)
757 /** Remember mappings and configuration */
760 /** Close the connection - if one was opened */
761 connectionProvider.close();
763 /** Stop Log-Capturing */
764 MavenLogAppender.endPluginLog(this);
770 MetadataImplementor metadata,
771 ExecutionOptions options,
772 TargetDescriptor target
775 MojoFailureException,
776 MojoExecutionException;
779 private MutableClassLoader createClassLoader() throws MojoExecutionException
783 getLog().debug("Creating ClassLoader for project-dependencies...");
784 LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
787 file = new File(testOutputDirectory);
790 getLog().info("Creating test-output-directory: " + testOutputDirectory);
793 urls.add(file.toURI().toURL());
795 file = new File(outputDirectory);
798 getLog().info("Creating output-directory: " + outputDirectory);
801 urls.add(file.toURI().toURL());
803 return new MutableClassLoader(urls, getLog());
807 getLog().error("Error while creating ClassLoader!", e);
808 throw new MojoExecutionException(e.getMessage());
812 private void completeClassPath(MutableClassLoader classLoader)
814 MojoExecutionException
818 getLog().debug("Completing class-paths of the ClassLoader for project-dependencies...");
819 List<String> classpathFiles = project.getCompileClasspathElements();
821 classpathFiles.addAll(project.getTestClasspathElements());
822 LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
823 for (String pathElement : classpathFiles)
825 getLog().debug("Dependency: " + pathElement);
826 urls.add(new File(pathElement).toURI().toURL());
828 classLoader.add(urls);
832 getLog().error("Error while creating ClassLoader!", e);
833 throw new MojoExecutionException(e.getMessage());
837 private Map loadProperties(ConfigLoader configLoader)
839 MojoExecutionException
841 /** Try to read configuration from properties-file */
842 if (hibernateProperties == null)
846 return configLoader.loadProperties("hibernate.properties");
848 catch (ConfigurationException e)
850 getLog().debug(e.getMessage());
851 return Collections.EMPTY_MAP;
858 File file = new File(hibernateProperties);
861 getLog().info("Reading settings from file " + hibernateProperties + "...");
862 return configLoader.loadProperties(file);
865 return configLoader.loadProperties(hibernateProperties);
867 catch (ConfigurationException e)
869 getLog().error("Error while reading properties!", e);
870 throw new MojoExecutionException(e.getMessage());
875 private LoadedConfig loadConfig(ConfigLoader configLoader)
876 throws MojoExecutionException
878 /** Try to read configuration from configuration-file */
879 if (hibernateConfig == null)
883 return configLoader.loadConfigXmlResource("hibernate.cfg.xml");
885 catch (ConfigurationException e)
887 getLog().debug(e.getMessage());
895 File file = new File(hibernateConfig);
898 getLog().info("Reading configuration from file " + hibernateConfig + "...");
899 return configLoader.loadConfigXmlFile(file);
903 return configLoader.loadConfigXmlResource(hibernateConfig);
906 catch (ConfigurationException e)
908 getLog().error("Error while reading configuration!", e);
909 throw new MojoExecutionException(e.getMessage());
914 private void configure(Properties properties, ModificationTracker tracker)
915 throws MojoFailureException
918 * Special treatment for the configuration-value "execute": if it is
919 * switched to "true", the genearation fo the schema should be forced!
921 if (tracker.check(EXECUTE, execute.toString()) && execute)
925 * Configure the generation of the SQL.
926 * Overwrite values from properties-file if the configuration parameter is
927 * known to Hibernate.
929 configure(properties, dialect, DIALECT);
930 configure(properties, delimiter, HBM2DDL_DELIMITER);
931 configure(properties, format, FORMAT_SQL);
932 configure(properties, createNamespaces, HBM2DLL_CREATE_NAMESPACES);
933 configure(properties, implicitNamingStrategy, IMPLICIT_NAMING_STRATEGY);
934 configure(properties, physicalNamingStrategy, PHYSICAL_NAMING_STRATEGY);
935 tracker.track(OUTPUTDIRECTORY, outputDirectory); // << not reflected in hibernate configuration!
936 tracker.track(SCAN_DEPENDENCIES, scanDependencies); // << not reflected in hibernate configuration!
937 tracker.track(SCAN_TESTCLASSES, scanTestClasses.toString()); // << not reflected in hibernate configuration!
938 tracker.track(TEST_OUTPUTDIRECTORY, testOutputDirectory); // << not reflected in hibernate configuration!
941 * Special treatment for the configuration-value "show": a change of its
942 * configured value should not lead to a regeneration of the database
946 show = Boolean.valueOf(properties.getProperty(SHOW_SQL));
948 properties.setProperty(SHOW_SQL, show.toString());
951 * Configure the connection parameters.
952 * Overwrite values from properties-file.
954 configure(properties, driver, DRIVER, JPA_JDBC_DRIVER);
955 configure(properties, url, URL, JPA_JDBC_URL);
956 configure(properties, username, USER, JPA_JDBC_USER);
957 configure(properties, password, PASS, JPA_JDBC_PASSWORD);
959 if (properties.isEmpty())
961 getLog().error("No properties set!");
962 throw new MojoFailureException("Hibernate configuration is missing!");
965 getLog().info("Gathered hibernate-configuration (turn on debugging for details):");
966 for (Entry<Object,Object> entry : properties.entrySet())
967 getLog().info(" " + entry.getKey() + " = " + entry.getValue());
970 private void configure(
971 Properties properties,
974 String alternativeKey
977 configure(properties, value, key);
979 if (properties.containsKey(alternativeKey))
981 if (properties.containsKey(key))
984 "Ignoring property " + alternativeKey + "=\"" +
985 properties.getProperty(alternativeKey) +
986 "\" in favour for property " + key + "=\"" +
987 properties.getProperty(key) + "\""
989 properties.remove(alternativeKey);
993 value = properties.getProperty(alternativeKey);
994 properties.remove(alternativeKey);
996 "Using value \"" + value + "\" from property " + alternativeKey +
997 " for property " + key
999 properties.setProperty(key, value);
1004 private void configure(Properties properties, String value, String key)
1008 if (properties.containsKey(key))
1010 "Overwriting property " + key + "=\"" +
1011 properties.getProperty(key) +
1012 "\" with value \"" + value + "\""
1015 getLog().debug("Using value \"" + value + "\" for property " + key);
1016 properties.setProperty(key, value);
1020 private void configure(Properties properties, Boolean value, String key)
1022 configure(properties, value == null ? null : value.toString(), key);
1025 private File getOutputFile(String filename)
1027 MojoExecutionException
1029 File output = new File(filename);
1031 if (!output.isAbsolute())
1033 // Interpret relative file path relative to build directory
1034 output = new File(buildDirectory, filename);
1036 getLog().debug("Output file: " + output.getPath());
1038 // Ensure that directory path for specified file exists
1039 File outFileParentDir = output.getParentFile();
1040 if (null != outFileParentDir && !outFileParentDir.exists())
1045 "Creating directory path for output file:" +
1046 outFileParentDir.getPath()
1048 outFileParentDir.mkdirs();
1053 "Error creating directory path for output file: " + e.getMessage();
1054 getLog().error(error);
1055 throw new MojoExecutionException(error);
1061 output.createNewFile();
1063 catch (IOException e)
1065 String error = "Error creating output file: " + e.getMessage();
1066 getLog().error(error);
1067 throw new MojoExecutionException(error);
1070 if (!output.canWrite())
1073 "Output file " + output.getAbsolutePath() + " is not writable!";
1074 getLog().error(error);
1075 throw new MojoExecutionException(error);
1081 private void addMappings(MetadataSources sources, ModificationTracker tracker)
1082 throws MojoFailureException
1084 getLog().debug("Adding explicitly configured mappings...");
1085 if (mappings != null)
1089 for (String filename : mappings.split("[\\s,]+"))
1091 // First try the filename as absolute/relative path
1092 File file = new File(filename);
1095 // If the file was not found, search for it in the resource-directories
1096 for (Resource resource : project.getResources())
1098 file = new File(resource.getDirectory() + File.separator + filename);
1105 if (file.isDirectory())
1106 // TODO: add support to read all mappings under a directory
1107 throw new MojoFailureException(file.getAbsolutePath() + " is a directory");
1108 if (tracker.track(filename, new FileInputStream(file)))
1109 getLog().debug("Found new or modified mapping-file: " + filename);
1111 getLog().debug("Mapping-file unchanged: " + filename);
1113 sources.addFile(file);
1116 throw new MojoFailureException("File " + filename + " could not be found in any of the configured resource-directories!");
1119 catch (IOException e)
1121 throw new MojoFailureException("Cannot calculate MD5 sums!", e);
1126 private void addRoot(Set<URL> urls, String path) throws MojoFailureException
1130 File dir = new File(outputDirectory);
1133 getLog().info("Adding " + dir.getAbsolutePath() + " to the list of roots to scan...");
1134 urls.add(dir.toURI().toURL());
1137 catch (MalformedURLException e)
1139 getLog().error("error while adding the project-root to the list of roots to scan!", e);
1140 throw new MojoFailureException(e.getMessage());
1144 private void addDependencies(Set<URL> urls) throws MojoFailureException
1148 if (scanDependencies != null)
1150 Matcher matcher = SPLIT.matcher(scanDependencies);
1151 while (matcher.find())
1153 getLog().info("Adding dependencies from scope " + matcher.group() + " to the list of roots to scan");
1154 for (Artifact artifact : project.getDependencyArtifacts())
1156 if (!artifact.getScope().equalsIgnoreCase(matcher.group()))
1158 if (artifact.getFile() == null)
1160 getLog().warn("Cannot add dependency " + artifact.getId() + ": no JAR-file available!");
1163 getLog().info("Adding dependencies from scope " + artifact.getId() + " to the list of roots to scan");
1164 urls.add(artifact.getFile().toURI().toURL());
1169 catch (MalformedURLException e)
1171 getLog().error("Error while adding dependencies to the list of roots to scan!", e);
1172 throw new MojoFailureException(e.getMessage());
1176 private Set<String> scanUrls(Set<URL> scanRoots)
1178 MojoFailureException
1182 AnnotationDB db = new AnnotationDB();
1183 for (URL root : scanRoots)
1184 db.scanArchives(root);
1186 Set<String> classes = new HashSet<String>();
1187 if (db.getAnnotationIndex().containsKey(Entity.class.getName()))
1188 classes.addAll(db.getAnnotationIndex().get(Entity.class.getName()));
1189 if (db.getAnnotationIndex().containsKey(MappedSuperclass.class.getName()))
1190 classes.addAll(db.getAnnotationIndex().get(MappedSuperclass.class.getName()));
1191 if (db.getAnnotationIndex().containsKey(Embeddable.class.getName()))
1192 classes.addAll(db.getAnnotationIndex().get(Embeddable.class.getName()));
1198 getLog().error("Error while scanning!", e);
1199 throw new MojoFailureException(e.getMessage());
1203 private void addAnnotated(
1205 MetadataSources sources,
1206 ClassLoaderService classLoaderService,
1207 ModificationTracker tracker
1210 MojoFailureException,
1211 MojoExecutionException
1215 getLog().info("Adding annotated resource: " + name);
1216 String packageName = null;
1218 boolean error = false;
1221 Class<?> annotatedClass = classLoaderService.classForName(name);
1222 String resourceName = annotatedClass.getName();
1224 resourceName.substring(
1225 resourceName.lastIndexOf(".") + 1,
1226 resourceName.length()
1228 InputStream is = annotatedClass.getResourceAsStream(resourceName);
1231 if (tracker.track(name, is))
1232 getLog().debug("New or modified class: " + name);
1234 getLog().debug("Unchanged class: " + name);
1235 sources.addAnnotatedClass(annotatedClass);
1236 packageName = annotatedClass.getPackage().getName();
1240 getLog().error("cannot find ressource " + resourceName + " for class " + name);
1244 catch(ClassLoadingException e)
1250 throw new MojoExecutionException("error while inspecting annotated class " + name);
1253 while (packageName != null)
1255 if (packages.contains(packageName))
1257 String resource = packageName.replace('.', '/') + "/package-info.class";
1258 InputStream is = classLoaderService.locateResourceStream(resource);
1261 // No compiled package-info available: no package-level annotations!
1262 getLog().debug("Package " + packageName + " is not annotated.");
1266 if (tracker.track(packageName, is))
1267 getLog().debug("New or modified package: " + packageName);
1269 getLog().debug("Unchanged package: " + packageName);
1270 getLog().info("Adding annotations from package " + packageName);
1271 sources.addPackage(packageName);
1273 packages.add(packageName);
1274 int i = packageName.lastIndexOf('.');
1278 packageName = packageName.substring(0,i);
1283 getLog().error("Error while adding the annotated class " + name, e);
1284 throw new MojoFailureException(e.getMessage());
1288 private ParsedPersistenceXmlDescriptor loadPersistenceUnit(
1289 ClassLoaderService classLoaderService,
1290 Properties properties
1293 MojoFailureException
1295 PersistenceXmlParser parser =
1296 new PersistenceXmlParser(
1298 PersistenceUnitTransactionType.RESOURCE_LOCAL
1301 Map<String, ParsedPersistenceXmlDescriptor> units =
1302 parser.doResolve(properties);
1304 if (persistenceUnit == null)
1306 Iterator<String> names = units.keySet().iterator();
1307 if (!names.hasNext())
1309 getLog().info("Found no META-INF/persistence.xml.");
1313 String name = names.next();
1314 if (!names.hasNext())
1316 getLog().info("Using persistence-unit " + name);
1317 return units.get(name);
1320 StringBuilder builder = new StringBuilder();
1321 builder.append("No name provided and multiple persistence units found: ");
1322 builder.append(name);
1323 while(names.hasNext())
1325 builder.append(", ");
1326 builder.append(names.next());
1328 builder.append('.');
1329 throw new MojoFailureException(builder.toString());
1332 if (units.containsKey(persistenceUnit))
1334 getLog().info("Using configured persistence-unit " + persistenceUnit);
1335 return units.get(persistenceUnit);
1338 throw new MojoFailureException("Could not find persistence-unit " + persistenceUnit);