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.util.Collections;
12 import java.util.HashSet;
13 import java.util.Iterator;
14 import java.util.LinkedHashSet;
15 import java.util.List;
17 import java.util.Map.Entry;
18 import java.util.Properties;
20 import java.util.regex.Matcher;
21 import java.util.regex.Pattern;
22 import javax.persistence.Embeddable;
23 import javax.persistence.Entity;
24 import javax.persistence.MappedSuperclass;
25 import javax.persistence.spi.PersistenceUnitTransactionType;
26 import org.apache.maven.artifact.Artifact;
27 import org.apache.maven.model.Resource;
28 import org.apache.maven.plugin.AbstractMojo;
29 import org.apache.maven.plugin.MojoExecutionException;
30 import org.apache.maven.plugin.MojoFailureException;
31 import org.apache.maven.project.MavenProject;
32 import org.hibernate.boot.MetadataBuilder;
33 import org.hibernate.boot.MetadataSources;
34 import org.hibernate.boot.cfgxml.internal.ConfigLoader;
35 import org.hibernate.boot.cfgxml.spi.LoadedConfig;
36 import org.hibernate.boot.cfgxml.spi.MappingReference;
37 import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
38 import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
39 import org.hibernate.boot.registry.BootstrapServiceRegistry;
40 import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
41 import org.hibernate.boot.registry.StandardServiceRegistry;
42 import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
43 import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
44 import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
45 import org.hibernate.boot.registry.selector.spi.StrategySelector;
46 import org.hibernate.boot.spi.MetadataImplementor;
47 import static org.hibernate.cfg.AvailableSettings.DIALECT;
48 import static org.hibernate.cfg.AvailableSettings.DRIVER;
49 import static org.hibernate.cfg.AvailableSettings.FORMAT_SQL;
50 import static org.hibernate.cfg.AvailableSettings.HBM2DDL_DELIMITER;
51 import static org.hibernate.cfg.AvailableSettings.HBM2DLL_CREATE_NAMESPACES;
52 import static org.hibernate.cfg.AvailableSettings.IMPLICIT_NAMING_STRATEGY;
53 import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_DRIVER;
54 import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_PASSWORD;
55 import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_URL;
56 import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_USER;
57 import static org.hibernate.cfg.AvailableSettings.PASS;
58 import static org.hibernate.cfg.AvailableSettings.PHYSICAL_NAMING_STRATEGY;
59 import static org.hibernate.cfg.AvailableSettings.SHOW_SQL;
60 import static org.hibernate.cfg.AvailableSettings.USER;
61 import static org.hibernate.cfg.AvailableSettings.URL;
62 import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
63 import org.hibernate.internal.util.config.ConfigurationException;
64 import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor;
65 import org.hibernate.jpa.boot.internal.PersistenceXmlParser;
66 import org.hibernate.tool.schema.internal.ExceptionHandlerCollectingImpl;
67 import org.scannotation.AnnotationDB;
71 * Baseclass with common attributes and methods.
73 * @phase process-classes
75 * @requiresDependencyResolution runtime
77 public abstract class AbstractSchemaMojo extends AbstractMojo
79 public final static String EXECUTE = "hibernate.schema.execute";
80 public final static String OUTPUTDIRECTORY = "project.build.outputDirectory";
81 public final static String SCAN_CLASSES = "hibernate.schema.scan.classes";
82 public final static String SCAN_DEPENDENCIES = "hibernate.schema.scan.dependencies";
83 public final static String SCAN_TESTCLASSES = "hibernate.schema.scan.test_classes";
84 public final static String TEST_OUTPUTDIRECTORY = "project.build.testOutputDirectory";
85 public final static String SKIPPED = "hibernate.schema.skipped";
86 public final static String SCRIPT = "hibernate.schema.script";
88 private final static Pattern SPLIT = Pattern.compile("[^,\\s]+");
90 private final Set<String> packages = new HashSet<String>();
96 * Only needed internally.
98 * @parameter property="project"
102 private MavenProject project;
107 * Only needed internally.
109 * @parameter property="project.build.directory"
113 String buildDirectory;
116 /** Parameters to configure the genaration of the SQL *********************/
119 * Excecute the generated SQL.
120 * If set to <code>false</code>, only the SQL-script is created and the
121 * database is not touched.
123 * <strong>Important:</strong>
124 * This configuration value can only be configured through the
125 * <code>pom.xml</code>, or by the definition of a system-property, because
126 * it is not known by Hibernate nor JPA and, hence, not picked up from
127 * their configuration!
129 * @parameter property="hibernate.schema.execute" default-value="true"
132 private Boolean execute;
137 * If set to <code>true</code>, the execution is skipped.
139 * A skipped execution is signaled via the maven-property
140 * <code>${hibernate.schema.skipped}</code>.
142 * The execution is skipped automatically, if no modified or newly added
143 * annotated classes are found and the dialect was not changed.
145 * <strong>Important:</strong>
146 * This configuration value can only be configured through the
147 * <code>pom.xml</code>, or by the definition of a system-property, because
148 * it is not known by Hibernate nor JPA and, hence, not picked up from
149 * their configuration!
151 * @parameter property="hibernate.schema.skip" default-value="${maven.test.skip}"
154 private boolean skip;
157 * Force generation/execution
159 * Force the generation and (if configured) the execution of the SQL, even if
160 * no modified or newly added annotated classes where found and the
161 * configuration was not changed.
163 * <code>skip</code> takes precedence over <code>force</code>.
165 * <strong>Important:</strong>
166 * This configuration value can only be configured through the
167 * <code>pom.xml</code>, or by the definition of a system-property, because
168 * it is not known by Hibernate nor JPA and, hence, not picked up from
169 * their configuration!
171 * @parameter property="hibernate.schema.force" default-value="false"
174 private boolean force;
179 * @parameter property="hibernate.dialect"
182 private String dialect;
185 * Delimiter in output-file.
187 * <strong>Important:</strong>
188 * This configuration value can only be configured through the
189 * <code>pom.xml</code>, or by the definition of a system-property, because
190 * it is not known by Hibernate nor JPA and, hence, not picked up from
191 * their configuration!
193 * @parameter property="hibernate.hbm2ddl.delimiter" default-value=";"
196 private String delimiter;
199 * Show the generated SQL in the command-line output.
201 * @parameter property="hibernate.show_sql"
204 private Boolean show;
207 * Format output-file.
209 * @parameter property="hibernate.format_sql"
212 private Boolean format;
215 * Specifies whether to automatically create also the database schema/catalog.
217 * @parameter property="hibernate.hbm2dll.create_namespaces" default-value="false"
220 private Boolean createNamespaces;
223 * Implicit naming strategy
225 * @parameter property="hibernate.implicit_naming_strategy"
228 private String implicitNamingStrategy;
231 * Physical naming strategy
233 * @parameter property="hibernate.physical_naming_strategy"
236 private String physicalNamingStrategy;
239 * Wether the project should be scanned for annotated-classes, or not
241 * This parameter is intended to allow overwriting of the parameter
242 * <code>exclude-unlisted-classes</code> of a <code>persistence-unit</code>.
243 * If not specified, it defaults to <code>true</code>
245 * @parameter property="hibernate.schema.scan.classes"
248 private Boolean scanClasses;
251 * Classes-Directory to scan.
253 * This parameter defaults to the maven build-output-directory for classes.
254 * Additionally, all dependencies are scanned for annotated classes.
256 * <strong>Important:</strong>
257 * This configuration value can only be configured through the
258 * <code>pom.xml</code>, or by the definition of a system-property, because
259 * it is not known by Hibernate nor JPA and, hence, not picked up from
260 * their configuration!
262 * @parameter property="project.build.outputDirectory"
265 private String outputDirectory;
268 * Dependency-Scopes, that should be scanned for annotated classes.
270 * By default, only dependencies in the scope <code>compile</code> are
271 * scanned for annotated classes. Multiple scopes can be seperated by
272 * white space or commas.
274 * If you do not want any dependencies to be scanned for annotated
275 * classes, set this parameter to <code>none</code>.
277 * The plugin does not scan for annotated classes in transitive
278 * dependencies. If some of your annotated classes are hidden in a
279 * transitive dependency, you can simply add that dependency explicitly.
281 * @parameter property="hibernate.schema.scan.dependencies" default-value="compile"
284 private String scanDependencies;
287 * Whether to scan the test-branch of the project for annotated classes, or
290 * If this parameter is set to <code>true</code> the test-classes of the
291 * artifact will be scanned for hibernate-annotated classes additionally.
293 * <strong>Important:</strong>
294 * This configuration value can only be configured through the
295 * <code>pom.xml</code>, or by the definition of a system-property, because
296 * it is not known by Hibernate nor JPA and, hence, not picked up from
297 * their configuration!
299 * @parameter property="hibernate.schema.scan.test_classes" default-value="false"
302 private Boolean scanTestClasses;
305 * Test-Classes-Directory to scan.
307 * This parameter defaults to the maven build-output-directory for
310 * This parameter is only used, when <code>scanTestClasses</code> is set
311 * to <code>true</code>!
313 * <strong>Important:</strong>
314 * This configuration value can only be configured through the
315 * <code>pom.xml</code>, or by the definition of a system-property, because
316 * it is not known by Hibernate nor JPA and, hence, not picked up from
317 * their configuration!
319 * @parameter property="project.build.testOutputDirectory"
322 private String testOutputDirectory;
325 /** Conection parameters *************************************************/
330 * @parameter property="hibernate.connection.driver_class"
333 private String driver;
338 * @parameter property="hibernate.connection.url"
346 * @parameter property="hibernate.connection.username"
349 private String username;
354 * @parameter property="hibernate.connection.password"
357 private String password;
360 /** Parameters to locate configuration sources ****************************/
363 * Path to a file or name of a ressource with hibernate properties.
364 * If this parameter is specified, the plugin will try to load configuration
365 * values from a file with the given path or a ressource on the classpath with
366 * the given name. If both fails, the execution of the plugin will fail.
368 * If this parameter is not set the plugin will load configuration values
369 * from a ressource named <code>hibernate.properties</code> on the classpath,
370 * if it is present, but will not fail if there is no such ressource.
372 * During ressource-lookup, the test-classpath takes precedence.
377 private String hibernateProperties;
380 * Path to Hibernate configuration file (.cfg.xml).
381 * If this parameter is specified, the plugin will try to load configuration
382 * values from a file with the given path or a ressource on the classpath with
383 * the given name. If both fails, the execution of the plugin will fail.
385 * If this parameter is not set the plugin will load configuration values
386 * from a ressource named <code>hibernate.cfg.xml</code> on the classpath,
387 * if it is present, but will not fail if there is no such ressource.
389 * During ressource-lookup, the test-classpath takes precedence.
391 * Settings in this file will overwrite settings in the properties file.
396 private String hibernateConfig;
399 * Name of the persistence-unit.
400 * If this parameter is specified, the plugin will try to load configuration
401 * values from a persistence-unit with the specified name. If no such
402 * persistence-unit can be found, the plugin will throw an exception.
404 * If this parameter is not set and there is only one persistence-unit
405 * available, that unit will be used automatically. But if this parameter is
406 * not set and there are multiple persistence-units available on,
407 * the class-path, the execution of the plugin will fail.
409 * Settings in this file will overwrite settings in the properties or the
410 * configuration file.
415 private String persistenceUnit;
418 * List of Hibernate-Mapping-Files (XML).
419 * Multiple files can be separated with white-spaces and/or commas.
421 * @parameter property="hibernate.mapping"
424 private String mappings;
428 public final void execute(ModificationTracker tracker)
430 MojoFailureException,
431 MojoExecutionException
435 getLog().info("Execution of hibernate-maven-plugin was skipped!");
436 project.getProperties().setProperty(SKIPPED, "true");
440 final SimpleConnectionProvider connectionProvider =
441 new SimpleConnectionProvider(getLog());
445 /** Start extended logging */
446 MavenLogAppender.startPluginLog(this);
448 /** Load checksums for old mapping and configuration */
451 /** Create the ClassLoader */
452 MutableClassLoader classLoader = createClassLoader();
454 /** Create a BootstrapServiceRegistry with the created ClassLoader */
455 BootstrapServiceRegistry bootstrapServiceRegitry =
456 new BootstrapServiceRegistryBuilder()
457 .applyClassLoader(classLoader)
459 ClassLoaderService classLoaderService =
460 bootstrapServiceRegitry.getService(ClassLoaderService.class);
462 Properties properties = new Properties();
463 ConfigLoader configLoader = new ConfigLoader(bootstrapServiceRegitry);
465 /** Loading and merging configuration */
466 properties.putAll(loadProperties(configLoader));
467 LoadedConfig config = loadConfig(configLoader);
469 properties.putAll(config.getConfigurationValues());
470 ParsedPersistenceXmlDescriptor unit =
471 loadPersistenceUnit(classLoaderService, properties);
473 properties.putAll(unit.getProperties());
475 /** Overwriting/Completing configuration */
476 configure(properties, tracker);
478 /** Check configuration for modifications */
479 if(tracker.track(properties))
480 getLog().debug("Configuration has changed.");
482 getLog().debug("Configuration unchanged.");
484 /** Check, if the outputfile is missing or was changed */
489 catch (IOException e)
492 "Error while checking the generated script: " + e.getMessage();
493 getLog().error(error);
494 throw new MojoExecutionException(error);
497 /** Configure Hibernate */
498 final StandardServiceRegistry serviceRegistry =
499 new StandardServiceRegistryBuilder(bootstrapServiceRegitry)
500 .applySettings(properties)
501 .addService(ConnectionProvider.class, connectionProvider)
503 final MetadataSources sources = new MetadataSources(serviceRegistry);
505 /** Add the remaining class-path-elements */
506 completeClassPath(classLoader);
508 /** Apply mappings from hibernate-configuration, if present */
511 for (MappingReference mapping : config.getMappingReferences())
512 mapping.apply(sources);
518 /** No persistent unit: default behaviour */
519 if (scanClasses == null)
521 Set<URL> urls = new HashSet<URL>();
523 addRoot(urls, outputDirectory);
525 addRoot(urls, testOutputDirectory);
526 addDependencies(urls);
527 classes = scanUrls(urls);
531 /** Follow configuration in persisten unit */
532 if (scanClasses == null)
533 scanClasses = !unit.isExcludeUnlistedClasses();
534 Set<URL> urls = new HashSet<URL>();
538 * Scan the root of the persiten unit and configured jars for
541 urls.add(unit.getPersistenceUnitRootUrl());
542 for (URL url : unit.getJarFileUrls())
546 addRoot(urls, testOutputDirectory);
547 classes = scanUrls(urls);
548 for (String className : unit.getManagedClassNames())
549 classes.add(className);
551 * Add mappings from the default mapping-file
552 * <code>META-INF/orm.xml</code>, if present
554 boolean error = false;
556 is = classLoader.getResourceAsStream("META-INF/orm.xml");
559 getLog().info("Adding default JPA-XML-mapping from META-INF/orm.xml");
562 tracker.track("META-INF/orm.xml", is);
563 sources.addResource("META-INF/orm.xml");
565 catch (IOException e)
567 getLog().error("cannot read META-INF/orm.xml: " + e);
573 getLog().debug("no META-INF/orm.xml found");
576 * Add mappings from files, that are explicitly configured in the
579 for (String mapping : unit.getMappingFileNames())
581 getLog().info("Adding explicitly configured mapping from " + mapping);
582 is = classLoader.getResourceAsStream(mapping);
587 tracker.track(mapping, is);
588 sources.addResource(mapping);
590 catch (IOException e)
592 getLog().info("cannot read mapping-file " + mapping + ": " + e);
598 getLog().error("cannot find mapping-file " + mapping);
603 throw new MojoFailureException(
604 "error, while reading mappings configured in persistence-unit \"" +
610 /** Add the configured/collected annotated classes */
611 for (String className : classes)
612 addAnnotated(className, sources, classLoaderService, tracker);
614 /** Add explicitly configured classes */
615 addMappings(sources, tracker);
617 /** Skip execution, if mapping and configuration is unchanged */
618 if (!tracker.modified())
620 getLog().info("Mapping and configuration unchanged.");
622 getLog().info("Generation/execution is forced!");
625 getLog().info("Skipping schema generation!");
626 project.getProperties().setProperty(SKIPPED, "true");
632 /** Truncate output file */
637 catch (IOException e)
640 "Error while truncating output file: " // TODO: Filename unknown here!
642 getLog().warn(error);
643 throw new MojoExecutionException(error);
646 /** Create a connection, if sufficient configuration infromation is available */
647 connectionProvider.open(classLoaderService, properties);
649 MetadataBuilder metadataBuilder = sources.getMetadataBuilder();
651 StrategySelector strategySelector =
652 serviceRegistry.getService(StrategySelector.class);
654 if (properties.containsKey(IMPLICIT_NAMING_STRATEGY))
656 metadataBuilder.applyImplicitNamingStrategy(
657 strategySelector.resolveStrategy(
658 ImplicitNamingStrategy.class,
659 properties.getProperty(IMPLICIT_NAMING_STRATEGY)
664 if (properties.containsKey(PHYSICAL_NAMING_STRATEGY))
666 metadataBuilder.applyPhysicalNamingStrategy(
667 strategySelector.resolveStrategy(
668 PhysicalNamingStrategy.class,
669 properties.getProperty(PHYSICAL_NAMING_STRATEGY)
675 * Change class-loader of current thread.
676 * This is necessary, because still not all parts of Hibernate 5 use
677 * the newly introduced ClassLoaderService and will fail otherwise!
679 Thread thread = Thread.currentThread();
680 ClassLoader contextClassLoader = thread.getContextClassLoader();
683 thread.setContextClassLoader(classLoader);
684 ExceptionHandlerCollectingImpl handler =
685 build((MetadataImplementor)metadataBuilder.build());
686 if (handler.getExceptions().size() > 0)
688 StringBuilder builder = new StringBuilder();
689 builder.append("Hibernate failed:");
690 for (Exception e : handler.getExceptions())
692 builder.append("\n * ");
693 builder.append(e.getMessage());
695 String error = builder.toString();
696 getLog().error(error);
697 throw new MojoFailureException(error);
702 thread.setContextClassLoader(contextClassLoader);
703 /** Track, the content of the generated script */
708 catch (IOException e)
711 "Error while checking the generated script: " + e.getMessage();
712 getLog().error(error);
713 throw new MojoExecutionException(error);
717 catch (MojoExecutionException e)
722 catch (MojoFailureException e)
727 catch (RuntimeException e)
734 /** Remember mappings and configuration */
737 /** Close the connection - if one was opened */
738 connectionProvider.close();
740 /** Stop Log-Capturing */
741 MavenLogAppender.endPluginLog(this);
746 abstract ExceptionHandlerCollectingImpl build(MetadataImplementor metadata)
748 MojoFailureException,
749 MojoExecutionException;
752 private MutableClassLoader createClassLoader() throws MojoExecutionException
756 getLog().debug("Creating ClassLoader for project-dependencies...");
757 LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
760 file = new File(testOutputDirectory);
763 getLog().info("Creating test-output-directory: " + testOutputDirectory);
766 urls.add(file.toURI().toURL());
768 file = new File(outputDirectory);
771 getLog().info("Creating output-directory: " + outputDirectory);
774 urls.add(file.toURI().toURL());
776 return new MutableClassLoader(urls, getLog());
780 getLog().error("Error while creating ClassLoader!", e);
781 throw new MojoExecutionException(e.getMessage());
785 private void completeClassPath(MutableClassLoader classLoader)
787 MojoExecutionException
791 getLog().debug("Completing class-paths of the ClassLoader for project-dependencies...");
792 List<String> classpathFiles = project.getCompileClasspathElements();
794 classpathFiles.addAll(project.getTestClasspathElements());
795 LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
796 for (String pathElement : classpathFiles)
798 getLog().debug("Dependency: " + pathElement);
799 urls.add(new File(pathElement).toURI().toURL());
801 classLoader.add(urls);
805 getLog().error("Error while creating ClassLoader!", e);
806 throw new MojoExecutionException(e.getMessage());
810 private Map loadProperties(ConfigLoader configLoader)
812 MojoExecutionException
814 /** Try to read configuration from properties-file */
815 if (hibernateProperties == null)
819 return configLoader.loadProperties("hibernate.properties");
821 catch (ConfigurationException e)
823 getLog().debug(e.getMessage());
824 return Collections.EMPTY_MAP;
831 File file = new File(hibernateProperties);
834 getLog().info("Reading settings from file " + hibernateProperties + "...");
835 return configLoader.loadProperties(file);
838 return configLoader.loadProperties(hibernateProperties);
840 catch (ConfigurationException e)
842 getLog().error("Error while reading properties!", e);
843 throw new MojoExecutionException(e.getMessage());
848 private LoadedConfig loadConfig(ConfigLoader configLoader)
849 throws MojoExecutionException
851 /** Try to read configuration from configuration-file */
852 if (hibernateConfig == null)
856 return configLoader.loadConfigXmlResource("hibernate.cfg.xml");
858 catch (ConfigurationException e)
860 getLog().debug(e.getMessage());
868 File file = new File(hibernateConfig);
871 getLog().info("Reading configuration from file " + hibernateConfig + "...");
872 return configLoader.loadConfigXmlFile(file);
876 return configLoader.loadConfigXmlResource(hibernateConfig);
879 catch (ConfigurationException e)
881 getLog().error("Error while reading configuration!", e);
882 throw new MojoExecutionException(e.getMessage());
887 private void configure(Properties properties, ModificationTracker tracker)
888 throws MojoFailureException
891 * Special treatment for the configuration-value "execute": if it is
892 * switched to "true", the genearation fo the schema should be forced!
894 if (tracker.check(EXECUTE, execute.toString()) && execute)
897 "hibernate.schema.execute was switched on: " +
898 "forcing generation/execution of SQL"
902 configure(properties, execute, EXECUTE);
905 * Configure the generation of the SQL.
906 * Overwrite values from properties-file if the configuration parameter is
907 * known to Hibernate.
909 configure(properties, dialect, DIALECT);
910 configure(properties, delimiter, HBM2DDL_DELIMITER);
911 configure(properties, format, FORMAT_SQL);
912 configure(properties, createNamespaces, HBM2DLL_CREATE_NAMESPACES);
913 configure(properties, implicitNamingStrategy, IMPLICIT_NAMING_STRATEGY);
914 configure(properties, physicalNamingStrategy, PHYSICAL_NAMING_STRATEGY);
915 configure(properties, outputDirectory, OUTPUTDIRECTORY);
916 configure(properties, scanDependencies, SCAN_DEPENDENCIES);
917 configure(properties, scanTestClasses, SCAN_TESTCLASSES);
918 configure(properties, testOutputDirectory, TEST_OUTPUTDIRECTORY);
921 * Special treatment for the configuration-value "show": a change of its
922 * configured value should not lead to a regeneration of the database
926 show = Boolean.valueOf(properties.getProperty(SHOW_SQL));
928 properties.setProperty(SHOW_SQL, show.toString());
931 * Configure the connection parameters.
932 * Overwrite values from properties-file.
934 configure(properties, driver, DRIVER, JPA_JDBC_DRIVER);
935 configure(properties, url, URL, JPA_JDBC_URL);
936 configure(properties, username, USER, JPA_JDBC_USER);
937 configure(properties, password, PASS, JPA_JDBC_PASSWORD);
939 if (properties.isEmpty())
941 getLog().error("No properties set!");
942 throw new MojoFailureException("Hibernate configuration is missing!");
945 getLog().info("Gathered configuration:");
946 for (Entry<Object,Object> entry : properties.entrySet())
947 getLog().info(" " + entry.getKey() + " = " + entry.getValue());
950 private void configure(
951 Properties properties,
954 String alternativeKey
957 configure(properties, value, key);
959 if (properties.containsKey(alternativeKey))
961 if (properties.containsKey(key))
964 "Ignoring property " + alternativeKey + "=\"" +
965 properties.getProperty(alternativeKey) +
966 "\" in favour for property " + key + "=\"" +
967 properties.getProperty(key) + "\""
969 properties.remove(alternativeKey);
973 value = properties.getProperty(alternativeKey);
974 properties.remove(alternativeKey);
976 "Using value \"" + value + "\" from property " + alternativeKey +
977 " for property " + key
979 properties.setProperty(key, value);
984 private void configure(Properties properties, String value, String key)
988 if (properties.containsKey(key))
990 if (!properties.getProperty(key).equals(value))
993 "Overwriting property " + key + "=\"" +
994 properties.getProperty(key) +
995 "\" with value \"" + value + "\""
997 properties.setProperty(key, value);
1002 getLog().debug("Using value \"" + value + "\" for property " + key);
1003 properties.setProperty(key, value);
1008 private void configure(Properties properties, Boolean value, String key)
1010 configure(properties, value == null ? null : value.toString(), key);
1013 private File getOutputFile(String filename)
1015 MojoExecutionException
1017 File output = new File(filename);
1019 if (!output.isAbsolute())
1021 // Interpret relative file path relative to build directory
1022 output = new File(buildDirectory, filename);
1024 getLog().debug("Output file: " + output.getPath());
1026 // Ensure that directory path for specified file exists
1027 File outFileParentDir = output.getParentFile();
1028 if (null != outFileParentDir && !outFileParentDir.exists())
1033 "Creating directory path for output file:" +
1034 outFileParentDir.getPath()
1036 outFileParentDir.mkdirs();
1041 "Error creating directory path for output file: " + e.getMessage();
1042 getLog().error(error);
1043 throw new MojoExecutionException(error);
1049 output.createNewFile();
1051 catch (IOException e)
1053 String error = "Error creating output file: " + e.getMessage();
1054 getLog().error(error);
1055 throw new MojoExecutionException(error);
1058 if (!output.canWrite())
1061 "Output file " + output.getAbsolutePath() + " is not writable!";
1062 getLog().error(error);
1063 throw new MojoExecutionException(error);
1069 private void addMappings(MetadataSources sources, ModificationTracker tracker)
1070 throws MojoFailureException
1072 getLog().debug("Adding explicitly configured mappings...");
1073 if (mappings != null)
1077 for (String filename : mappings.split("[\\s,]+"))
1079 // First try the filename as absolute/relative path
1080 File file = new File(filename);
1083 // If the file was not found, search for it in the resource-directories
1084 for (Resource resource : project.getResources())
1086 file = new File(resource.getDirectory() + File.separator + filename);
1093 if (file.isDirectory())
1094 // TODO: add support to read all mappings under a directory
1095 throw new MojoFailureException(file.getAbsolutePath() + " is a directory");
1096 if (tracker.track(filename, new FileInputStream(file)))
1097 getLog().debug("Found new or modified mapping-file: " + filename);
1099 getLog().debug("Mapping-file unchanged: " + filename);
1101 sources.addFile(file);
1104 throw new MojoFailureException("File " + filename + " could not be found in any of the configured resource-directories!");
1107 catch (IOException e)
1109 throw new MojoFailureException("Cannot calculate MD5 sums!", e);
1114 private void addRoot(Set<URL> urls, String path) throws MojoFailureException
1118 File dir = new File(outputDirectory);
1121 getLog().info("Adding " + dir.getAbsolutePath() + " to the list of roots to scan...");
1122 urls.add(dir.toURI().toURL());
1125 catch (MalformedURLException e)
1127 getLog().error("error while adding the project-root to the list of roots to scan!", e);
1128 throw new MojoFailureException(e.getMessage());
1132 private void addDependencies(Set<URL> urls) throws MojoFailureException
1136 if (scanDependencies != null)
1138 Matcher matcher = SPLIT.matcher(scanDependencies);
1139 while (matcher.find())
1141 getLog().info("Adding dependencies from scope " + matcher.group() + " to the list of roots to scan");
1142 for (Artifact artifact : project.getDependencyArtifacts())
1144 if (!artifact.getScope().equalsIgnoreCase(matcher.group()))
1146 if (artifact.getFile() == null)
1148 getLog().warn("Cannot add dependency " + artifact.getId() + ": no JAR-file available!");
1151 getLog().info("Adding dependencies from scope " + artifact.getId() + " to the list of roots to scan");
1152 urls.add(artifact.getFile().toURI().toURL());
1157 catch (MalformedURLException e)
1159 getLog().error("Error while adding dependencies to the list of roots to scan!", e);
1160 throw new MojoFailureException(e.getMessage());
1164 private Set<String> scanUrls(Set<URL> scanRoots)
1166 MojoFailureException
1170 AnnotationDB db = new AnnotationDB();
1171 for (URL root : scanRoots)
1172 db.scanArchives(root);
1174 Set<String> classes = new HashSet<String>();
1175 if (db.getAnnotationIndex().containsKey(Entity.class.getName()))
1176 classes.addAll(db.getAnnotationIndex().get(Entity.class.getName()));
1177 if (db.getAnnotationIndex().containsKey(MappedSuperclass.class.getName()))
1178 classes.addAll(db.getAnnotationIndex().get(MappedSuperclass.class.getName()));
1179 if (db.getAnnotationIndex().containsKey(Embeddable.class.getName()))
1180 classes.addAll(db.getAnnotationIndex().get(Embeddable.class.getName()));
1186 getLog().error("Error while scanning!", e);
1187 throw new MojoFailureException(e.getMessage());
1191 private void addAnnotated(
1193 MetadataSources sources,
1194 ClassLoaderService classLoaderService,
1195 ModificationTracker tracker
1198 MojoFailureException,
1199 MojoExecutionException
1203 getLog().info("Adding annotated resource: " + name);
1204 String packageName = null;
1206 boolean error = false;
1209 Class<?> annotatedClass = classLoaderService.classForName(name);
1210 String resourceName = annotatedClass.getName();
1212 resourceName.substring(
1213 resourceName.lastIndexOf(".") + 1,
1214 resourceName.length()
1216 InputStream is = annotatedClass.getResourceAsStream(resourceName);
1219 if (tracker.track(name, is))
1220 getLog().debug("New or modified class: " + name);
1222 getLog().debug("Unchanged class: " + name);
1223 sources.addAnnotatedClass(annotatedClass);
1224 packageName = annotatedClass.getPackage().getName();
1228 getLog().error("cannot find ressource " + resourceName + " for class " + name);
1232 catch(ClassLoadingException e)
1238 throw new MojoExecutionException("error while inspecting annotated class " + name);
1241 while (packageName != null)
1243 if (packages.contains(packageName))
1245 String resource = packageName.replace('.', '/') + "/package-info.class";
1246 InputStream is = classLoaderService.locateResourceStream(resource);
1249 // No compiled package-info available: no package-level annotations!
1250 getLog().debug("Package " + packageName + " is not annotated.");
1254 if (tracker.track(packageName, is))
1255 getLog().debug("New or modified package: " + packageName);
1257 getLog().debug("Unchanged package: " + packageName);
1258 getLog().info("Adding annotations from package " + packageName);
1259 sources.addPackage(packageName);
1261 packages.add(packageName);
1262 int i = packageName.lastIndexOf('.');
1266 packageName = packageName.substring(0,i);
1271 getLog().error("Error while adding the annotated class " + name, e);
1272 throw new MojoFailureException(e.getMessage());
1276 private ParsedPersistenceXmlDescriptor loadPersistenceUnit(
1277 ClassLoaderService classLoaderService,
1278 Properties properties
1281 MojoFailureException
1283 PersistenceXmlParser parser =
1284 new PersistenceXmlParser(
1286 PersistenceUnitTransactionType.RESOURCE_LOCAL
1289 Map<String, ParsedPersistenceXmlDescriptor> units =
1290 parser.doResolve(properties);
1292 if (persistenceUnit == null)
1294 Iterator<String> names = units.keySet().iterator();
1295 if (!names.hasNext())
1297 getLog().info("Found no META-INF/persistence.xml.");
1301 String name = names.next();
1302 if (!names.hasNext())
1304 getLog().info("Using persistence-unit " + name);
1305 return units.get(name);
1308 StringBuilder builder = new StringBuilder();
1309 builder.append("No name provided and multiple persistence units found: ");
1310 builder.append(name);
1311 while(names.hasNext())
1313 builder.append(", ");
1314 builder.append(names.next());
1316 builder.append('.');
1317 throw new MojoFailureException(builder.toString());
1320 if (units.containsKey(persistenceUnit))
1322 getLog().info("Using configured persistence-unit " + persistenceUnit);
1323 return units.get(persistenceUnit);
1326 throw new MojoFailureException("Could not find persistence-unit " + persistenceUnit);