1 package de.juplo.plugins.hibernate;
4 import com.pyx4j.log4j.MavenLogAppender;
6 import java.io.FileInputStream;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.net.MalformedURLException;
11 import java.security.NoSuchAlgorithmException;
12 import java.util.Collections;
13 import java.util.EnumSet;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.Iterator;
17 import java.util.LinkedHashSet;
18 import java.util.List;
20 import java.util.Map.Entry;
21 import java.util.Properties;
23 import java.util.regex.Matcher;
24 import java.util.regex.Pattern;
25 import javax.persistence.Embeddable;
26 import javax.persistence.Entity;
27 import javax.persistence.MappedSuperclass;
28 import javax.persistence.spi.PersistenceUnitTransactionType;
29 import org.apache.maven.artifact.Artifact;
30 import org.apache.maven.model.Resource;
31 import org.apache.maven.plugin.AbstractMojo;
32 import org.apache.maven.plugin.MojoExecutionException;
33 import org.apache.maven.plugin.MojoFailureException;
34 import org.apache.maven.project.MavenProject;
35 import org.hibernate.boot.MetadataBuilder;
36 import org.hibernate.boot.MetadataSources;
37 import org.hibernate.boot.cfgxml.internal.ConfigLoader;
38 import org.hibernate.boot.cfgxml.spi.LoadedConfig;
39 import org.hibernate.boot.cfgxml.spi.MappingReference;
40 import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
41 import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
42 import org.hibernate.boot.registry.BootstrapServiceRegistry;
43 import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
44 import org.hibernate.boot.registry.StandardServiceRegistry;
45 import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
46 import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
47 import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
48 import org.hibernate.boot.registry.selector.spi.StrategySelector;
49 import org.hibernate.boot.spi.MetadataImplementor;
50 import org.hibernate.cfg.AvailableSettings;
51 import static org.hibernate.cfg.AvailableSettings.DIALECT;
52 import static org.hibernate.cfg.AvailableSettings.DRIVER;
53 import static org.hibernate.cfg.AvailableSettings.FORMAT_SQL;
54 import static org.hibernate.cfg.AvailableSettings.HBM2DDL_DELIMITER;
55 import static org.hibernate.cfg.AvailableSettings.HBM2DLL_CREATE_NAMESPACES;
56 import static org.hibernate.cfg.AvailableSettings.IMPLICIT_NAMING_STRATEGY;
57 import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_DRIVER;
58 import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_PASSWORD;
59 import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_URL;
60 import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_USER;
61 import static org.hibernate.cfg.AvailableSettings.PASS;
62 import static org.hibernate.cfg.AvailableSettings.PHYSICAL_NAMING_STRATEGY;
63 import static org.hibernate.cfg.AvailableSettings.SHOW_SQL;
64 import static org.hibernate.cfg.AvailableSettings.USER;
65 import static org.hibernate.cfg.AvailableSettings.URL;
66 import org.hibernate.engine.config.spi.ConfigurationService;
67 import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
68 import org.hibernate.internal.util.config.ConfigurationException;
69 import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor;
70 import org.hibernate.jpa.boot.internal.PersistenceXmlParser;
71 import org.hibernate.tool.schema.TargetType;
72 import org.hibernate.tool.schema.internal.ExceptionHandlerCollectingImpl;
73 import org.hibernate.tool.schema.internal.exec.ScriptTargetOutputToFile;
74 import org.hibernate.tool.schema.spi.ExecutionOptions;
75 import org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator;
76 import org.hibernate.tool.schema.spi.ScriptTargetOutput;
77 import org.hibernate.tool.schema.spi.TargetDescriptor;
78 import org.scannotation.AnnotationDB;
82 * Baseclass with common attributes and methods.
84 * @phase process-classes
86 * @requiresDependencyResolution runtime
88 public abstract class AbstractSchemaMojo extends AbstractMojo
90 public final static String EXPORT = "hibernate.schema.export";
91 public final static String OUTPUTDIRECTORY = "project.build.outputDirectory";
92 public final static String SCAN_CLASSES = "hibernate.schema.scan.classes";
93 public final static String SCAN_DEPENDENCIES = "hibernate.schema.scan.dependencies";
94 public final static String SCAN_TESTCLASSES = "hibernate.schema.scan.test_classes";
95 public final static String TEST_OUTPUTDIRECTORY = "project.build.testOutputDirectory";
96 public final static String SKIPPED = "hibernate.schema.skipped";
98 private final static Pattern SPLIT = Pattern.compile("[^,\\s]+");
100 private final Set<String> packages = new HashSet<String>();
106 * Only needed internally.
108 * @parameter property="project"
112 private MavenProject project;
117 * Only needed internally.
119 * @parameter property="project.build.directory"
123 private String buildDirectory;
126 /** Parameters to configure the genaration of the SQL *********************/
129 * Export the database-schma to the database.
130 * If set to <code>false</code>, only the SQL-script is created and the
131 * database is not touched.
133 * <strong>Important:</strong>
134 * This configuration value can only be configured through the
135 * <code>pom.xml</code>, or by the definition of a system-property, because
136 * it is not known by Hibernate nor JPA and, hence, not picked up from
137 * their configuration!
139 * @parameter property="hibernate.schema.export" default-value="true"
142 private Boolean export;
147 * If set to <code>true</code>, the execution is skipped.
149 * A skipped execution is signaled via the maven-property
150 * <code>${hibernate.schema.skipped}</code>.
152 * The execution is skipped automatically, if no modified or newly added
153 * annotated classes are found and the dialect was not changed.
155 * <strong>Important:</strong>
156 * This configuration value can only be configured through the
157 * <code>pom.xml</code>, or by the definition of a system-property, because
158 * it is not known by Hibernate nor JPA and, hence, not picked up from
159 * their configuration!
161 * @parameter property="hibernate.schema.skip" default-value="${maven.test.skip}"
164 private boolean skip;
169 * Force execution, even if no modified or newly added annotated classes
170 * where found and the dialect was not changed.
172 * <code>skip</code> takes precedence over <code>force</code>.
174 * <strong>Important:</strong>
175 * This configuration value can only be configured through the
176 * <code>pom.xml</code>, or by the definition of a system-property, because
177 * it is not known by Hibernate nor JPA and, hence, not picked up from
178 * their configuration!
180 * @parameter property="hibernate.schema.force" default-value="false"
183 private boolean force;
188 * @parameter property="hibernate.dialect"
191 private String dialect;
194 * Delimiter in output-file.
196 * <strong>Important:</strong>
197 * This configuration value can only be configured through the
198 * <code>pom.xml</code>, or by the definition of a system-property, because
199 * it is not known by Hibernate nor JPA and, hence, not picked up from
200 * their configuration!
202 * @parameter property="hibernate.hbm2ddl.delimiter" default-value=";"
205 private String delimiter;
208 * Show the generated SQL in the command-line output.
210 * @parameter property="hibernate.show_sql"
213 private Boolean show;
216 * Format output-file.
218 * @parameter property="hibernate.format_sql"
221 private Boolean format;
224 * Specifies whether to automatically create also the database schema/catalog.
226 * @parameter property="hibernate.hbm2dll.create_namespaces" default-value="false"
229 private Boolean createNamespaces;
232 * Implicit naming strategy
234 * @parameter property="hibernate.implicit_naming_strategy"
237 private String implicitNamingStrategy;
240 * Physical naming strategy
242 * @parameter property="hibernate.physical_naming_strategy"
245 private String physicalNamingStrategy;
248 * Wether the project should be scanned for annotated-classes, or not
250 * This parameter is intended to allow overwriting of the parameter
251 * <code>exclude-unlisted-classes</code> of a <code>persistence-unit</code>.
252 * If not specified, it defaults to <code>true</code>
254 * @parameter property="hibernate.schema.scan.classes"
257 private Boolean scanClasses;
260 * Classes-Directory to scan.
262 * This parameter defaults to the maven build-output-directory for classes.
263 * Additionally, all dependencies are scanned for annotated classes.
265 * <strong>Important:</strong>
266 * This configuration value can only be configured through the
267 * <code>pom.xml</code>, or by the definition of a system-property, because
268 * it is not known by Hibernate nor JPA and, hence, not picked up from
269 * their configuration!
271 * @parameter property="project.build.outputDirectory"
274 private String outputDirectory;
277 * Dependency-Scopes, that should be scanned for annotated classes.
279 * By default, only dependencies in the scope <code>compile</code> are
280 * scanned for annotated classes. Multiple scopes can be seperated by
281 * white space or commas.
283 * If you do not want any dependencies to be scanned for annotated
284 * classes, set this parameter to <code>none</code>.
286 * The plugin does not scan for annotated classes in transitive
287 * dependencies. If some of your annotated classes are hidden in a
288 * transitive dependency, you can simply add that dependency explicitly.
290 * @parameter property="hibernate.schema.scan.dependencies" default-value="compile"
293 private String scanDependencies;
296 * Whether to scan the test-branch of the project for annotated classes, or
299 * If this parameter is set to <code>true</code> the test-classes of the
300 * artifact will be scanned for hibernate-annotated classes additionally.
302 * <strong>Important:</strong>
303 * This configuration value can only be configured through the
304 * <code>pom.xml</code>, or by the definition of a system-property, because
305 * it is not known by Hibernate nor JPA and, hence, not picked up from
306 * their configuration!
308 * @parameter property="hibernate.schema.scan.test_classes" default-value="false"
311 private Boolean scanTestClasses;
314 * Test-Classes-Directory to scan.
316 * This parameter defaults to the maven build-output-directory for
319 * This parameter is only used, when <code>scanTestClasses</code> is set
320 * to <code>true</code>!
322 * <strong>Important:</strong>
323 * This configuration value can only be configured through the
324 * <code>pom.xml</code>, or by the definition of a system-property, because
325 * it is not known by Hibernate nor JPA and, hence, not picked up from
326 * their configuration!
328 * @parameter property="project.build.testOutputDirectory"
331 private String testOutputDirectory;
334 /** Conection parameters *************************************************/
339 * @parameter property="hibernate.connection.driver_class"
342 private String driver;
347 * @parameter property="hibernate.connection.url"
355 * @parameter property="hibernate.connection.username"
358 private String username;
363 * @parameter property="hibernate.connection.password"
366 private String password;
369 /** Parameters to locate configuration sources ****************************/
372 * Path to a file or name of a ressource with hibernate properties.
373 * If this parameter is specified, the plugin will try to load configuration
374 * values from a file with the given path or a ressource on the classpath with
375 * the given name. If both fails, the execution of the plugin will fail.
377 * If this parameter is not set the plugin will load configuration values
378 * from a ressource named <code>hibernate.properties</code> on the classpath,
379 * if it is present, but will not fail if there is no such ressource.
381 * During ressource-lookup, the test-classpath takes precedence.
386 private String hibernateProperties;
389 * Path to Hibernate configuration file (.cfg.xml).
390 * If this parameter is specified, the plugin will try to load configuration
391 * values from a file with the given path or a ressource on the classpath with
392 * the given name. If both fails, the execution of the plugin will fail.
394 * If this parameter is not set the plugin will load configuration values
395 * from a ressource named <code>hibernate.cfg.xml</code> on the classpath,
396 * if it is present, but will not fail if there is no such ressource.
398 * During ressource-lookup, the test-classpath takes precedence.
400 * Settings in this file will overwrite settings in the properties file.
405 private String hibernateConfig;
408 * Name of the persistence-unit.
409 * If this parameter is specified, the plugin will try to load configuration
410 * values from a persistence-unit with the specified name. If no such
411 * persistence-unit can be found, the plugin will throw an exception.
413 * If this parameter is not set and there is only one persistence-unit
414 * available, that unit will be used automatically. But if this parameter is
415 * not set and there are multiple persistence-units available on,
416 * the class-path, the execution of the plugin will fail.
418 * Settings in this file will overwrite settings in the properties or the
419 * configuration file.
424 private String persistenceUnit;
427 * List of Hibernate-Mapping-Files (XML).
428 * Multiple files can be separated with white-spaces and/or commas.
430 * @parameter property="hibernate.mapping"
433 private String mappings;
437 public final void execute(String filename)
439 MojoFailureException,
440 MojoExecutionException
444 getLog().info("Execution of hibernate-maven-plugin was skipped!");
445 project.getProperties().setProperty(SKIPPED, "true");
449 ModificationTracker tracker;
452 tracker = new ModificationTracker(buildDirectory, filename, getLog());
454 catch (NoSuchAlgorithmException e)
456 throw new MojoFailureException("Digest-Algorithm MD5 is missing!", e);
459 final SimpleConnectionProvider connectionProvider =
460 new SimpleConnectionProvider(getLog());
464 /** Start extended logging */
465 MavenLogAppender.startPluginLog(this);
467 /** Load checksums for old mapping and configuration */
470 /** Create the ClassLoader */
471 MutableClassLoader classLoader = createClassLoader();
473 /** Create a BootstrapServiceRegistry with the created ClassLoader */
474 BootstrapServiceRegistry bootstrapServiceRegitry =
475 new BootstrapServiceRegistryBuilder()
476 .applyClassLoader(classLoader)
478 ClassLoaderService classLoaderService =
479 bootstrapServiceRegitry.getService(ClassLoaderService.class);
481 Properties properties = new Properties();
482 ConfigLoader configLoader = new ConfigLoader(bootstrapServiceRegitry);
484 /** Loading and merging configuration */
485 properties.putAll(loadProperties(configLoader));
486 LoadedConfig config = loadConfig(configLoader);
488 properties.putAll(config.getConfigurationValues());
489 ParsedPersistenceXmlDescriptor unit =
490 loadPersistenceUnit(classLoaderService, properties);
492 properties.putAll(unit.getProperties());
494 /** Overwriting/Completing configuration */
495 configure(properties, tracker);
497 /** Check configuration for modifications */
498 if(tracker.track(properties))
499 getLog().debug("Configuration has changed.");
501 getLog().debug("Configuration unchanged.");
503 /** Check, that the outputfile is writable */
504 final File output = getOutputFile(filename);
506 /** Configure Hibernate */
507 final StandardServiceRegistry serviceRegistry =
508 new StandardServiceRegistryBuilder(bootstrapServiceRegitry)
509 .applySettings(properties)
510 .addService(ConnectionProvider.class, connectionProvider)
512 final MetadataSources sources = new MetadataSources(serviceRegistry);
514 /** Add the remaining class-path-elements */
515 completeClassPath(classLoader);
517 /** Apply mappings from hibernate-configuration, if present */
520 for (MappingReference mapping : config.getMappingReferences())
521 mapping.apply(sources);
527 /** No persistent unit: default behaviour */
528 if (scanClasses == null)
530 Set<URL> urls = new HashSet<URL>();
532 addRoot(urls, outputDirectory);
534 addRoot(urls, testOutputDirectory);
535 addDependencies(urls);
536 classes = scanUrls(urls);
540 /** Follow configuration in persisten unit */
541 if (scanClasses == null)
542 scanClasses = !unit.isExcludeUnlistedClasses();
543 Set<URL> urls = new HashSet<URL>();
547 * Scan the root of the persiten unit and configured jars for
550 urls.add(unit.getPersistenceUnitRootUrl());
551 for (URL url : unit.getJarFileUrls())
555 addRoot(urls, testOutputDirectory);
556 classes = scanUrls(urls);
557 for (String className : unit.getManagedClassNames())
558 classes.add(className);
560 * Add mappings from the default mapping-file
561 * <code>META-INF/orm.xml</code>, if present
563 boolean error = false;
565 is = classLoader.getResourceAsStream("META-INF/orm.xml");
568 getLog().info("Adding default JPA-XML-mapping from META-INF/orm.xml");
571 tracker.track("META-INF/orm.xml", is);
572 sources.addResource("META-INF/orm.xml");
574 catch (IOException e)
576 getLog().error("cannot read META-INF/orm.xml: " + e);
582 getLog().debug("no META-INF/orm.xml found");
585 * Add mappings from files, that are explicitly configured in the
588 for (String mapping : unit.getMappingFileNames())
590 getLog().info("Adding explicitly configured mapping from " + mapping);
591 is = classLoader.getResourceAsStream(mapping);
596 tracker.track(mapping, is);
597 sources.addResource(mapping);
599 catch (IOException e)
601 getLog().info("cannot read mapping-file " + mapping + ": " + e);
607 getLog().error("cannot find mapping-file " + mapping);
612 throw new MojoFailureException(
613 "error, while reading mappings configured in persistence-unit \"" +
619 /** Add the configured/collected annotated classes */
620 for (String className : classes)
621 addAnnotated(className, sources, classLoaderService, tracker);
623 /** Add explicitly configured classes */
624 addMappings(sources, tracker);
626 /** Skip execution, if mapping and configuration is unchanged */
627 if (!tracker.modified())
630 "Mapping and configuration unchanged."
633 getLog().info("Schema generation is forced!");
636 getLog().info("Skipping schema generation!");
637 project.getProperties().setProperty(SKIPPED, "true");
643 /** Create a connection, if sufficient configuration infromation is available */
644 connectionProvider.open(classLoaderService, properties);
646 MetadataBuilder metadataBuilder = sources.getMetadataBuilder();
648 StrategySelector strategySelector =
649 serviceRegistry.getService(StrategySelector.class);
651 if (properties.containsKey(IMPLICIT_NAMING_STRATEGY))
653 metadataBuilder.applyImplicitNamingStrategy(
654 strategySelector.resolveStrategy(
655 ImplicitNamingStrategy.class,
656 properties.getProperty(IMPLICIT_NAMING_STRATEGY)
661 if (properties.containsKey(PHYSICAL_NAMING_STRATEGY))
663 metadataBuilder.applyPhysicalNamingStrategy(
664 strategySelector.resolveStrategy(
665 PhysicalNamingStrategy.class,
666 properties.getProperty(PHYSICAL_NAMING_STRATEGY)
671 /** Prepare the generation of the SQL */
672 Map settings = new HashMap();
675 .getService(ConfigurationService.class)
678 ExceptionHandlerCollectingImpl handler =
679 new ExceptionHandlerCollectingImpl();
680 ExecutionOptions options =
681 SchemaManagementToolCoordinator
682 .buildExecutionOptions(settings, handler);
683 final EnumSet<TargetType> targetTypes = EnumSet.of(TargetType.SCRIPT);
685 targetTypes.add(TargetType.DATABASE);
686 TargetDescriptor target = new TargetDescriptor()
689 public EnumSet<TargetType> getTargetTypes()
695 public ScriptTargetOutput getScriptTargetOutput()
700 .getService(ConfigurationService.class)
702 .get(AvailableSettings.HBM2DDL_CHARSET_NAME);
703 return new ScriptTargetOutputToFile(output, charset);
708 * Change class-loader of current thread.
709 * This is necessary, because still not all parts of Hibernate 5 use
710 * the newly introduced ClassLoaderService and will fail otherwise!
712 Thread thread = Thread.currentThread();
713 ClassLoader contextClassLoader = thread.getContextClassLoader();
716 thread.setContextClassLoader(classLoader);
717 build((MetadataImplementor)metadataBuilder.build(), options, target);
721 thread.setContextClassLoader(contextClassLoader);
722 for (Exception e : handler.getExceptions())
723 getLog().error(e.getMessage());
726 catch (MojoExecutionException e)
731 catch (MojoFailureException e)
736 catch (RuntimeException e)
743 /** Remember mappings and configuration */
746 /** Close the connection - if one was opened */
747 connectionProvider.close();
749 /** Stop Log-Capturing */
750 MavenLogAppender.endPluginLog(this);
756 MetadataImplementor metadata,
757 ExecutionOptions options,
758 TargetDescriptor target
761 MojoFailureException,
762 MojoExecutionException;
765 private MutableClassLoader createClassLoader() throws MojoExecutionException
769 getLog().debug("Creating ClassLoader for project-dependencies...");
770 LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
773 file = new File(testOutputDirectory);
776 getLog().info("Creating test-output-directory: " + testOutputDirectory);
779 urls.add(file.toURI().toURL());
781 file = new File(outputDirectory);
784 getLog().info("Creating output-directory: " + outputDirectory);
787 urls.add(file.toURI().toURL());
789 return new MutableClassLoader(urls, getLog());
793 getLog().error("Error while creating ClassLoader!", e);
794 throw new MojoExecutionException(e.getMessage());
798 private void completeClassPath(MutableClassLoader classLoader)
800 MojoExecutionException
804 getLog().debug("Completing class-paths of the ClassLoader for project-dependencies...");
805 List<String> classpathFiles = project.getCompileClasspathElements();
807 classpathFiles.addAll(project.getTestClasspathElements());
808 LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
809 for (String pathElement : classpathFiles)
811 getLog().debug("Dependency: " + pathElement);
812 urls.add(new File(pathElement).toURI().toURL());
814 classLoader.add(urls);
818 getLog().error("Error while creating ClassLoader!", e);
819 throw new MojoExecutionException(e.getMessage());
823 private Map loadProperties(ConfigLoader configLoader)
825 MojoExecutionException
827 /** Try to read configuration from properties-file */
828 if (hibernateProperties == null)
832 return configLoader.loadProperties("hibernate.properties");
834 catch (ConfigurationException e)
836 getLog().debug(e.getMessage());
837 return Collections.EMPTY_MAP;
844 File file = new File(hibernateProperties);
847 getLog().info("Reading settings from file " + hibernateProperties + "...");
848 return configLoader.loadProperties(file);
851 return configLoader.loadProperties(hibernateProperties);
853 catch (ConfigurationException e)
855 getLog().error("Error while reading properties!", e);
856 throw new MojoExecutionException(e.getMessage());
861 private LoadedConfig loadConfig(ConfigLoader configLoader)
862 throws MojoExecutionException
864 /** Try to read configuration from configuration-file */
865 if (hibernateConfig == null)
869 return configLoader.loadConfigXmlResource("hibernate.cfg.xml");
871 catch (ConfigurationException e)
873 getLog().debug(e.getMessage());
881 File file = new File(hibernateConfig);
884 getLog().info("Reading configuration from file " + hibernateConfig + "...");
885 return configLoader.loadConfigXmlFile(file);
889 return configLoader.loadConfigXmlResource(hibernateConfig);
892 catch (ConfigurationException e)
894 getLog().error("Error while reading configuration!", e);
895 throw new MojoExecutionException(e.getMessage());
900 private void configure(Properties properties, ModificationTracker tracker)
901 throws MojoFailureException
904 * Special treatment for the configuration-value "export": if it is
905 * switched to "true", the genearation fo the schema should be forced!
907 if (tracker.check(EXPORT, export.toString()) && export)
911 * Configure the generation of the SQL.
912 * Overwrite values from properties-file if the configuration parameter is
913 * known to Hibernate.
915 configure(properties, dialect, DIALECT);
916 configure(properties, delimiter, HBM2DDL_DELIMITER);
917 configure(properties, format, FORMAT_SQL);
918 configure(properties, createNamespaces, HBM2DLL_CREATE_NAMESPACES);
919 configure(properties, implicitNamingStrategy, IMPLICIT_NAMING_STRATEGY);
920 configure(properties, physicalNamingStrategy, PHYSICAL_NAMING_STRATEGY);
921 tracker.track(OUTPUTDIRECTORY, outputDirectory); // << not reflected in hibernate configuration!
922 tracker.track(SCAN_DEPENDENCIES, scanDependencies); // << not reflected in hibernate configuration!
923 tracker.track(SCAN_TESTCLASSES, scanTestClasses.toString()); // << not reflected in hibernate configuration!
924 tracker.track(TEST_OUTPUTDIRECTORY, testOutputDirectory); // << not reflected in hibernate configuration!
927 * Special treatment for the configuration-value "show": a change of its
928 * configured value should not lead to a regeneration of the database
932 show = Boolean.valueOf(properties.getProperty(SHOW_SQL));
934 properties.setProperty(SHOW_SQL, show.toString());
937 * Configure the connection parameters.
938 * Overwrite values from properties-file.
940 configure(properties, driver, DRIVER, JPA_JDBC_DRIVER);
941 configure(properties, url, URL, JPA_JDBC_URL);
942 configure(properties, username, USER, JPA_JDBC_USER);
943 configure(properties, password, PASS, JPA_JDBC_PASSWORD);
945 if (properties.isEmpty())
947 getLog().error("No properties set!");
948 throw new MojoFailureException("Hibernate configuration is missing!");
951 getLog().info("Gathered hibernate-configuration (turn on debugging for details):");
952 for (Entry<Object,Object> entry : properties.entrySet())
953 getLog().info(" " + entry.getKey() + " = " + entry.getValue());
956 private void configure(
957 Properties properties,
960 String alternativeKey
963 value = configure(properties, value, key);
967 if (properties.containsKey(alternativeKey))
970 "Ignoring property " + alternativeKey + "=" +
971 properties.getProperty(alternativeKey) + " in favour for property " +
972 key + "=" + properties.getProperty(key)
974 properties.remove(alternativeKey);
978 private String configure(Properties properties, String value, String key)
982 if (properties.containsKey(key))
984 "Overwriting property " + key + "=" + properties.getProperty(key) +
985 " with the value " + value
988 getLog().debug("Using the value " + value + " for property " + key);
989 properties.setProperty(key, value);
991 return properties.getProperty(key);
994 private void configure(Properties properties, Boolean value, String key)
998 if (properties.containsKey(key))
1000 "Overwriting property " + key + "=" + properties.getProperty(key) +
1001 " with the value " + value
1004 getLog().debug("Using the value " + value + " for property " + key);
1005 properties.setProperty(key, value.toString());
1009 private File getOutputFile(String filename)
1011 MojoExecutionException
1013 File output = new File(filename);
1015 if (!output.isAbsolute())
1017 // Interpret relative file path relative to build directory
1018 output = new File(buildDirectory, filename);
1020 getLog().debug("Output file: " + output.getPath());
1022 // Ensure that directory path for specified file exists
1023 File outFileParentDir = output.getParentFile();
1024 if (null != outFileParentDir && !outFileParentDir.exists())
1029 "Creating directory path for output file:" +
1030 outFileParentDir.getPath()
1032 outFileParentDir.mkdirs();
1037 "Error creating directory path for output file: " + e.getMessage();
1038 getLog().error(error);
1039 throw new MojoExecutionException(error);
1045 output.createNewFile();
1047 catch (IOException e)
1049 String error = "Error creating output file: " + e.getMessage();
1050 getLog().error(error);
1051 throw new MojoExecutionException(error);
1054 if (!output.canWrite())
1057 "Output file " + output.getAbsolutePath() + " is not writable!";
1058 getLog().error(error);
1059 throw new MojoExecutionException(error);
1065 private void addMappings(MetadataSources sources, ModificationTracker tracker)
1066 throws MojoFailureException
1068 getLog().debug("Adding explicitly configured mappings...");
1069 if (mappings != null)
1073 for (String filename : mappings.split("[\\s,]+"))
1075 // First try the filename as absolute/relative path
1076 File file = new File(filename);
1079 // If the file was not found, search for it in the resource-directories
1080 for (Resource resource : project.getResources())
1082 file = new File(resource.getDirectory() + File.separator + filename);
1089 if (file.isDirectory())
1090 // TODO: add support to read all mappings under a directory
1091 throw new MojoFailureException(file.getAbsolutePath() + " is a directory");
1092 if (tracker.track(filename, new FileInputStream(file)))
1093 getLog().debug("Found new or modified mapping-file: " + filename);
1095 getLog().debug("Mapping-file unchanged: " + filename);
1097 sources.addFile(file);
1100 throw new MojoFailureException("File " + filename + " could not be found in any of the configured resource-directories!");
1103 catch (IOException e)
1105 throw new MojoFailureException("Cannot calculate MD5 sums!", e);
1110 private void addRoot(Set<URL> urls, String path) throws MojoFailureException
1114 File dir = new File(outputDirectory);
1117 getLog().info("Adding " + dir.getAbsolutePath() + " to the list of roots to scan...");
1118 urls.add(dir.toURI().toURL());
1121 catch (MalformedURLException e)
1123 getLog().error("error while adding the project-root to the list of roots to scan!", e);
1124 throw new MojoFailureException(e.getMessage());
1128 private void addDependencies(Set<URL> urls) throws MojoFailureException
1132 if (scanDependencies != null)
1134 Matcher matcher = SPLIT.matcher(scanDependencies);
1135 while (matcher.find())
1137 getLog().info("Adding dependencies from scope " + matcher.group() + " to the list of roots to scan");
1138 for (Artifact artifact : project.getDependencyArtifacts())
1140 if (!artifact.getScope().equalsIgnoreCase(matcher.group()))
1142 if (artifact.getFile() == null)
1144 getLog().warn("Cannot add dependency " + artifact.getId() + ": no JAR-file available!");
1147 getLog().info("Adding dependencies from scope " + artifact.getId() + " to the list of roots to scan");
1148 urls.add(artifact.getFile().toURI().toURL());
1153 catch (MalformedURLException e)
1155 getLog().error("Error while adding dependencies to the list of roots to scan!", e);
1156 throw new MojoFailureException(e.getMessage());
1160 private Set<String> scanUrls(Set<URL> scanRoots)
1162 MojoFailureException
1166 AnnotationDB db = new AnnotationDB();
1167 for (URL root : scanRoots)
1168 db.scanArchives(root);
1170 Set<String> classes = new HashSet<String>();
1171 if (db.getAnnotationIndex().containsKey(Entity.class.getName()))
1172 classes.addAll(db.getAnnotationIndex().get(Entity.class.getName()));
1173 if (db.getAnnotationIndex().containsKey(MappedSuperclass.class.getName()))
1174 classes.addAll(db.getAnnotationIndex().get(MappedSuperclass.class.getName()));
1175 if (db.getAnnotationIndex().containsKey(Embeddable.class.getName()))
1176 classes.addAll(db.getAnnotationIndex().get(Embeddable.class.getName()));
1182 getLog().error("Error while scanning!", e);
1183 throw new MojoFailureException(e.getMessage());
1187 private void addAnnotated(
1189 MetadataSources sources,
1190 ClassLoaderService classLoaderService,
1191 ModificationTracker tracker
1194 MojoFailureException,
1195 MojoExecutionException
1199 getLog().info("Adding annotated resource: " + name);
1200 String packageName = null;
1202 boolean error = false;
1205 Class<?> annotatedClass = classLoaderService.classForName(name);
1206 String resourceName = annotatedClass.getName();
1208 resourceName.substring(
1209 resourceName.lastIndexOf(".") + 1,
1210 resourceName.length()
1212 InputStream is = annotatedClass.getResourceAsStream(resourceName);
1215 if (tracker.track(name, is))
1216 getLog().debug("New or modified class: " + name);
1218 getLog().debug("Unchanged class: " + name);
1219 sources.addAnnotatedClass(annotatedClass);
1220 packageName = annotatedClass.getPackage().getName();
1224 getLog().error("cannot find ressource " + resourceName + " for class " + name);
1228 catch(ClassLoadingException e)
1234 throw new MojoExecutionException("error while inspecting annotated class " + name);
1237 while (packageName != null)
1239 if (packages.contains(packageName))
1241 String resource = packageName.replace('.', '/') + "/package-info.class";
1242 InputStream is = classLoaderService.locateResourceStream(resource);
1245 // No compiled package-info available: no package-level annotations!
1246 getLog().debug("Package " + packageName + " is not annotated.");
1250 if (tracker.track(packageName, is))
1251 getLog().debug("New or modified package: " + packageName);
1253 getLog().debug("Unchanged package: " + packageName);
1254 getLog().info("Adding annotations from package " + packageName);
1255 sources.addPackage(packageName);
1257 packages.add(packageName);
1258 int i = packageName.lastIndexOf('.');
1262 packageName = packageName.substring(0,i);
1267 getLog().error("Error while adding the annotated class " + name, e);
1268 throw new MojoFailureException(e.getMessage());
1272 private ParsedPersistenceXmlDescriptor loadPersistenceUnit(
1273 ClassLoaderService classLoaderService,
1274 Properties properties
1277 MojoFailureException
1279 PersistenceXmlParser parser =
1280 new PersistenceXmlParser(
1282 PersistenceUnitTransactionType.RESOURCE_LOCAL
1285 Map<String, ParsedPersistenceXmlDescriptor> units =
1286 parser.doResolve(properties);
1288 if (persistenceUnit == null)
1290 Iterator<String> names = units.keySet().iterator();
1291 if (!names.hasNext())
1293 getLog().info("Found no META-INF/persistence.xml.");
1297 String name = names.next();
1298 if (!names.hasNext())
1300 getLog().info("Using persistence-unit " + name);
1301 return units.get(name);
1304 StringBuilder builder = new StringBuilder();
1305 builder.append("No name provided and multiple persistence units found: ");
1306 builder.append(name);
1307 while(names.hasNext())
1309 builder.append(", ");
1310 builder.append(names.next());
1312 builder.append('.');
1313 throw new MojoFailureException(builder.toString());
1316 if (units.containsKey(persistenceUnit))
1318 getLog().info("Using configured persistence-unit " + persistenceUnit);
1319 return units.get(persistenceUnit);
1322 throw new MojoFailureException("Could not find persistence-unit " + persistenceUnit);