Added support for mapping-configuration through mapping-files (*.hbm.xml)
[hibernate4-maven-plugin] / src / main / java / de / juplo / plugins / hibernate4 / Hbm2DdlMojo.java
index 3b3a72b..044e330 100644 (file)
@@ -19,6 +19,7 @@ package de.juplo.plugins.hibernate4;
 import com.pyx4j.log4j.MavenLogAppender;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -28,6 +29,7 @@ import java.math.BigInteger;
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.sql.Connection;
 import java.sql.Driver;
 import java.sql.DriverManager;
@@ -48,11 +50,13 @@ import java.util.logging.Logger;
 import javax.persistence.Embeddable;
 import javax.persistence.Entity;
 import javax.persistence.MappedSuperclass;
+import org.apache.maven.model.Resource;
 import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
 import org.apache.maven.project.MavenProject;
 import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.NamingStrategy;
 import org.hibernate.tool.hbm2ddl.SchemaExport;
 import org.hibernate.tool.hbm2ddl.SchemaExport.Type;
 import org.hibernate.tool.hbm2ddl.Target;
@@ -77,6 +81,7 @@ public class Hbm2DdlMojo extends AbstractMojo
   public final static String USERNAME = "hibernate.connection.username";
   public final static String PASSWORD = "hibernate.connection.password";
   public final static String DIALECT = "hibernate.dialect";
+  public final static String NAMING_STRATEGY="hibernate.ejb.naming_strategy";
 
   private final static String MD5S = "schema.md5s";
 
@@ -85,7 +90,7 @@ public class Hbm2DdlMojo extends AbstractMojo
    * <p>
    * Only needed internally.
    *
-   * @parameter expression="${project}"
+   * @parameter property="project"
    * @required
    * @readonly
    */
@@ -96,7 +101,7 @@ public class Hbm2DdlMojo extends AbstractMojo
    * <p>
    * Only needed internally.
    *
-   * @parameter expression="${project.build.directory}"
+   * @parameter property="project.build.directory"
    * @required
    * @readonly
    */
@@ -108,7 +113,7 @@ public class Hbm2DdlMojo extends AbstractMojo
    * This parameter defaults to the maven build-output-directory for classes.
    * Additonally, all dependencies are scanned for annotated classes.
    *
-   * @parameter expression="${project.build.outputDirectory}"
+   * @parameter property="project.build.outputDirectory"
    */
   private String outputDirectory;
 
@@ -118,7 +123,7 @@ public class Hbm2DdlMojo extends AbstractMojo
    * If this parameter is set to <code>true</code> the test-classes of the
    * artifact will be scanned for hibernate-annotated classes additionally.
    *
-   * @parameter expression="${hibernate.export.scann_testclasses}" default-value="false"
+   * @parameter property="hibernate.export.scan_testclasses" default-value="false"
    */
   private boolean scanTestClasses;
 
@@ -131,7 +136,7 @@ public class Hbm2DdlMojo extends AbstractMojo
    * This parameter is only used, when <code>scanTestClasses</code> is set
    * to <code>true</code>!
    *
-   * @parameter expression="${project.build.testOutputDirectory}"
+   * @parameter property="project.build.testOutputDirectory"
    */
   private String testOutputDirectory;
 
@@ -146,7 +151,7 @@ public class Hbm2DdlMojo extends AbstractMojo
    * The excecution is skipped automatically, if no modified or newly added
    * annotated classes are found and the dialect was not changed.
    *
-   * @parameter expression="${maven.test.skip}" default-value="false"
+   * @parameter property="maven.test.skip" default-value="false"
    */
   private boolean skip;
 
@@ -158,45 +163,51 @@ public class Hbm2DdlMojo extends AbstractMojo
    * <p>
    * <code>skip</code> takes precedence over <code>force</code>.
    *
-   * @parameter expression="${hibernate.export.force}" default-value="false"
+   * @parameter property="hibernate.export.force" default-value="false"
    */
   private boolean force;
 
   /**
    * SQL-Driver name.
    *
-   * @parameter expression="${hibernate.connection.driver_class}
+   * @parameter property="hibernate.connection.driver_class"
    */
   private String driverClassName;
 
   /**
    * Database URL.
    *
-   * @parameter expression="${hibernate.connection.url}"
+   * @parameter property="hibernate.connection.url"
    */
   private String url;
 
   /**
    * Database username
    *
-   * @parameter expression="${hibernate.connection.username}"
+   * @parameter property="hibernate.connection.username"
    */
   private String username;
 
   /**
    * Database password
    *
-   * @parameter expression="${hibernate.connection.password}"
+   * @parameter property="hibernate.connection.password"
    */
   private String password;
 
   /**
    * Hibernate dialect.
    *
-   * @parameter expression="${hibernate.dialect}"
+   * @parameter property="hibernate.dialect"
    */
   private String hibernateDialect;
 
+  /**
+   * Hibernate Naming Strategy
+   * @parameter property="hibernate.ejb.naming_strategy"
+   */
+  private String hibernateNamingStrategy;
+
   /**
    * Path to Hibernate configuration file.
    *
@@ -204,6 +215,14 @@ public class Hbm2DdlMojo extends AbstractMojo
    */
   private String hibernateProperties;
 
+  /**
+   * List of Hibernate-Mapping-Files (XML).
+   * Multiple files can be separated with white-spaces and/or commas.
+   *
+   * @parameter property="hibernate.mapping"
+   */
+  private String hibernateMapping;
+
   /**
    * Target of execution:
    * <ul>
@@ -212,7 +231,8 @@ public class Hbm2DdlMojo extends AbstractMojo
    *   <li><strong>SCRIPT</strong> export schema to SQL-script</li>
    *   <li><strong>BOTH</strong></li>
    * </ul>
-   * @parameter expression="${hibernate.export.target}" default-value="EXPORT"
+   *
+   * @parameter property="hibernate.export.target" default-value="EXPORT"
    */
   private String target;
 
@@ -224,28 +244,29 @@ public class Hbm2DdlMojo extends AbstractMojo
    *   <li><strong>DROP</strong> drop database-schema</li>
    *   <li><strong>BOTH</strong> (<strong>DEFAULT!</strong>)</li>
    * </ul>
-   * @parameter expression="${hibernate.export.type}" default-value="BOTH"
+   *
+   * @parameter property="hibernate.export.type" default-value="BOTH"
    */
   private String type;
 
   /**
    * Output file.
    *
-   * @parameter expression="${hibernate.export.schema.filename}" default-value="${project.build.directory}/schema.sql"
+   * @parameter property="hibernate.export.schema.filename" default-value="${project.build.directory}/schema.sql"
    */
   private String outputFile;
 
   /**
    * Delimiter in output-file.
    *
-   * @parameter expression="${hibernate.export.schema.delimiter}" default-value=";"
+   * @parameter property="hibernate.export.schema.delimiter" default-value=";"
    */
   private String delimiter;
 
   /**
    * Format output-file.
    *
-   * @parameter expression="${hibernate.export.schema.format}" default-value="true"
+   * @parameter property="hibernate.export.schema.format" default-value="true"
    */
   private boolean format;
 
@@ -396,11 +417,16 @@ public class Hbm2DdlMojo extends AbstractMojo
     }
 
     if (classes.isEmpty())
-      throw new MojoFailureException("No annotated classes found in directory " + outputDirectory);
-
-    getLog().debug("Detected classes with mapping-annotations:");
-    for (Class<?> annotatedClass : classes)
-      getLog().debug("  " + annotatedClass.getName());
+    {
+      if (hibernateMapping == null || hibernateMapping.isEmpty())
+        throw new MojoFailureException("No annotated classes found in directory " + outputDirectory);
+    }
+    else
+    {
+      getLog().debug("Detected classes with mapping-annotations:");
+      for (Class<?> annotatedClass : classes)
+        getLog().debug("  " + annotatedClass.getName());
+    }
 
 
     Properties properties = new Properties();
@@ -466,7 +492,7 @@ public class Hbm2DdlMojo extends AbstractMojo
         getLog().debug(
             "Overwriting property " +
             PASSWORD + "=" + properties.getProperty(PASSWORD) +
-            " with the value " + password 
+            " with the value " + password
           );
       else
         getLog().debug("Using the value " + password);
@@ -484,6 +510,18 @@ public class Hbm2DdlMojo extends AbstractMojo
         getLog().debug("Using the value " + hibernateDialect);
       properties.setProperty(DIALECT, hibernateDialect);
     }
+    if ( hibernateNamingStrategy != null )
+    {
+      if ( properties.contains(NAMING_STRATEGY))
+        getLog().debug(
+            "Overwriting property " +
+            NAMING_STRATEGY + "=" + properties.getProperty(NAMING_STRATEGY) +
+            " with the value " + hibernateNamingStrategy
+           );
+      else
+        getLog().debug("Using the value " + hibernateNamingStrategy);
+      properties.setProperty(NAMING_STRATEGY, hibernateNamingStrategy);
+    }
 
     /** The generated SQL varies with the dialect! */
     if (md5s.containsKey(DIALECT))
@@ -512,6 +550,24 @@ public class Hbm2DdlMojo extends AbstractMojo
 
     Configuration config = new Configuration();
     config.setProperties(properties);
+
+    if ( properties.containsKey(NAMING_STRATEGY))
+    {
+      String namingStrategy = properties.getProperty(NAMING_STRATEGY);
+      getLog().debug("Explicitly set NamingStrategy: " + namingStrategy);
+      try
+      {
+        @SuppressWarnings("unchecked")
+        Class<NamingStrategy> namingStrategyClass = (Class<NamingStrategy>) Class.forName(namingStrategy);
+        config.setNamingStrategy(namingStrategyClass.newInstance());
+      }
+      catch (Exception e)
+      {
+        getLog().error("Error setting NamingStrategy", e);
+        throw new MojoExecutionException(e.getMessage());
+      }
+    }
+
     getLog().debug("Adding annotated classes to hibernate-mapping-configuration...");
     for (Class<?> annotatedClass : classes)
     {
@@ -519,6 +575,63 @@ public class Hbm2DdlMojo extends AbstractMojo
       config.addAnnotatedClass(annotatedClass);
     }
 
+    if (hibernateMapping != null)
+    {
+      try
+      {
+        MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
+        for (String filename : hibernateMapping.split("[\\s,]+"))
+        {
+          File file = null;
+          for (Resource resource : project.getResources())
+          {
+            file = new File(resource.getDirectory() + File.separator + filename);
+            if (file.exists())
+              break;
+          }
+          if (file != null && file.exists())
+          {
+            InputStream is = new FileInputStream(file);
+            byte[] buffer = new byte[1024*4]; // copy data in 4MB-chunks
+            int i;
+            while((i = is.read(buffer)) > -1)
+              digest.update(buffer, 0, i);
+            is.close();
+            byte[] bytes = digest.digest();
+            BigInteger bi = new BigInteger(1, bytes);
+            String newMd5 = String.format("%0" + (bytes.length << 1) + "x", bi);
+            String oldMd5 = !md5s.containsKey(filename) ? "" : md5s.get(filename);
+            if (!newMd5.equals(oldMd5))
+            {
+              getLog().debug("Found new or modified mapping-file: " + filename);
+              modified = true;
+              md5s.put(filename, newMd5);
+            }
+            else
+            {
+              getLog().debug(oldMd5 + " -> mapping-file unchanged: " + filename);
+            }
+            getLog().debug("Adding mappings from XML-configurationfile: " + file);
+            config.addFile(file);
+          }
+          else
+            throw new MojoFailureException("File " + filename + " could not be found in any of the configured resource-directories!");
+        }
+      }
+      catch (NoSuchAlgorithmException e)
+      {
+        throw new MojoFailureException("Cannot calculate MD5-summs!", e);
+      }
+      catch (FileNotFoundException e)
+      {
+        throw new MojoFailureException("Cannot calculate MD5-summs!", e);
+      }
+      catch (IOException e)
+      {
+        throw new MojoFailureException("Cannot calculate MD5-summs!", e);
+      }
+    }
+
     Target target = null;
     try
     {
@@ -553,7 +666,7 @@ public class Hbm2DdlMojo extends AbstractMojo
         && !force
       )
     {
-      getLog().info("No modified annotated classes found and dialect unchanged.");
+      getLog().info("No modified annotated classes or mapping-files found and dialect unchanged.");
       getLog().info("Skipping schema generation!");
       project.getProperties().setProperty(EXPORT_SKIPPED_PROPERTY, "true");
       return;