import com.pyx4j.log4j.MavenLogAppender;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.net.MalformedURLException;
import java.net.URL;
-import java.net.URLClassLoader;
import java.security.NoSuchAlgorithmException;
+import java.time.ZonedDateTime;
import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
-import java.util.LinkedList;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.hibernate.boot.MetadataBuilder;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.cfgxml.internal.ConfigLoader;
+import org.hibernate.boot.cfgxml.spi.LoadedConfig;
+import org.hibernate.boot.cfgxml.spi.MappingReference;
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.boot.registry.BootstrapServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
+import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.boot.registry.selector.spi.StrategySelector;
import org.hibernate.boot.spi.MetadataImplementor;
+import org.hibernate.cfg.AvailableSettings;
import static org.hibernate.cfg.AvailableSettings.DIALECT;
import static org.hibernate.cfg.AvailableSettings.DRIVER;
import static org.hibernate.cfg.AvailableSettings.FORMAT_SQL;
+import static org.hibernate.cfg.AvailableSettings.HBM2DDL_DELIMITER;
import static org.hibernate.cfg.AvailableSettings.HBM2DLL_CREATE_NAMESPACES;
import static org.hibernate.cfg.AvailableSettings.IMPLICIT_NAMING_STRATEGY;
+import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_DRIVER;
+import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_PASSWORD;
+import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_URL;
+import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_USER;
import static org.hibernate.cfg.AvailableSettings.PASS;
import static org.hibernate.cfg.AvailableSettings.PHYSICAL_NAMING_STRATEGY;
import static org.hibernate.cfg.AvailableSettings.SHOW_SQL;
import static org.hibernate.cfg.AvailableSettings.USER;
import static org.hibernate.cfg.AvailableSettings.URL;
+import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.internal.util.config.ConfigurationException;
-import static org.hibernate.jpa.AvailableSettings.JDBC_DRIVER;
-import static org.hibernate.jpa.AvailableSettings.JDBC_PASSWORD;
-import static org.hibernate.jpa.AvailableSettings.JDBC_URL;
-import static org.hibernate.jpa.AvailableSettings.JDBC_USER;
import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor;
import org.hibernate.jpa.boot.internal.PersistenceXmlParser;
-import org.hibernate.jpa.boot.spi.ProviderChecker;
+import org.hibernate.tool.schema.TargetType;
+import org.hibernate.tool.schema.internal.ExceptionHandlerCollectingImpl;
+import org.hibernate.tool.schema.internal.exec.ScriptTargetOutputToFile;
+import org.hibernate.tool.schema.spi.ExecutionOptions;
+import org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator;
+import org.hibernate.tool.schema.spi.ScriptTargetOutput;
+import org.hibernate.tool.schema.spi.TargetDescriptor;
import org.scannotation.AnnotationDB;
*/
public abstract class AbstractSchemaMojo extends AbstractMojo
{
- public final static String EXPORT = "hibernate.schema.export";
- public final static String DELIMITER = "hibernate.schema.delimiter";
+ public final static String EXECUTE = "hibernate.schema.execute";
public final static String OUTPUTDIRECTORY = "project.build.outputDirectory";
+ public final static String SCAN_CLASSES = "hibernate.schema.scan.classes";
public final static String SCAN_DEPENDENCIES = "hibernate.schema.scan.dependencies";
public final static String SCAN_TESTCLASSES = "hibernate.schema.scan.test_classes";
public final static String TEST_OUTPUTDIRECTORY = "project.build.testOutputDirectory";
public final static String SKIPPED = "hibernate.schema.skipped";
+ public final static String SCRIPT = "hibernate.schema.script";
private final static Pattern SPLIT = Pattern.compile("[^,\\s]+");
+ private final Set<String> packages = new HashSet<String>();
+
/**
* The maven project.
* @required
* @readonly
*/
- String buildDirectory;
+ private String buildDirectory;
/** Parameters to configure the genaration of the SQL *********************/
/**
- * Export the database-schma to the database.
+ * Excecute the generated SQL.
* If set to <code>false</code>, only the SQL-script is created and the
* database is not touched.
* <p>
* it is not known by Hibernate nor JPA and, hence, not picked up from
* their configuration!
*
- * @parameter property="hibernate.schema.export" default-value="true"
+ * @parameter property="hibernate.schema.execute" default-value="true"
* @since 2.0
*/
- Boolean export;
+ private Boolean execute;
/**
* Skip execution
* If set to <code>true</code>, the execution is skipped.
* <p>
* A skipped execution is signaled via the maven-property
- * <code>${hibernate.export.skipped}</code>.
+ * <code>${hibernate.schema.skipped}</code>.
* <p>
* The execution is skipped automatically, if no modified or newly added
* annotated classes are found and the dialect was not changed.
private boolean skip;
/**
- * Force execution
+ * Force generation/execution
* <p>
- * Force execution, even if no modified or newly added annotated classes
- * where found and the dialect was not changed.
+ * Force the generation and (if configured) the execution of the SQL, even if
+ * no modified or newly added annotated classes where found and the
+ * configuration was not changed.
* <p>
* <code>skip</code> takes precedence over <code>force</code>.
* <p>
* it is not known by Hibernate nor JPA and, hence, not picked up from
* their configuration!
*
- * @parameter property="hibernate.schema.delimiter" default-value=";"
+ * @parameter property="hibernate.hbm2ddl.delimiter" default-value=";"
* @since 1.0
*/
- String delimiter;
+ private String delimiter;
/**
* Show the generated SQL in the command-line output.
* @parameter property="hibernate.show_sql"
* @since 1.0
*/
- Boolean show;
+ private Boolean show;
/**
* Format output-file.
* @parameter property="hibernate.format_sql"
* @since 1.0
*/
- Boolean format;
+ private Boolean format;
/**
* Specifies whether to automatically create also the database schema/catalog.
* @parameter property="hibernate.hbm2dll.create_namespaces" default-value="false"
* @since 2.0
*/
- Boolean createNamespaces;
+ private Boolean createNamespaces;
/**
* Implicit naming strategy
*/
private String physicalNamingStrategy;
+ /**
+ * Wether the project should be scanned for annotated-classes, or not
+ * <p>
+ * This parameter is intended to allow overwriting of the parameter
+ * <code>exclude-unlisted-classes</code> of a <code>persistence-unit</code>.
+ * If not specified, it defaults to <code>true</code>
+ *
+ * @parameter property="hibernate.schema.scan.classes"
+ * @since 2.0
+ */
+ private Boolean scanClasses;
+
/**
* Classes-Directory to scan.
* <p>
private String scanDependencies;
/**
- * Whether to scan test-classes too, or not.
+ * Whether to scan the test-branch of the project for annotated classes, or
+ * not.
* <p>
* If this parameter is set to <code>true</code> the test-classes of the
* artifact will be scanned for hibernate-annotated classes additionally.
throw new MojoFailureException("Digest-Algorithm MD5 is missing!", e);
}
- SimpleConnectionProvider connectionProvider =
+ final SimpleConnectionProvider connectionProvider =
new SimpleConnectionProvider(getLog());
try
/** Load checksums for old mapping and configuration */
tracker.load();
- /** Create a BootstrapServiceRegistry with special ClassLoader */
+ /** Create the ClassLoader */
+ MutableClassLoader classLoader = createClassLoader();
+
+ /** Create a BootstrapServiceRegistry with the created ClassLoader */
BootstrapServiceRegistry bootstrapServiceRegitry =
new BootstrapServiceRegistryBuilder()
- .applyClassLoader(createClassLoader())
+ .applyClassLoader(classLoader)
.build();
ClassLoaderService classLoaderService =
bootstrapServiceRegitry.getService(ClassLoaderService.class);
/** Loading and merging configuration */
properties.putAll(loadProperties(configLoader));
- properties.putAll(loadConfig(configLoader));
- properties.putAll(loadPersistenceUnit(classLoaderService, properties));
+ LoadedConfig config = loadConfig(configLoader);
+ if (config != null)
+ properties.putAll(config.getConfigurationValues());
+ ParsedPersistenceXmlDescriptor unit =
+ loadPersistenceUnit(classLoaderService, properties);
+ if (unit != null)
+ properties.putAll(unit.getProperties());
/** Overwriting/Completing configuration */
configure(properties, tracker);
else
getLog().debug("Configuration unchanged.");
+ /** Check, that the outputfile is writable */
+ final File output = getOutputFile(filename);
+ /** Check, if the outputfile is missing or was changed */
+ checkOutputFile(output, tracker);
+
/** Configure Hibernate */
- StandardServiceRegistry serviceRegistry =
+ final StandardServiceRegistry serviceRegistry =
new StandardServiceRegistryBuilder(bootstrapServiceRegitry)
.applySettings(properties)
.addService(ConnectionProvider.class, connectionProvider)
.build();
+ final MetadataSources sources = new MetadataSources(serviceRegistry);
+
+ /** Add the remaining class-path-elements */
+ completeClassPath(classLoader);
+
+ /** Apply mappings from hibernate-configuration, if present */
+ if (config != null)
+ {
+ for (MappingReference mapping : config.getMappingReferences())
+ mapping.apply(sources);
+ }
+
+ Set<String> classes;
+ if (unit == null)
+ {
+ /** No persistent unit: default behaviour */
+ if (scanClasses == null)
+ scanClasses = true;
+ Set<URL> urls = new HashSet<URL>();
+ if (scanClasses)
+ addRoot(urls, outputDirectory);
+ if (scanTestClasses)
+ addRoot(urls, testOutputDirectory);
+ addDependencies(urls);
+ classes = scanUrls(urls);
+ }
+ else
+ {
+ /** Follow configuration in persisten unit */
+ if (scanClasses == null)
+ scanClasses = !unit.isExcludeUnlistedClasses();
+ Set<URL> urls = new HashSet<URL>();
+ if (scanClasses)
+ {
+ /**
+ * Scan the root of the persiten unit and configured jars for
+ * annotated classes
+ */
+ urls.add(unit.getPersistenceUnitRootUrl());
+ for (URL url : unit.getJarFileUrls())
+ urls.add(url);
+ }
+ if (scanTestClasses)
+ addRoot(urls, testOutputDirectory);
+ classes = scanUrls(urls);
+ for (String className : unit.getManagedClassNames())
+ classes.add(className);
+ /**
+ * Add mappings from the default mapping-file
+ * <code>META-INF/orm.xml</code>, if present
+ */
+ boolean error = false;
+ InputStream is;
+ is = classLoader.getResourceAsStream("META-INF/orm.xml");
+ if (is != null)
+ {
+ getLog().info("Adding default JPA-XML-mapping from META-INF/orm.xml");
+ try
+ {
+ tracker.track("META-INF/orm.xml", is);
+ sources.addResource("META-INF/orm.xml");
+ }
+ catch (IOException e)
+ {
+ getLog().error("cannot read META-INF/orm.xml: " + e);
+ error = true;
+ }
+ }
+ else
+ {
+ getLog().debug("no META-INF/orm.xml found");
+ }
+ /**
+ * Add mappings from files, that are explicitly configured in the
+ * persistence unit
+ */
+ for (String mapping : unit.getMappingFileNames())
+ {
+ getLog().info("Adding explicitly configured mapping from " + mapping);
+ is = classLoader.getResourceAsStream(mapping);
+ if (is != null)
+ {
+ try
+ {
+ tracker.track(mapping, is);
+ sources.addResource(mapping);
+ }
+ catch (IOException e)
+ {
+ getLog().info("cannot read mapping-file " + mapping + ": " + e);
+ error = true;
+ }
+ }
+ else
+ {
+ getLog().error("cannot find mapping-file " + mapping);
+ error = true;
+ }
+ }
+ if (error)
+ throw new MojoFailureException(
+ "error, while reading mappings configured in persistence-unit \"" +
+ unit.getName() +
+ "\""
+ );
+ }
- /** Load Mappings */
- MetadataSources sources = new MetadataSources(serviceRegistry);
- addAnnotatedClasses(sources, classLoaderService, tracker);
+ /** Add the configured/collected annotated classes */
+ for (String className : classes)
+ addAnnotated(className, sources, classLoaderService, tracker);
+
+ /** Add explicitly configured classes */
addMappings(sources, tracker);
/** Skip execution, if mapping and configuration is unchanged */
if (!tracker.modified())
{
- getLog().info(
- "Mapping and configuration unchanged."
- );
+ getLog().info("Mapping and configuration unchanged.");
if (force)
- getLog().info("Schema generation is forced!");
+ getLog().info("Generation/execution is forced!");
else
{
getLog().info("Skipping schema generation!");
}
+ /** Truncate output file */
+ try
+ {
+ new FileOutputStream(output).getChannel().truncate(0).close();
+ }
+ catch (IOException e)
+ {
+ String error =
+ "Error while truncating " + output.getAbsolutePath() + ": "
+ + e.getMessage();
+ getLog().warn(error);
+ throw new MojoExecutionException(error);
+ }
+
/** Create a connection, if sufficient configuration infromation is available */
connectionProvider.open(classLoaderService, properties);
);
}
- build((MetadataImplementor)metadataBuilder.build());
+ /** Prepare the generation of the SQL */
+ Map settings = new HashMap();
+ settings.putAll(
+ serviceRegistry
+ .getService(ConfigurationService.class)
+ .getSettings()
+ );
+ ExceptionHandlerCollectingImpl handler =
+ new ExceptionHandlerCollectingImpl();
+ ExecutionOptions options =
+ SchemaManagementToolCoordinator
+ .buildExecutionOptions(settings, handler);
+ final EnumSet<TargetType> targetTypes = EnumSet.of(TargetType.SCRIPT);
+ if (execute)
+ targetTypes.add(TargetType.DATABASE);
+ TargetDescriptor target = new TargetDescriptor()
+ {
+ @Override
+ public EnumSet<TargetType> getTargetTypes()
+ {
+ return targetTypes;
+ }
+
+ @Override
+ public ScriptTargetOutput getScriptTargetOutput()
+ {
+ String charset =
+ (String)
+ serviceRegistry
+ .getService(ConfigurationService.class)
+ .getSettings()
+ .get(AvailableSettings.HBM2DDL_CHARSET_NAME);
+ return new ScriptTargetOutputToFile(output, charset);
+ }
+ };
+
+ /**
+ * Change class-loader of current thread.
+ * This is necessary, because still not all parts of Hibernate 5 use
+ * the newly introduced ClassLoaderService and will fail otherwise!
+ */
+ Thread thread = Thread.currentThread();
+ ClassLoader contextClassLoader = thread.getContextClassLoader();
+ try
+ {
+ thread.setContextClassLoader(classLoader);
+ build((MetadataImplementor)metadataBuilder.build(), options, target);
+ if (handler.getExceptions().size() > 0)
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Hibernate failed:");
+ for (Exception e : handler.getExceptions())
+ {
+ builder.append("\n * ");
+ builder.append(e.getMessage());
+ AbstractSchemaMojo.printStrackTrace(builder, e);
+ builder.append("\n");
+ }
+ String error = builder.toString();
+ getLog().error(error);
+ throw new MojoFailureException(error);
+ }
+ }
+ finally
+ {
+ thread.setContextClassLoader(contextClassLoader);
+ /** Track, the content of the generated script */
+ checkOutputFile(output, tracker);
+ }
+ }
+ catch (MojoExecutionException e)
+ {
+ tracker.failed();
+ throw e;
+ }
+ catch (MojoFailureException e)
+ {
+ tracker.failed();
+ throw e;
+ }
+ catch (RuntimeException e)
+ {
+ tracker.failed();
+ throw e;
}
finally
{
}
- abstract void build(MetadataImplementor metadata)
+ abstract void build(
+ MetadataImplementor metadata,
+ ExecutionOptions options,
+ TargetDescriptor target
+ )
throws
MojoFailureException,
MojoExecutionException;
- private URLClassLoader createClassLoader() throws MojoExecutionException
+ private MutableClassLoader createClassLoader() throws MojoExecutionException
{
try
{
getLog().debug("Creating ClassLoader for project-dependencies...");
- List<String> classpathFiles = project.getCompileClasspathElements();
- if (scanTestClasses)
- classpathFiles.addAll(project.getTestClasspathElements());
- List<URL> urls = new LinkedList<URL>();
+ LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
File file;
+
file = new File(testOutputDirectory);
if (!file.exists())
{
- getLog().info("creating test-output-directory: " + testOutputDirectory);
+ getLog().info("Creating test-output-directory: " + testOutputDirectory);
file.mkdirs();
}
urls.add(file.toURI().toURL());
+
file = new File(outputDirectory);
if (!file.exists())
{
- getLog().info("creating output-directory: " + outputDirectory);
+ getLog().info("Creating output-directory: " + outputDirectory);
file.mkdirs();
}
urls.add(file.toURI().toURL());
+
+ return new MutableClassLoader(urls, getLog());
+ }
+ catch (Exception e)
+ {
+ getLog().error("Error while creating ClassLoader!", e);
+ throw new MojoExecutionException(e.getMessage());
+ }
+ }
+
+ private void completeClassPath(MutableClassLoader classLoader)
+ throws
+ MojoExecutionException
+ {
+ try
+ {
+ getLog().debug("Completing class-paths of the ClassLoader for project-dependencies...");
+ List<String> classpathFiles = project.getCompileClasspathElements();
+ if (scanTestClasses)
+ classpathFiles.addAll(project.getTestClasspathElements());
+ LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
for (String pathElement : classpathFiles)
{
getLog().debug("Dependency: " + pathElement);
urls.add(new File(pathElement).toURI().toURL());
}
- return
- new URLClassLoader(
- urls.toArray(new URL[urls.size()]),
- getClass().getClassLoader()
- );
+ classLoader.add(urls);
}
catch (Exception e)
{
}
}
- private Map loadConfig(ConfigLoader configLoader)
+ private LoadedConfig loadConfig(ConfigLoader configLoader)
throws MojoExecutionException
{
/** Try to read configuration from configuration-file */
{
try
{
- return
- configLoader
- .loadConfigXmlResource("hibernate.cfg.xml")
- .getConfigurationValues();
+ return configLoader.loadConfigXmlResource("hibernate.cfg.xml");
}
catch (ConfigurationException e)
{
getLog().debug(e.getMessage());
- return Collections.EMPTY_MAP;
+ return null;
}
}
else
if (file.exists())
{
getLog().info("Reading configuration from file " + hibernateConfig + "...");
- return configLoader.loadConfigXmlFile(file).getConfigurationValues();
+ return configLoader.loadConfigXmlFile(file);
}
else
- return
- configLoader
- .loadConfigXmlResource(hibernateConfig)
- .getConfigurationValues();
+ {
+ return configLoader.loadConfigXmlResource(hibernateConfig);
+ }
}
catch (ConfigurationException e)
{
throws MojoFailureException
{
/**
- * Special treatment for the configuration-value "export": if it is
+ * Special treatment for the configuration-value "execute": if it is
* switched to "true", the genearation fo the schema should be forced!
*/
- if (tracker.check(EXPORT, export.toString()) && export)
+ if (tracker.check(EXECUTE, execute.toString()) && execute)
+ {
+ getLog().info(
+ "hibernate.schema.execute was switched on: " +
+ "forcing generation/execution of SQL"
+ );
tracker.touch();
+ }
+ configure(properties, execute, EXECUTE);
/**
* Configure the generation of the SQL.
* Overwrite values from properties-file if the configuration parameter is
* known to Hibernate.
*/
- dialect = configure(properties, dialect, DIALECT);
- tracker.track(DELIMITER, delimiter); // << not reflected in hibernate configuration!
- format = configure(properties, format, FORMAT_SQL);
- createNamespaces = configure(properties, createNamespaces, HBM2DLL_CREATE_NAMESPACES);
- implicitNamingStrategy = configure(properties, implicitNamingStrategy, IMPLICIT_NAMING_STRATEGY);
- physicalNamingStrategy = configure(properties, physicalNamingStrategy, PHYSICAL_NAMING_STRATEGY);
- tracker.track(OUTPUTDIRECTORY, outputDirectory); // << not reflected in hibernate configuration!
- tracker.track(SCAN_DEPENDENCIES, scanDependencies); // << not reflected in hibernate configuration!
- tracker.track(SCAN_TESTCLASSES, scanTestClasses.toString()); // << not reflected in hibernate configuration!
- tracker.track(TEST_OUTPUTDIRECTORY, testOutputDirectory); // << not reflected in hibernate configuration!
+ configure(properties, dialect, DIALECT);
+ configure(properties, delimiter, HBM2DDL_DELIMITER);
+ configure(properties, format, FORMAT_SQL);
+ configure(properties, createNamespaces, HBM2DLL_CREATE_NAMESPACES);
+ configure(properties, implicitNamingStrategy, IMPLICIT_NAMING_STRATEGY);
+ configure(properties, physicalNamingStrategy, PHYSICAL_NAMING_STRATEGY);
+ configure(properties, outputDirectory, OUTPUTDIRECTORY);
+ configure(properties, scanDependencies, SCAN_DEPENDENCIES);
+ configure(properties, scanTestClasses, SCAN_TESTCLASSES);
+ configure(properties, testOutputDirectory, TEST_OUTPUTDIRECTORY);
/**
* Special treatment for the configuration-value "show": a change of its
* Configure the connection parameters.
* Overwrite values from properties-file.
*/
- driver = configure(properties, driver, DRIVER, JDBC_DRIVER);
- url = configure(properties, url, URL, JDBC_URL);
- username = configure(properties, username, USER, JDBC_USER);
- password = configure(properties, password, PASS, JDBC_PASSWORD);
+ configure(properties, driver, DRIVER, JPA_JDBC_DRIVER);
+ configure(properties, url, URL, JPA_JDBC_URL);
+ configure(properties, username, USER, JPA_JDBC_USER);
+ configure(properties, password, PASS, JPA_JDBC_PASSWORD);
if (properties.isEmpty())
{
throw new MojoFailureException("Hibernate configuration is missing!");
}
- getLog().info("Gathered hibernate-configuration (turn on debugging for details):");
+ getLog().info("Gathered configuration:");
for (Entry<Object,Object> entry : properties.entrySet())
getLog().info(" " + entry.getKey() + " = " + entry.getValue());
}
- private String configure(
+ private void configure(
Properties properties,
String value,
String key,
String alternativeKey
)
{
- value = configure(properties, value, key);
- if (value == null)
- return properties.getProperty(alternativeKey);
+ configure(properties, value, key);
if (properties.containsKey(alternativeKey))
{
- getLog().warn(
- "Ignoring property " + alternativeKey + "=" +
- properties.getProperty(alternativeKey) + " in favour for property " +
- key + "=" + properties.getProperty(key)
- );
- properties.remove(alternativeKey);
+ if (properties.containsKey(key))
+ {
+ getLog().warn(
+ "Ignoring property " + alternativeKey + "=\"" +
+ properties.getProperty(alternativeKey) +
+ "\" in favour for property " + key + "=\"" +
+ properties.getProperty(key) + "\""
+ );
+ properties.remove(alternativeKey);
+ }
+ else
+ {
+ value = properties.getProperty(alternativeKey);
+ properties.remove(alternativeKey);
+ getLog().info(
+ "Using value \"" + value + "\" from property " + alternativeKey +
+ " for property " + key
+ );
+ properties.setProperty(key, value);
+ }
}
- return properties.getProperty(alternativeKey);
}
- private String configure(Properties properties, String value, String key)
+ private void configure(Properties properties, String value, String key)
{
if (value != null)
{
if (properties.containsKey(key))
- getLog().debug(
- "Overwriting property " + key + "=" + properties.getProperty(key) +
- " with the value " + value
- );
+ {
+ if (!properties.getProperty(key).equals(value))
+ {
+ getLog().info(
+ "Overwriting property " + key + "=\"" +
+ properties.getProperty(key) +
+ "\" with value \"" + value + "\""
+ );
+ properties.setProperty(key, value);
+ }
+ }
else
- getLog().debug("Using the value " + value + " for property " + key);
- properties.setProperty(key, value);
+ {
+ getLog().debug("Using value \"" + value + "\" for property " + key);
+ properties.setProperty(key, value);
+ }
}
- return properties.getProperty(key);
}
- private boolean configure(Properties properties, Boolean value, String key)
+ private void configure(Properties properties, Boolean value, String key)
{
- if (value != null)
+ configure(properties, value == null ? null : value.toString(), key);
+ }
+
+ private File getOutputFile(String filename)
+ throws
+ MojoExecutionException
+ {
+ File output = new File(filename);
+
+ if (!output.isAbsolute())
{
- if (properties.containsKey(key))
- getLog().debug(
- "Overwriting property " + key + "=" + properties.getProperty(key) +
- " with the value " + value
+ // Interpret relative file path relative to build directory
+ output = new File(buildDirectory, filename);
+ }
+ getLog().debug("Output file: " + output.getPath());
+
+ // Ensure that directory path for specified file exists
+ File outFileParentDir = output.getParentFile();
+ if (null != outFileParentDir && !outFileParentDir.exists())
+ {
+ try
+ {
+ getLog().info(
+ "Creating directory path for output file:" +
+ outFileParentDir.getPath()
);
+ outFileParentDir.mkdirs();
+ }
+ catch (Exception e)
+ {
+ String error =
+ "Error creating directory path for output file: " + e.getMessage();
+ getLog().error(error);
+ throw new MojoExecutionException(error);
+ }
+ }
+
+ try
+ {
+ output.createNewFile();
+ }
+ catch (IOException e)
+ {
+ String error = "Error creating output file: " + e.getMessage();
+ getLog().error(error);
+ throw new MojoExecutionException(error);
+ }
+
+ if (!output.canWrite())
+ {
+ String error =
+ "Output file " + output.getAbsolutePath() + " is not writable!";
+ getLog().error(error);
+ throw new MojoExecutionException(error);
+ }
+
+ return output;
+ }
+
+ private void checkOutputFile(File output, ModificationTracker tracker)
+ throws
+ MojoExecutionException
+ {
+ try
+ {
+ if (output.exists())
+ tracker.track(SCRIPT, new FileInputStream(output));
else
- getLog().debug("Using the value " + value + " for property " + key);
- properties.setProperty(key, value.toString());
+ tracker.track(SCRIPT, ZonedDateTime.now().toString());
+ }
+ catch (IOException e)
+ {
+ String error =
+ "Error while checking the generated script: " + e.getMessage();
+ getLog().error(error);
+ throw new MojoExecutionException(error);
}
- return Boolean.valueOf(properties.getProperty(key));
}
private void addMappings(MetadataSources sources, ModificationTracker tracker)
if (tracker.track(filename, new FileInputStream(file)))
getLog().debug("Found new or modified mapping-file: " + filename);
else
- getLog().debug("mapping-file unchanged: " + filename);
+ getLog().debug("Mapping-file unchanged: " + filename);
sources.addFile(file);
}
}
}
- private void addAnnotatedClasses(
- MetadataSources sources,
- ClassLoaderService classLoaderService,
- ModificationTracker tracker
- )
- throws
- MojoFailureException,
- MojoExecutionException
-
+ private void addRoot(Set<URL> urls, String path) throws MojoFailureException
{
try
{
- AnnotationDB db = new AnnotationDB();
- File dir;
-
- dir = new File(outputDirectory);
+ File dir = new File(path);
if (dir.exists())
{
- getLog().info("Scanning directory " + dir.getAbsolutePath() + " for annotated classes...");
- URL dirUrl = dir.toURI().toURL();
- db.scanArchives(dirUrl);
- }
-
- if (scanTestClasses)
- {
- dir = new File(testOutputDirectory);
- if (dir.exists())
- {
- getLog().info("Scanning directory " + dir.getAbsolutePath() + " for annotated classes...");
- URL dirUrl = dir.toURI().toURL();
- db.scanArchives(dirUrl);
- }
+ getLog().info("Adding " + dir.getAbsolutePath() + " to the list of roots to scan...");
+ urls.add(dir.toURI().toURL());
}
+ else
+ getLog().warn(
+ "the directory cannot be scanned for annotated classes, " +
+ "because it does not exist: " +
+ dir.getAbsolutePath()
+ );
+ }
+ catch (MalformedURLException e)
+ {
+ getLog().error("error while adding the project-root to the list of roots to scan!", e);
+ throw new MojoFailureException(e.getMessage());
+ }
+ }
+ private void addDependencies(Set<URL> urls) throws MojoFailureException
+ {
+ try
+ {
if (scanDependencies != null)
{
Matcher matcher = SPLIT.matcher(scanDependencies);
while (matcher.find())
{
- getLog().info("Scanning dependencies for scope " + matcher.group());
+ getLog().info("Adding dependencies from scope " + matcher.group() + " to the list of roots to scan");
for (Artifact artifact : project.getDependencyArtifacts())
{
if (!artifact.getScope().equalsIgnoreCase(matcher.group()))
continue;
if (artifact.getFile() == null)
{
- getLog().warn("Cannot scan dependency " + artifact.getId() + ": no JAR-file available!");
+ getLog().warn("Cannot add dependency " + artifact.getId() + ": no JAR-file available!");
continue;
}
- getLog().info("Scanning dependency " + artifact.getId() + " for annotated classes...");
- db.scanArchives(artifact.getFile().toURI().toURL());
+ getLog().info("Adding dependencies from scope " + artifact.getId() + " to the list of roots to scan");
+ urls.add(artifact.getFile().toURI().toURL());
}
}
}
+ }
+ catch (MalformedURLException e)
+ {
+ getLog().error("Error while adding dependencies to the list of roots to scan!", e);
+ throw new MojoFailureException(e.getMessage());
+ }
+ }
+
+ private Set<String> scanUrls(Set<URL> scanRoots)
+ throws
+ MojoFailureException
+ {
+ try
+ {
+ AnnotationDB db = new AnnotationDB();
+ for (URL root : scanRoots)
+ db.scanArchives(root);
Set<String> classes = new HashSet<String>();
if (db.getAnnotationIndex().containsKey(Entity.class.getName()))
if (db.getAnnotationIndex().containsKey(Embeddable.class.getName()))
classes.addAll(db.getAnnotationIndex().get(Embeddable.class.getName()));
- Set<String> packages = new HashSet<String>();
+ return classes;
+ }
+ catch (Exception e)
+ {
+ getLog().error("Error while scanning!", e);
+ throw new MojoFailureException(e.getMessage());
+ }
+ }
- for (String name : classes)
+ private void addAnnotated(
+ String name,
+ MetadataSources sources,
+ ClassLoaderService classLoaderService,
+ ModificationTracker tracker
+ )
+ throws
+ MojoFailureException,
+ MojoExecutionException
+ {
+ try
+ {
+ getLog().info("Adding annotated resource: " + name);
+ String packageName = null;
+
+ boolean error = false;
+ try
{
Class<?> annotatedClass = classLoaderService.classForName(name);
- String packageName = annotatedClass.getPackage().getName();
- if (!packages.contains(packageName))
- {
- InputStream is =
- annotatedClass.getResourceAsStream("package-info.class");
- if (is == null)
- {
- // No compiled package-info available: no package-level annotations!
- getLog().debug("Package " + packageName + " is not annotated.");
- }
- else
- {
- if (tracker.track(packageName, is))
- getLog().debug("New or modified package: " + packageName);
- else
- getLog().debug("Unchanged package: " + packageName);
- getLog().info("Adding annotated package " + packageName);
- sources.addPackage(packageName);
- }
- packages.add(packageName);
- }
String resourceName = annotatedClass.getName();
resourceName =
resourceName.substring(
resourceName.lastIndexOf(".") + 1,
resourceName.length()
) + ".class";
- InputStream is =
- annotatedClass
- .getResourceAsStream(resourceName);
- if (tracker.track(name, is))
- getLog().debug("New or modified class: " + name);
+ InputStream is = annotatedClass.getResourceAsStream(resourceName);
+ if (is != null)
+ {
+ if (tracker.track(name, is))
+ getLog().debug("New or modified class: " + name);
+ else
+ getLog().debug("Unchanged class: " + name);
+ sources.addAnnotatedClass(annotatedClass);
+ packageName = annotatedClass.getPackage().getName();
+ }
else
- getLog().debug("Unchanged class: " + name);
- getLog().info("Adding annotated class " + annotatedClass);
- sources.addAnnotatedClass(annotatedClass);
+ {
+ getLog().error("cannot find ressource " + resourceName + " for class " + name);
+ error = true;
+ }
+ }
+ catch(ClassLoadingException e)
+ {
+ packageName = name;
+ }
+ if (error)
+ {
+ throw new MojoExecutionException("error while inspecting annotated class " + name);
+ }
+
+ while (packageName != null)
+ {
+ if (packages.contains(packageName))
+ return;
+ String resource = packageName.replace('.', '/') + "/package-info.class";
+ InputStream is = classLoaderService.locateResourceStream(resource);
+ if (is == null)
+ {
+ // No compiled package-info available: no package-level annotations!
+ getLog().debug("Package " + packageName + " is not annotated.");
+ }
+ else
+ {
+ if (tracker.track(packageName, is))
+ getLog().debug("New or modified package: " + packageName);
+ else
+ getLog().debug("Unchanged package: " + packageName);
+ getLog().info("Adding annotations from package " + packageName);
+ sources.addPackage(packageName);
+ }
+ packages.add(packageName);
+ int i = packageName.lastIndexOf('.');
+ if (i < 0)
+ packageName = null;
+ else
+ packageName = packageName.substring(0,i);
}
}
catch (Exception e)
{
- getLog().error("Error while scanning!", e);
+ getLog().error("Error while adding the annotated class " + name, e);
throw new MojoFailureException(e.getMessage());
}
}
- private Properties loadPersistenceUnit(
+ private ParsedPersistenceXmlDescriptor loadPersistenceUnit(
ClassLoaderService classLoaderService,
Properties properties
)
PersistenceUnitTransactionType.RESOURCE_LOCAL
);
- List<ParsedPersistenceXmlDescriptor> units = parser.doResolve(properties);
+ Map<String, ParsedPersistenceXmlDescriptor> units =
+ parser.doResolve(properties);
if (persistenceUnit == null)
{
- switch (units.size())
+ Iterator<String> names = units.keySet().iterator();
+ if (!names.hasNext())
{
- case 0:
- getLog().info("Found no META-INF/persistence.xml.");
- return new Properties();
- case 1:
- getLog().info("Using persistence-unit " + units.get(0).getName());
- return units.get(0).getProperties();
- default:
- StringBuilder builder = new StringBuilder();
- builder.append("No name provided and multiple persistence units found: ");
- Iterator<ParsedPersistenceXmlDescriptor> it = units.iterator();
- builder.append(it.next().getName());
- while (it.hasNext())
- {
- builder.append(", ");
- builder.append(it.next().getName());
- }
- builder.append('.');
- throw new MojoFailureException(builder.toString());
+ getLog().info("Found no META-INF/persistence.xml.");
+ return null;
}
- }
- for (ParsedPersistenceXmlDescriptor unit : units)
- {
- getLog().debug("Found persistence-unit " + unit.getName());
- if (!unit.getName().equals(persistenceUnit))
- continue;
+ String name = names.next();
+ if (!names.hasNext())
+ {
+ getLog().info("Using persistence-unit " + name);
+ return units.get(name);
+ }
- // See if we (Hibernate) are the persistence provider
- if (!ProviderChecker.isProvider(unit, properties))
+ StringBuilder builder = new StringBuilder();
+ builder.append("No name provided and multiple persistence units found: ");
+ builder.append(name);
+ while(names.hasNext())
{
- getLog().debug("Wrong provider: " + unit.getProviderClassName());
- continue;
+ builder.append(", ");
+ builder.append(names.next());
}
+ builder.append('.');
+ throw new MojoFailureException(builder.toString());
+ }
- getLog().info("Using persistence-unit " + unit.getName());
- return unit.getProperties();
+ if (units.containsKey(persistenceUnit))
+ {
+ getLog().info("Using configured persistence-unit " + persistenceUnit);
+ return units.get(persistenceUnit);
}
throw new MojoFailureException("Could not find persistence-unit " + persistenceUnit);
}
+
+
+ public static void printStrackTrace(StringBuilder builder, Throwable t)
+ {
+ while (t != null)
+ {
+ builder.append("\n\tCause: ");
+ builder.append(t.getMessage() == null ? "" : t.getMessage().replaceAll("\\s+", " "));
+ for (StackTraceElement trace : t.getStackTrace())
+ {
+ builder.append("\n\t");
+ builder.append(trace.getClassName());
+ builder.append(".");
+ builder.append(trace.getMethodName());
+ builder.append("():");
+ builder.append(trace.getLineNumber());
+ }
+ t = t.getCause();
+ }
+ }
}