Fixed bug with directories, that should be scanned for annotations
[hibernate4-maven-plugin] / src / main / java / de / juplo / plugins / hibernate / AbstractSchemaMojo.java
index 6c8a281..6f3ff97 100644 (file)
@@ -4,15 +4,19 @@ 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.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;
@@ -33,6 +37,8 @@ import org.apache.maven.project.MavenProject;
 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;
@@ -40,24 +46,37 @@ import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
 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;
 
 
@@ -70,10 +89,19 @@ import org.scannotation.AnnotationDB;
  */
 public abstract class AbstractSchemaMojo extends AbstractMojo
 {
-  public final static String EXPORT_SKIPPED_PROPERTY = "hibernate.export.skipped";
+  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.
@@ -95,29 +123,159 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
    * @required
    * @readonly
    */
-  String buildDirectory;
+  private String buildDirectory;
+
+
+  /** Parameters to configure the genaration of the SQL *********************/
 
   /**
-   * Classes-Directory to scan.
+   * Excecute the generated SQL.
+   * If set to <code>false</code>, only the SQL-script is created and the
+   * database is not touched.
    * <p>
-   * This parameter defaults to the maven build-output-directory for classes.
-   * Additionally, all dependencies are scanned for annotated classes.
+   * <strong>Important:</strong>
+   * This configuration value can only be configured through the
+   * <code>pom.xml</code>, or by the definition of a system-property, because
+   * it is not known by Hibernate nor JPA and, hence, not picked up from
+   * their configuration!
    *
-   * @parameter property="project.build.outputDirectory"
+   * @parameter property="hibernate.schema.execute" default-value="true"
+   * @since 2.0
+   */
+  private Boolean execute;
+
+  /**
+   * Skip execution
+   * <p>
+   * If set to <code>true</code>, the execution is skipped.
+   * <p>
+   * A skipped execution is signaled via the maven-property
+   * <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.
+   * <p>
+   * <strong>Important:</strong>
+   * This configuration value can only be configured through the
+   * <code>pom.xml</code>, or by the definition of a system-property, because
+   * it is not known by Hibernate nor JPA and, hence, not picked up from
+   * their configuration!
+   *
+   * @parameter property="hibernate.schema.skip" default-value="${maven.test.skip}"
    * @since 1.0
    */
-  private String outputDirectory;
+  private boolean skip;
 
   /**
-   * Whether to scan test-classes too, or not.
+   * Force generation/execution
    * <p>
-   * If this parameter is set to <code>true</code> the test-classes of the
-   * artifact will be scanned for hibernate-annotated classes additionally.
+   * 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>
+   * <strong>Important:</strong>
+   * This configuration value can only be configured through the
+   * <code>pom.xml</code>, or by the definition of a system-property, because
+   * it is not known by Hibernate nor JPA and, hence, not picked up from
+   * their configuration!
    *
-   * @parameter property="hibernate.export.scan_testclasses" default-value="false"
-   * @since 1.0.1
+   * @parameter property="hibernate.schema.force" default-value="false"
+   * @since 1.0
+   */
+  private boolean force;
+
+  /**
+   * Hibernate dialect.
+   *
+   * @parameter property="hibernate.dialect"
+   * @since 1.0
+   */
+  private String dialect;
+
+  /**
+   * Delimiter in output-file.
+   * <p>
+   * <strong>Important:</strong>
+   * This configuration value can only be configured through the
+   * <code>pom.xml</code>, or by the definition of a system-property, because
+   * it is not known by Hibernate nor JPA and, hence, not picked up from
+   * their configuration!
+   *
+   * @parameter property="hibernate.hbm2ddl.delimiter" default-value=";"
+   * @since 1.0
+   */
+  private String delimiter;
+
+  /**
+   * Show the generated SQL in the command-line output.
+   *
+   * @parameter property="hibernate.show_sql"
+   * @since 1.0
+   */
+  private Boolean show;
+
+  /**
+   * Format output-file.
+   *
+   * @parameter property="hibernate.format_sql"
+   * @since 1.0
+   */
+  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
+   */
+  private Boolean createNamespaces;
+
+  /**
+   * Implicit naming strategy
+   *
+   * @parameter property="hibernate.implicit_naming_strategy"
+   * @since 2.0
    */
-  private boolean scanTestClasses;
+  private String implicitNamingStrategy;
+
+  /**
+   * Physical naming strategy
+   *
+   * @parameter property="hibernate.physical_naming_strategy"
+   * @since 2.0
+   */
+  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>
+   * This parameter defaults to the maven build-output-directory for classes.
+   * Additionally, all dependencies are scanned for annotated classes.
+   * <p>
+   * <strong>Important:</strong>
+   * This configuration value can only be configured through the
+   * <code>pom.xml</code>, or by the definition of a system-property, because
+   * it is not known by Hibernate nor JPA and, hence, not picked up from
+   * their configuration!
+   *
+   * @parameter property="project.build.outputDirectory"
+   * @since 1.0
+   */
+  private String outputDirectory;
 
   /**
    * Dependency-Scopes, that should be scanned for annotated classes.
@@ -125,7 +283,7 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
    * By default, only dependencies in the scope <code>compile</code> are
    * scanned for annotated classes. Multiple scopes can be seperated by
    * white space or commas.
-   * <p>md5s
+   * <p>
    * If you do not want any dependencies to be scanned for annotated
    * classes, set this parameter to <code>none</code>.
    * <p>
@@ -133,11 +291,29 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
    * dependencies. If some of your annotated classes are hidden in a
    * transitive dependency, you can simply add that dependency explicitly.
    *
-   * @parameter property="hibernate.export.scan_dependencies" default-value="compile"
+   * @parameter property="hibernate.schema.scan.dependencies" default-value="compile"
    * @since 1.0.3
    */
   private String scanDependencies;
 
+  /**
+   * 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.
+   * <p>
+   * <strong>Important:</strong>
+   * This configuration value can only be configured through the
+   * <code>pom.xml</code>, or by the definition of a system-property, because
+   * it is not known by Hibernate nor JPA and, hence, not picked up from
+   * their configuration!
+   *
+   * @parameter property="hibernate.schema.scan.test_classes" default-value="false"
+   * @since 1.0.1
+   */
+  private Boolean scanTestClasses;
+
   /**
    * Test-Classes-Directory to scan.
    * <p>
@@ -146,40 +322,20 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
    * <p>
    * This parameter is only used, when <code>scanTestClasses</code> is set
    * to <code>true</code>!
+   * <p>
+   * <strong>Important:</strong>
+   * This configuration value can only be configured through the
+   * <code>pom.xml</code>, or by the definition of a system-property, because
+   * it is not known by Hibernate nor JPA and, hence, not picked up from
+   * their configuration!
    *
    * @parameter property="project.build.testOutputDirectory"
    * @since 1.0.2
    */
   private String testOutputDirectory;
 
-  /**
-   * Skip execution
-   * <p>
-   * 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>.
-   * <p>
-   * The execution is skipped automatically, if no modified or newly added
-   * annotated classes are found and the dialect was not changed.
-   *
-   * @parameter property="hibernate.skip" default-value="${maven.test.skip}"
-   * @since 1.0
-   */
-  private boolean skip;
 
-  /**
-   * Force execution
-   * <p>
-   * Force execution, even if no modified or newly added annotated classes
-   * where found and the dialect was not changed.
-   * <p>
-   * <code>skip</code> takes precedence over <code>force</code>.
-   *
-   * @parameter property="hibernate.export.force" default-value="false"
-   * @since 1.0
-   */
-  private boolean force;
+  /** Conection parameters *************************************************/
 
   /**
    * SQL-Driver name.
@@ -213,29 +369,8 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
    */
   private String password;
 
-  /**
-   * Hibernate dialect.
-   *
-   * @parameter property="hibernate.dialect"
-   * @since 1.0
-   */
-  private String dialect;
 
-  /**
-   * Implicit naming strategy
-   *
-   * @parameter property=IMPLICIT_NAMING_STRATEGY
-   * @since 2.0
-   */
-  private String implicitNamingStrategy;
-
-  /**
-   * Physical naming strategy
-   *
-   * @parameter property=PHYSICAL_NAMING_STRATEGY
-   * @since 2.0
-   */
-  private String physicalNamingStrategy;
+  /** Parameters to locate configuration sources ****************************/
 
   /**
    * Path to a file or name of a ressource with hibernate properties.
@@ -302,8 +437,8 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
   private String mappings;
 
 
-  @Override
-  public final void execute()
+
+  public final void execute(String filename)
     throws
       MojoFailureException,
       MojoExecutionException
@@ -311,21 +446,21 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
     if (skip)
     {
       getLog().info("Execution of hibernate-maven-plugin was skipped!");
-      project.getProperties().setProperty(EXPORT_SKIPPED_PROPERTY, "true");
+      project.getProperties().setProperty(SKIPPED, "true");
       return;
     }
 
     ModificationTracker tracker;
     try
     {
-      tracker = new ModificationTracker(buildDirectory, getLog());
+      tracker = new ModificationTracker(buildDirectory, filename, getLog());
     }
     catch (NoSuchAlgorithmException e)
     {
       throw new MojoFailureException("Digest-Algorithm MD5 is missing!", e);
     }
 
-    SimpleConnectionProvider connectionProvider =
+    final SimpleConnectionProvider connectionProvider =
         new SimpleConnectionProvider(getLog());
 
     try
@@ -336,10 +471,13 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
       /** 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);
@@ -349,47 +487,177 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
 
       /** 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);
+      configure(properties, tracker);
 
       /** Check configuration for modifications */
-      if(tracker.check(properties))
+      if(tracker.track(properties))
         getLog().debug("Configuration has changed.");
       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);
 
-      /** Load Mappings */
-      MetadataSources sources = new MetadataSources(serviceRegistry);
-      addAnnotatedClasses(sources, classLoaderService, tracker);
+      /** 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() +
+              "\""
+              );
+      }
+
+      /** 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!");
-          project.getProperties().setProperty(EXPORT_SKIPPED_PROPERTY, "true");
+          project.getProperties().setProperty(SKIPPED, "true");
           return;
         }
       }
 
 
+      /** 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);
 
@@ -418,7 +686,88 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
             );
       }
 
-      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());
+          }
+          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
     {
@@ -434,46 +783,66 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
   }
 
 
-  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)
     {
@@ -520,7 +889,7 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
     }
   }
 
-  private Map loadConfig(ConfigLoader configLoader)
+  private LoadedConfig loadConfig(ConfigLoader configLoader)
       throws MojoExecutionException
   {
     /** Try to read configuration from configuration-file */
@@ -528,15 +897,12 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
     {
       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
@@ -547,13 +913,12 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
         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)
       {
@@ -563,170 +928,206 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
     }
   }
 
-  private void configure(Properties properties)
+  private void configure(Properties properties, ModificationTracker tracker)
       throws MojoFailureException
   {
-    /** Overwrite values from properties-file or set, if given */
-
-    if (driver != null)
+    /**
+     * Special treatment for the configuration-value "execute": if it is
+     * switched to "true", the genearation fo the schema should be forced!
+     */
+    if (tracker.check(EXECUTE, execute.toString()) && execute)
     {
-      if (properties.containsKey(DRIVER))
-        getLog().debug("Overwriting property " +
-            DRIVER + "=" + properties.getProperty(DRIVER) +
-            " with the value " + driver
+      getLog().info(
+          "hibernate.schema.execute was switched on: " +
+          "forcing generation/execution of SQL"
           );
-      else
-        getLog().debug("Using the value " + driver);
-      properties.setProperty(DRIVER, driver);
+      tracker.touch();
     }
-    if (properties.getProperty(DRIVER) == null)
+    configure(properties, execute, EXECUTE);
+
+    /**
+     * Configure the generation of the SQL.
+     * Overwrite values from properties-file if the configuration parameter is
+     * known to Hibernate.
+     */
+    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
+     * configured value should not lead to a regeneration of the database
+     * schama!
+     */
+    if (show == null)
+      show = Boolean.valueOf(properties.getProperty(SHOW_SQL));
+    else
+      properties.setProperty(SHOW_SQL, show.toString());
+
+    /**
+     * Configure the connection parameters.
+     * Overwrite values from properties-file.
+     */
+    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())
     {
-      String driver = properties.getProperty(JDBC_DRIVER);
-      if (driver != null)
-      {
-        getLog().info(DRIVER +
-            " is not set. Borrow setting from " +
-            JDBC_DRIVER +
-            ": " +
-            driver);
-        properties.setProperty(DRIVER, driver);
-      }
+      getLog().error("No properties set!");
+      throw new MojoFailureException("Hibernate configuration is missing!");
     }
 
-    if (url != null)
+    getLog().info("Gathered configuration:");
+    for (Entry<Object,Object> entry : properties.entrySet())
+      getLog().info("  " + entry.getKey() + " = " + entry.getValue());
+  }
+
+  private void configure(
+      Properties properties,
+      String value,
+      String key,
+      String alternativeKey
+      )
+  {
+    configure(properties, value, key);
+
+    if (properties.containsKey(alternativeKey))
     {
-      if (properties.containsKey(URL))
-        getLog().debug(
-            "Overwriting property " +
-            URL + "=" + properties.getProperty(URL) +
-            " with the value " + url
-          );
+      if (properties.containsKey(key))
+      {
+        getLog().warn(
+            "Ignoring property " + alternativeKey + "=\"" +
+            properties.getProperty(alternativeKey) +
+            "\" in favour for property " + key + "=\"" +
+            properties.getProperty(key) + "\""
+            );
+        properties.remove(alternativeKey);
+      }
       else
-        getLog().debug("Using the value " + url);
-      properties.setProperty(URL, url);
-    }
-    if (properties.getProperty(URL) == null)
-    {
-      String url = properties.getProperty(JDBC_URL);
-      if (url != null)
       {
-        getLog().info(URL +
-            " is not set. Borrow setting from " +
-            JDBC_URL +
-            ": " +
-            url);
-        properties.setProperty(URL, url);
+        value = properties.getProperty(alternativeKey);
+        properties.remove(alternativeKey);
+        getLog().info(
+            "Using value \"" + value + "\" from property " + alternativeKey +
+            " for property " + key
+            );
+        properties.setProperty(key, value);
       }
     }
+  }
 
-    if (username != null)
+  private void configure(Properties properties, String value, String key)
+  {
+    if (value != null)
     {
-      if (properties.containsKey(USER))
-        getLog().debug("Overwriting property " +
-            USER + "=" + properties.getProperty(USER) +
-            " with the value " + username
-          );
+      if (properties.containsKey(key))
+      {
+        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 " + username);
-      properties.setProperty(USER, username);
-    }
-    if (properties.getProperty(USER) == null)
-    {
-      username = properties.getProperty(JDBC_USER);
-      if (username != null)
       {
-        getLog().info(USER +
-            " is not set. Borrow setting from " +
-            JDBC_USER +
-            ": " +
-            username);
-        properties.setProperty(USER, username);
+        getLog().debug("Using value \"" + value + "\" for property " + key);
+        properties.setProperty(key, value);
       }
     }
+  }
+
+  private void configure(Properties properties, Boolean value, String key)
+  {
+    configure(properties, value == null ? null : value.toString(), key);
+  }
 
-    if (password != null)
+  private File getOutputFile(String filename)
+      throws
+        MojoExecutionException
+  {
+    File output = new File(filename);
+
+    if (!output.isAbsolute())
     {
-      if (properties.containsKey(PASS))
-        getLog().debug("Overwriting property " +
-            PASS + "=" + properties.getProperty(PASS) +
-            " with value " + password
-          );
-      else
-        getLog().debug("Using value " + password + " for property " + PASS);
-      properties.setProperty(PASS, password);
+      // Interpret relative file path relative to build directory
+      output = new File(buildDirectory, filename);
     }
-    if (properties.getProperty(PASS) == null)
+    getLog().debug("Output file: " + output.getPath());
+
+    // Ensure that directory path for specified file exists
+    File outFileParentDir = output.getParentFile();
+    if (null != outFileParentDir && !outFileParentDir.exists())
     {
-      password = properties.getProperty(JDBC_PASSWORD);
-      if (password != null)
+      try
       {
-        getLog().info(PASS +
-            " is not set. Borrow setting from " +
-            JDBC_PASSWORD +
-            ": " +
-            password);
-        properties.setProperty(PASS, password);
+        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);
       }
     }
 
-    if (dialect != null)
+    try
     {
-      if (properties.containsKey(DIALECT))
-        getLog().debug(
-            "Overwriting property " +
-            DIALECT + "=" + properties.getProperty(DIALECT) +
-            " with value " + dialect
-          );
-      else
-        getLog().debug(
-            "Using value " + dialect + " for property " + DIALECT
-            );
-      properties.setProperty(DIALECT, dialect);
+      output.createNewFile();
+    }
+    catch (IOException e)
+    {
+      String error = "Error creating output file: " + e.getMessage();
+      getLog().error(error);
+      throw new MojoExecutionException(error);
     }
 
-    if (implicitNamingStrategy != null )
+    if (!output.canWrite())
     {
-      if ( properties.contains(IMPLICIT_NAMING_STRATEGY))
-        getLog().debug(
-            "Overwriting property " +
-            IMPLICIT_NAMING_STRATEGY + "=" + properties.getProperty(IMPLICIT_NAMING_STRATEGY) +
-            " with value " + implicitNamingStrategy
-           );
-      else
-        getLog().debug(
-            "Using value " + implicitNamingStrategy + " for property " +
-            IMPLICIT_NAMING_STRATEGY +
-            "Using value " + dialect + " for property " + DIALECT
-            );
-      properties.setProperty(IMPLICIT_NAMING_STRATEGY, implicitNamingStrategy);
+      String error =
+          "Output file " + output.getAbsolutePath() + " is not writable!";
+      getLog().error(error);
+      throw new MojoExecutionException(error);
     }
 
-    if (physicalNamingStrategy != null )
+    return output;
+  }
+
+  private void checkOutputFile(File output, ModificationTracker tracker)
+      throws
+        MojoExecutionException
+  {
+    try
     {
-      if ( properties.contains(PHYSICAL_NAMING_STRATEGY))
-        getLog().debug(
-            "Overwriting property " +
-            PHYSICAL_NAMING_STRATEGY + "=" + properties.getProperty(PHYSICAL_NAMING_STRATEGY) +
-            " with value " + physicalNamingStrategy
-           );
+      if (output.exists())
+        tracker.track(SCRIPT, new FileInputStream(output));
       else
-        getLog().debug(
-            "Using value " + physicalNamingStrategy + " for property " +
-            PHYSICAL_NAMING_STRATEGY +
-            "Using value " + dialect + " for property " + DIALECT
-            );
-      properties.setProperty(PHYSICAL_NAMING_STRATEGY, physicalNamingStrategy);
+        tracker.track(SCRIPT, ZonedDateTime.now().toString());
     }
-
-    if (properties.isEmpty())
+    catch (IOException e)
     {
-      getLog().error("No properties set!");
-      throw new MojoFailureException("Hibernate configuration is missing!");
+      String error =
+          "Error while checking the generated script: " + e.getMessage();
+      getLog().error(error);
+      throw new MojoExecutionException(error);
     }
-
-    getLog().info("Gathered hibernate-configuration (turn on debugging for details):");
-    for (Entry<Object,Object> entry : properties.entrySet())
-      getLog().info("  " + entry.getKey() + " = " + entry.getValue());
   }
 
   private void addMappings(MetadataSources sources, ModificationTracker tracker)
@@ -756,10 +1157,10 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
             if (file.isDirectory())
               // TODO: add support to read all mappings under a directory
               throw new MojoFailureException(file.getAbsolutePath() + " is a directory");
-            if (tracker.check(filename, new FileInputStream(file)))
+            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);
           }
@@ -774,60 +1175,65 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
     }
   }
 
-  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());
       }
+    }
+    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()))
@@ -837,57 +1243,101 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
       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.check(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.check(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().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
-          getLog().debug("Unchanged class: " + name);
-        getLog().info("Adding annotated class " + annotatedClass);
-        sources.addAnnotatedClass(annotatedClass);
+        {
+          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
       )
@@ -900,48 +1350,41 @@ public abstract class AbstractSchemaMojo extends AbstractMojo
             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);