Upgraded Hibernate from 5.2.4.Final to 5.2.18.Final
[hibernate4-maven-plugin] / src / main / java / de / juplo / plugins / hibernate / AbstractSchemaMojo.java
index 9006f35..72ec9d4 100644 (file)
@@ -4,11 +4,13 @@ package de.juplo.plugins.hibernate;
 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;
@@ -25,7 +27,6 @@ import java.util.regex.Pattern;
 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;
@@ -87,13 +88,14 @@ import org.scannotation.AnnotationDB;
  */
 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]+");
 
@@ -126,7 +128,7 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
   /** 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>
@@ -136,10 +138,10 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
    * 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
@@ -164,10 +166,11 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
   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>
@@ -481,13 +484,18 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
       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());
 
@@ -502,6 +510,8 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
 
       /** 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 =
@@ -512,7 +522,7 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
       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)
@@ -528,6 +538,7 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
         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)
@@ -540,19 +551,28 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
         /** 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);
@@ -585,9 +605,10 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
          * 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)
           {
@@ -626,11 +647,9 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
       /** 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!");
@@ -640,6 +659,20 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
       }
 
 
+      /** 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);
 
@@ -681,7 +714,7 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
           SchemaManagementToolCoordinator
               .buildExecutionOptions(settings, handler);
       final EnumSet<TargetType> targetTypes = EnumSet.of(TargetType.SCRIPT);
-      if (export)
+      if (execute)
         targetTypes.add(TargetType.DATABASE);
       TargetDescriptor target = new TargetDescriptor()
       {
@@ -715,12 +748,27 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
       {
         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)
@@ -795,20 +843,39 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
     }
   }
 
-  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);
@@ -901,11 +968,18 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
       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.
@@ -918,10 +992,10 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
     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
@@ -948,7 +1022,7 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
       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());
   }
@@ -992,14 +1066,22 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
     if (value != null)
     {
       if (properties.containsKey(key))
-        getLog().info(
-            "Overwriting property " + key + "=\"" +
-            properties.getProperty(key) +
-            "\" with 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 value \"" + value + "\" for property " + key);
-      properties.setProperty(key, value);
+        properties.setProperty(key, value);
+      }
     }
   }
 
@@ -1064,6 +1146,26 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
     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
   {
@@ -1092,9 +1194,9 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
               // 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);
           }
@@ -1113,12 +1215,18 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
   {
     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)
     {
@@ -1136,7 +1244,7 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
         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()))
@@ -1146,7 +1254,7 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
               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());
           }
         }
@@ -1272,20 +1380,36 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
   }
 
   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);
 
+    // Find all persistent units in the located descriptors
     Map<String, ParsedPersistenceXmlDescriptor> units =
-        parser.doResolve(properties);
+        new HashMap<String, ParsedPersistenceXmlDescriptor>();
+    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)
     {
@@ -1323,4 +1447,24 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
 
     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();
+    }
+  }
 }