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.security.NoSuchAlgorithmException;
+import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
-import javax.persistence.spi.PersistenceUnitTransactionType;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.AbstractMojo;
*/
public abstract class AbstractSchemaMojo extends AbstractMojo
{
- public final static String EXPORT = "hibernate.schema.export";
+ 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]+");
/** 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
*/
- private Boolean export;
+ private Boolean execute;
/**
* Skip execution
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>
Properties properties = new Properties();
ConfigLoader configLoader = new ConfigLoader(bootstrapServiceRegitry);
- /** Loading and merging configuration */
+ /** Loading configuration */
properties.putAll(loadProperties(configLoader));
LoadedConfig config = loadConfig(configLoader);
if (config != null)
properties.putAll(config.getConfigurationValues());
+
+ /** Add the remaining class-path-elements */
+ addDirectDependenciesClassPath(classLoader);
+
+ /** Loading and merging configuration from persistence-unit(s) */
ParsedPersistenceXmlDescriptor unit =
- loadPersistenceUnit(classLoaderService, properties);
+ loadPersistenceUnit(classLoader, properties);
if (unit != null)
properties.putAll(unit.getProperties());
/** 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 */
final StandardServiceRegistry serviceRegistry =
final MetadataSources sources = new MetadataSources(serviceRegistry);
/** Add the remaining class-path-elements */
- completeClassPath(classLoader);
+ addAllDependenciesToClassPath(classLoader);
/** Apply mappings from hibernate-configuration, if present */
if (config != null)
if (scanClasses == null)
scanClasses = true;
Set<URL> urls = new HashSet<URL>();
+ getLog().debug("Compiling the dependencies, that are scanned for annotated classes");
if (scanClasses)
addRoot(urls, outputDirectory);
if (scanTestClasses)
/** Follow configuration in persisten unit */
if (scanClasses == null)
scanClasses = !unit.isExcludeUnlistedClasses();
+ getLog().debug("Compiling the dependencies, that are scanned for annotated classes");
+
Set<URL> urls = new HashSet<URL>();
if (scanClasses)
{
+ getLog().debug("Only dependencies relative to persistent-unit " + unit.getName() + " are scanned!");
/**
* Scan the root of the persiten unit and configured jars for
* annotated classes
*/
+ getLog().debug(" - adding " + unit.getPersistenceUnitRootUrl());
urls.add(unit.getPersistenceUnitRootUrl());
for (URL url : unit.getJarFileUrls())
+ {
+ getLog().debug(" - adding " + url);
urls.add(url);
+ }
+ if (scanTestClasses)
+ addRoot(urls, testOutputDirectory);
}
- if (scanTestClasses)
- addRoot(urls, testOutputDirectory);
+ else
+ getLog().debug("Scanning of unlisted classes is prohibited in persistent-unit " + unit.getName());
classes = scanUrls(urls);
for (String className : unit.getManagedClassNames())
classes.add(className);
* Add mappings from files, that are explicitly configured in the
* persistence unit
*/
+ getLog().info("Adding mappings from persistence-unit " + unit.getName());
for (String mapping : unit.getMappingFileNames())
{
- getLog().info("Adding explicitly configured mapping from " + mapping);
+ getLog().info(" - adding " + mapping);
is = classLoader.getResourceAsStream(mapping);
if (is != null)
{
/** 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);
SchemaManagementToolCoordinator
.buildExecutionOptions(settings, handler);
final EnumSet<TargetType> targetTypes = EnumSet.of(TargetType.SCRIPT);
- if (export)
+ if (execute)
targetTypes.add(TargetType.DATABASE);
TargetDescriptor target = new TargetDescriptor()
{
{
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);
- for (Exception e : handler.getExceptions())
- getLog().error(e.getMessage());
+ /** Track, the content of the generated script */
+ checkOutputFile(output, tracker);
}
}
catch (MojoExecutionException e)
}
}
- private void completeClassPath(MutableClassLoader classLoader)
+ private void addDirectDependenciesClassPath(MutableClassLoader classLoader)
throws
MojoExecutionException
{
try
{
- getLog().debug("Completing class-paths of the ClassLoader for project-dependencies...");
- List<String> classpathFiles = project.getCompileClasspathElements();
+ getLog().debug("Adding all direct project-dependencies to the ClassLoader...");
+ LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
+ addDependencies(urls);
if (scanTestClasses)
- classpathFiles.addAll(project.getTestClasspathElements());
+ addRoot(urls, testOutputDirectory);
+ classLoader.add(urls);
+ }
+ catch (Exception e)
+ {
+ getLog().error("Error while creating ClassLoader!", e);
+ throw new MojoExecutionException(e.getMessage());
+ }
+ }
+
+ private void addAllDependenciesToClassPath(MutableClassLoader classLoader)
+ throws
+ MojoExecutionException
+ {
+ try
+ {
+ getLog().debug("Adding all project-dependencies to the ClassLoader...");
+ List<String> classpathFiles = project.getCompileClasspathElements();
+ classpathFiles.addAll(project.getTestClasspathElements());
LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
for (String pathElement : classpathFiles)
{
- getLog().debug("Dependency: " + pathElement);
+ getLog().debug(" - adding " + pathElement);
urls.add(new File(pathElement).toURI().toURL());
}
classLoader.add(urls);
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.
configure(properties, createNamespaces, HBM2DLL_CREATE_NAMESPACES);
configure(properties, implicitNamingStrategy, IMPLICIT_NAMING_STRATEGY);
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, 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
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());
}
String alternativeKey
)
{
- value = configure(properties, value, key);
- if (value == null)
- return;
+ 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);
+ }
}
}
- 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 void configure(Properties properties, Boolean value, String key)
{
- if (value != null)
- {
- if (properties.containsKey(key))
- getLog().debug(
- "Overwriting property " + key + "=" + properties.getProperty(key) +
- " with the value " + value
- );
- else
- getLog().debug("Using the value " + value + " for property " + key);
- properties.setProperty(key, value.toString());
- }
+ configure(properties, value == null ? null : value.toString(), key);
}
private File getOutputFile(String filename)
return output;
}
+ private void checkOutputFile(File output, ModificationTracker tracker)
+ throws
+ MojoExecutionException
+ {
+ try
+ {
+ if (output.exists())
+ tracker.track(SCRIPT, new FileInputStream(output));
+ else
+ 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);
+ }
+ }
+
private void addMappings(MetadataSources sources, ModificationTracker tracker)
throws MojoFailureException
{
// TODO: add support to read all mappings under a directory
throw new MojoFailureException(file.getAbsolutePath() + " is a directory");
if (tracker.track(filename, new FileInputStream(file)))
- getLog().debug("Found new or modified mapping-file: " + filename);
+ 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);
}
{
try
{
- File dir = new File(outputDirectory);
+ File dir = new File(path);
if (dir.exists())
{
- getLog().info("Adding " + dir.getAbsolutePath() + " to the list of roots to scan...");
+ getLog().info(" - adding " + dir.getAbsolutePath());
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)
{
Matcher matcher = SPLIT.matcher(scanDependencies);
while (matcher.find())
{
- getLog().info("Adding dependencies from scope " + matcher.group() + " to the list of roots to scan");
+ getLog().debug("Adding dependencies from scope " + matcher.group() + " to the list of roots to scan");
for (Artifact artifact : project.getDependencyArtifacts())
{
if (!artifact.getScope().equalsIgnoreCase(matcher.group()))
getLog().warn("Cannot add dependency " + artifact.getId() + ": no JAR-file available!");
continue;
}
- getLog().info("Adding dependencies from scope " + artifact.getId() + " to the list of roots to scan");
+ getLog().debug(" - adding " + artifact.getId());
urls.add(artifact.getFile().toURI().toURL());
}
}
}
private ParsedPersistenceXmlDescriptor loadPersistenceUnit(
- ClassLoaderService classLoaderService,
+ ClassLoader classLoader,
Properties properties
)
throws
MojoFailureException
{
- PersistenceXmlParser parser =
- new PersistenceXmlParser(
- classLoaderService,
- PersistenceUnitTransactionType.RESOURCE_LOCAL
- );
+ Map<String, ? extends Object> settings =
+ Collections.singletonMap(
+ AvailableSettings.CLASSLOADERS,
+ Collections.singletonList(classLoader)
+ );
+ // Find all available persistent unit descriptors
+ List<ParsedPersistenceXmlDescriptor> descriptors =
+ PersistenceXmlParser.locatePersistenceUnits(settings);
- Map<String, ParsedPersistenceXmlDescriptor> units =
- parser.doResolve(properties);
+ // Find all persistent units in the located descriptors
+ Map<String, ParsedPersistenceXmlDescriptor> units = new HashMap<>();
+ for (ParsedPersistenceXmlDescriptor descriptor : descriptors)
+ {
+ String unit = descriptor.getName();
+ if (units.containsKey(unit))
+ getLog().warn(
+ "Persistence unit " + unit +
+ " from " + descriptor.getPersistenceUnitRootUrl() +
+ " overwrites unit with the same name from " +
+ units.get(unit).getPersistenceUnitRootUrl()
+ );
+ units.put(unit, descriptor);
+ }
if (persistenceUnit == null)
{
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();
+ }
+ }
}