4135b618e360db7c1e3c6591c2a4e94353ff7145
[hibernate4-maven-plugin] / src / main / java / de / juplo / plugins / hibernate / AbstractSchemaMojo.java
1 package de.juplo.plugins.hibernate;
2
3
4 import com.pyx4j.log4j.MavenLogAppender;
5 import java.io.File;
6 import java.io.FileInputStream;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.net.URL;
10 import java.net.URLClassLoader;
11 import java.security.NoSuchAlgorithmException;
12 import java.util.Collections;
13 import java.util.HashSet;
14 import java.util.Iterator;
15 import java.util.LinkedList;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Map.Entry;
19 import java.util.Properties;
20 import java.util.Set;
21 import java.util.regex.Matcher;
22 import java.util.regex.Pattern;
23 import javax.persistence.Embeddable;
24 import javax.persistence.Entity;
25 import javax.persistence.MappedSuperclass;
26 import javax.persistence.spi.PersistenceUnitTransactionType;
27 import org.apache.maven.artifact.Artifact;
28 import org.apache.maven.model.Resource;
29 import org.apache.maven.plugin.AbstractMojo;
30 import org.apache.maven.plugin.MojoExecutionException;
31 import org.apache.maven.plugin.MojoFailureException;
32 import org.apache.maven.project.MavenProject;
33 import org.hibernate.boot.MetadataBuilder;
34 import org.hibernate.boot.MetadataSources;
35 import org.hibernate.boot.cfgxml.internal.ConfigLoader;
36 import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
37 import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
38 import org.hibernate.boot.registry.BootstrapServiceRegistry;
39 import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
40 import org.hibernate.boot.registry.StandardServiceRegistry;
41 import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
42 import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
43 import org.hibernate.boot.registry.selector.spi.StrategySelector;
44 import org.hibernate.boot.spi.MetadataImplementor;
45 import static org.hibernate.cfg.AvailableSettings.DIALECT;
46 import static org.hibernate.cfg.AvailableSettings.DRIVER;
47 import static org.hibernate.cfg.AvailableSettings.IMPLICIT_NAMING_STRATEGY;
48 import static org.hibernate.cfg.AvailableSettings.PASS;
49 import static org.hibernate.cfg.AvailableSettings.PHYSICAL_NAMING_STRATEGY;
50 import static org.hibernate.cfg.AvailableSettings.USER;
51 import static org.hibernate.cfg.AvailableSettings.URL;
52 import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
53 import org.hibernate.internal.util.config.ConfigurationException;
54 import static org.hibernate.jpa.AvailableSettings.JDBC_DRIVER;
55 import static org.hibernate.jpa.AvailableSettings.JDBC_PASSWORD;
56 import static org.hibernate.jpa.AvailableSettings.JDBC_URL;
57 import static org.hibernate.jpa.AvailableSettings.JDBC_USER;
58 import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor;
59 import org.hibernate.jpa.boot.internal.PersistenceXmlParser;
60 import org.hibernate.jpa.boot.spi.ProviderChecker;
61 import org.scannotation.AnnotationDB;
62
63
64 /**
65  * Baseclass with common attributes and methods.
66  *
67  * @phase process-classes
68  * @threadSafe
69  * @requiresDependencyResolution runtime
70  */
71 public abstract class AbstractSchemaMojo extends AbstractMojo
72 {
73   public final static String EXPORT_SKIPPED_PROPERTY = "hibernate.export.skipped";
74
75   private final static Pattern SPLIT = Pattern.compile("[^,\\s]+");
76
77
78   /**
79    * The maven project.
80    * <p>
81    * Only needed internally.
82    *
83    * @parameter property="project"
84    * @required
85    * @readonly
86    */
87   private MavenProject project;
88
89   /**
90    * Build-directory.
91    * <p>
92    * Only needed internally.
93    *
94    * @parameter property="project.build.directory"
95    * @required
96    * @readonly
97    */
98   String buildDirectory;
99
100   /**
101    * Classes-Directory to scan.
102    * <p>
103    * This parameter defaults to the maven build-output-directory for classes.
104    * Additionally, all dependencies are scanned for annotated classes.
105    *
106    * @parameter property="project.build.outputDirectory"
107    * @since 1.0
108    */
109   private String outputDirectory;
110
111   /**
112    * Whether to scan test-classes too, or not.
113    * <p>
114    * If this parameter is set to <code>true</code> the test-classes of the
115    * artifact will be scanned for hibernate-annotated classes additionally.
116    *
117    * @parameter property="hibernate.export.scan_testclasses" default-value="false"
118    * @since 1.0.1
119    */
120   private boolean scanTestClasses;
121
122   /**
123    * Dependency-Scopes, that should be scanned for annotated classes.
124    * <p>
125    * By default, only dependencies in the scope <code>compile</code> are
126    * scanned for annotated classes. Multiple scopes can be seperated by
127    * white space or commas.
128    * <p>md5s
129    * If you do not want any dependencies to be scanned for annotated
130    * classes, set this parameter to <code>none</code>.
131    * <p>
132    * The plugin does not scan for annotated classes in transitive
133    * dependencies. If some of your annotated classes are hidden in a
134    * transitive dependency, you can simply add that dependency explicitly.
135    *
136    * @parameter property="hibernate.export.scan_dependencies" default-value="compile"
137    * @since 1.0.3
138    */
139   private String scanDependencies;
140
141   /**
142    * Test-Classes-Directory to scan.
143    * <p>
144    * This parameter defaults to the maven build-output-directory for
145    * test-classes.
146    * <p>
147    * This parameter is only used, when <code>scanTestClasses</code> is set
148    * to <code>true</code>!
149    *
150    * @parameter property="project.build.testOutputDirectory"
151    * @since 1.0.2
152    */
153   private String testOutputDirectory;
154
155   /**
156    * Skip execution
157    * <p>
158    * If set to <code>true</code>, the execution is skipped.
159    * <p>
160    * A skipped execution is signaled via the maven-property
161    * <code>${hibernate.export.skipped}</code>.
162    * <p>
163    * The execution is skipped automatically, if no modified or newly added
164    * annotated classes are found and the dialect was not changed.
165    *
166    * @parameter property="hibernate.skip" default-value="${maven.test.skip}"
167    * @since 1.0
168    */
169   private boolean skip;
170
171   /**
172    * Force execution
173    * <p>
174    * Force execution, even if no modified or newly added annotated classes
175    * where found and the dialect was not changed.
176    * <p>
177    * <code>skip</code> takes precedence over <code>force</code>.
178    *
179    * @parameter property="hibernate.export.force" default-value="false"
180    * @since 1.0
181    */
182   private boolean force;
183
184   /**
185    * SQL-Driver name.
186    *
187    * @parameter property="hibernate.connection.driver_class"
188    * @since 1.0
189    */
190   private String driver;
191
192   /**
193    * Database URL.
194    *
195    * @parameter property="hibernate.connection.url"
196    * @since 1.0
197    */
198   private String url;
199
200   /**
201    * Database username
202    *
203    * @parameter property="hibernate.connection.username"
204    * @since 1.0
205    */
206   private String username;
207
208   /**
209    * Database password
210    *
211    * @parameter property="hibernate.connection.password"
212    * @since 1.0
213    */
214   private String password;
215
216   /**
217    * Hibernate dialect.
218    *
219    * @parameter property="hibernate.dialect"
220    * @since 1.0
221    */
222   private String dialect;
223
224   /**
225    * Implicit naming strategy
226    *
227    * @parameter property=IMPLICIT_NAMING_STRATEGY
228    * @since 2.0
229    */
230   private String implicitNamingStrategy;
231
232   /**
233    * Physical naming strategy
234    *
235    * @parameter property=PHYSICAL_NAMING_STRATEGY
236    * @since 2.0
237    */
238   private String physicalNamingStrategy;
239
240   /**
241    * Path to a file or name of a ressource with hibernate properties.
242    * If this parameter is specified, the plugin will try to load configuration
243    * values from a file with the given path or a ressource on the classpath with
244    * the given name. If both fails, the execution of the plugin will fail.
245    * <p>
246    * If this parameter is not set the plugin will load configuration values
247    * from a ressource named <code>hibernate.properties</code> on the classpath,
248    * if it is present, but will not fail if there is no such ressource.
249    * <p>
250    * During ressource-lookup, the test-classpath takes precedence.
251    *
252    * @parameter
253    * @since 1.0
254    */
255   private String hibernateProperties;
256
257   /**
258    * Path to Hibernate configuration file (.cfg.xml).
259    * If this parameter is specified, the plugin will try to load configuration
260    * values from a file with the given path or a ressource on the classpath with
261    * the given name. If both fails, the execution of the plugin will fail.
262    * <p>
263    * If this parameter is not set the plugin will load configuration values
264    * from a ressource named <code>hibernate.cfg.xml</code> on the classpath,
265    * if it is present, but will not fail if there is no such ressource.
266    * <p>
267    * During ressource-lookup, the test-classpath takes precedence.
268    * <p>
269    * Settings in this file will overwrite settings in the properties file.
270    *
271    * @parameter
272    * @since 1.1.0
273    */
274   private String hibernateConfig;
275
276   /**
277    * Name of the persistence-unit.
278    * If this parameter is specified, the plugin will try to load configuration
279    * values from a persistence-unit with the specified name. If no such
280    * persistence-unit can be found, the plugin will throw an exception.
281    * <p>
282    * If this parameter is not set and there is only one persistence-unit
283    * available, that unit will be used automatically. But if this parameter is
284    * not set and there are multiple persistence-units available on,
285    * the class-path, the execution of the plugin will fail.
286    * <p>
287    * Settings in this file will overwrite settings in the properties or the
288    * configuration file.
289    *
290    * @parameter
291    * @since 1.1.0
292    */
293   private String persistenceUnit;
294
295   /**
296    * List of Hibernate-Mapping-Files (XML).
297    * Multiple files can be separated with white-spaces and/or commas.
298    *
299    * @parameter property="hibernate.mapping"
300    * @since 1.0.2
301    */
302   private String mappings;
303
304
305   @Override
306   public final void execute()
307     throws
308       MojoFailureException,
309       MojoExecutionException
310   {
311     if (skip)
312     {
313       getLog().info("Execution of hibernate-maven-plugin was skipped!");
314       project.getProperties().setProperty(EXPORT_SKIPPED_PROPERTY, "true");
315       return;
316     }
317
318     ModificationTracker tracker;
319     try
320     {
321       tracker = new ModificationTracker(buildDirectory, getLog());
322     }
323     catch (NoSuchAlgorithmException e)
324     {
325       throw new MojoFailureException("Digest-Algorithm MD5 is missing!", e);
326     }
327
328     SimpleConnectionProvider connectionProvider =
329         new SimpleConnectionProvider(getLog());
330
331     try
332     {
333       /** Start extended logging */
334       MavenLogAppender.startPluginLog(this);
335
336       /** Load checksums for old mapping and configuration */
337       tracker.load();
338
339       /** Create a BootstrapServiceRegistry with special ClassLoader */
340       BootstrapServiceRegistry bootstrapServiceRegitry =
341           new BootstrapServiceRegistryBuilder()
342               .applyClassLoader(createClassLoader())
343               .build();
344       ClassLoaderService classLoaderService =
345           bootstrapServiceRegitry.getService(ClassLoaderService.class);
346
347       Properties properties = new Properties();
348       ConfigLoader configLoader = new ConfigLoader(bootstrapServiceRegitry);
349
350       /** Loading and merging configuration */
351       properties.putAll(loadProperties(configLoader));
352       properties.putAll(loadConfig(configLoader));
353       properties.putAll(loadPersistenceUnit(classLoaderService, properties));
354
355       /** Overwriting/Completing configuration */
356       configure(properties);
357
358       /** Check configuration for modifications */
359       if(tracker.check(properties))
360         getLog().debug("Configuration has changed.");
361       else
362         getLog().debug("Configuration unchanged.");
363
364       /** Configure Hibernate */
365       StandardServiceRegistry serviceRegistry =
366           new StandardServiceRegistryBuilder(bootstrapServiceRegitry)
367               .applySettings(properties)
368               .addService(ConnectionProvider.class, connectionProvider)
369               .build();
370
371       /** Load Mappings */
372       MetadataSources sources = new MetadataSources(serviceRegistry);
373       addAnnotatedClasses(sources, classLoaderService, tracker);
374       addMappings(sources, tracker);
375
376       /** Skip execution, if mapping and configuration is unchanged */
377       if (!tracker.modified())
378       {
379         getLog().info(
380             "Mapping and configuration unchanged."
381             );
382         if (force)
383           getLog().info("Schema generation is forced!");
384         else
385         {
386           getLog().info("Skipping schema generation!");
387           project.getProperties().setProperty(EXPORT_SKIPPED_PROPERTY, "true");
388           return;
389         }
390       }
391
392
393       /** Create a connection, if sufficient configuration infromation is available */
394       connectionProvider.open(classLoaderService, properties);
395
396       MetadataBuilder metadataBuilder = sources.getMetadataBuilder();
397
398       StrategySelector strategySelector =
399           serviceRegistry.getService(StrategySelector.class);
400
401       if (properties.containsKey(IMPLICIT_NAMING_STRATEGY))
402       {
403         metadataBuilder.applyImplicitNamingStrategy(
404             strategySelector.resolveStrategy(
405                 ImplicitNamingStrategy.class,
406                 properties.getProperty(IMPLICIT_NAMING_STRATEGY)
407                 )
408             );
409       }
410
411       if (properties.containsKey(PHYSICAL_NAMING_STRATEGY))
412       {
413         metadataBuilder.applyPhysicalNamingStrategy(
414             strategySelector.resolveStrategy(
415                 PhysicalNamingStrategy.class,
416                 properties.getProperty(PHYSICAL_NAMING_STRATEGY)
417                 )
418             );
419       }
420
421       build((MetadataImplementor)metadataBuilder.build());
422     }
423     finally
424     {
425       /** Remember mappings and configuration */
426       tracker.save();
427
428       /** Close the connection - if one was opened */
429       connectionProvider.close();
430
431       /** Stop Log-Capturing */
432       MavenLogAppender.endPluginLog(this);
433     }
434   }
435
436
437   abstract void build(MetadataImplementor metadata)
438     throws
439       MojoFailureException,
440       MojoExecutionException;
441
442
443   private URLClassLoader createClassLoader() throws MojoExecutionException
444   {
445     try
446     {
447       getLog().debug("Creating ClassLoader for project-dependencies...");
448       List<String> classpathFiles = project.getCompileClasspathElements();
449       if (scanTestClasses)
450         classpathFiles.addAll(project.getTestClasspathElements());
451       List<URL> urls = new LinkedList<URL>();
452       File file;
453       file = new File(testOutputDirectory);
454       if (!file.exists())
455       {
456         getLog().info("creating test-output-directory: " + testOutputDirectory);
457         file.mkdirs();
458       }
459       urls.add(file.toURI().toURL());
460       file = new File(outputDirectory);
461       if (!file.exists())
462       {
463         getLog().info("creating output-directory: " + outputDirectory);
464         file.mkdirs();
465       }
466       urls.add(file.toURI().toURL());
467       for (String pathElement : classpathFiles)
468       {
469         getLog().debug("Dependency: " + pathElement);
470         urls.add(new File(pathElement).toURI().toURL());
471       }
472       return
473           new URLClassLoader(
474               urls.toArray(new URL[urls.size()]),
475               getClass().getClassLoader()
476               );
477     }
478     catch (Exception e)
479     {
480       getLog().error("Error while creating ClassLoader!", e);
481       throw new MojoExecutionException(e.getMessage());
482     }
483   }
484
485   private Map loadProperties(ConfigLoader configLoader)
486       throws
487         MojoExecutionException
488   {
489     /** Try to read configuration from properties-file */
490     if (hibernateProperties == null)
491     {
492       try
493       {
494         return configLoader.loadProperties("hibernate.properties");
495       }
496       catch (ConfigurationException e)
497       {
498         getLog().debug(e.getMessage());
499         return Collections.EMPTY_MAP;
500       }
501     }
502     else
503     {
504       try
505       {
506         File file = new File(hibernateProperties);
507         if (file.exists())
508         {
509           getLog().info("Reading settings from file " + hibernateProperties + "...");
510           return configLoader.loadProperties(file);
511         }
512         else
513           return configLoader.loadProperties(hibernateProperties);
514       }
515       catch (ConfigurationException e)
516       {
517         getLog().error("Error while reading properties!", e);
518         throw new MojoExecutionException(e.getMessage());
519       }
520     }
521   }
522
523   private Map loadConfig(ConfigLoader configLoader)
524       throws MojoExecutionException
525   {
526     /** Try to read configuration from configuration-file */
527     if (hibernateConfig == null)
528     {
529       try
530       {
531         return
532             configLoader
533                 .loadConfigXmlResource("hibernate.cfg.xml")
534                 .getConfigurationValues();
535       }
536       catch (ConfigurationException e)
537       {
538         getLog().debug(e.getMessage());
539         return Collections.EMPTY_MAP;
540       }
541     }
542     else
543     {
544       try
545       {
546         File file = new File(hibernateConfig);
547         if (file.exists())
548         {
549           getLog().info("Reading configuration from file " + hibernateConfig + "...");
550           return configLoader.loadConfigXmlFile(file).getConfigurationValues();
551         }
552         else
553           return
554               configLoader
555                   .loadConfigXmlResource(hibernateConfig)
556                   .getConfigurationValues();
557       }
558       catch (ConfigurationException e)
559       {
560         getLog().error("Error while reading configuration!", e);
561         throw new MojoExecutionException(e.getMessage());
562       }
563     }
564   }
565
566   private void configure(Properties properties)
567       throws MojoFailureException
568   {
569     /** Overwrite values from properties-file or set, if given */
570
571     configure(properties, driver, DRIVER, JDBC_DRIVER);
572     configure(properties, url, URL, JDBC_URL);
573     configure(properties, username, USER, JDBC_USER);
574     configure(properties, password, PASS, JDBC_PASSWORD);
575     configure(properties, dialect, DIALECT);
576     configure(properties, implicitNamingStrategy, IMPLICIT_NAMING_STRATEGY);
577     configure(properties, physicalNamingStrategy, PHYSICAL_NAMING_STRATEGY);
578
579     if (properties.isEmpty())
580     {
581       getLog().error("No properties set!");
582       throw new MojoFailureException("Hibernate configuration is missing!");
583     }
584
585     getLog().info("Gathered hibernate-configuration (turn on debugging for details):");
586     for (Entry<Object,Object> entry : properties.entrySet())
587       getLog().info("  " + entry.getKey() + " = " + entry.getValue());
588   }
589
590   private void configure(
591       Properties properties,
592       String value,
593       String key,
594       String alternativeKey
595       )
596   {
597     configure(properties, value, key);
598     if (properties.containsKey(key) && properties.containsKey(alternativeKey))
599     {
600       getLog().warn(
601           "Ignoring property " + alternativeKey + "=" +
602           properties.getProperty(alternativeKey) + " in favour for property " +
603           key + "=" + properties.getProperty(key)
604           );
605       properties.remove(JDBC_DRIVER);
606     }
607   }
608
609   private void configure(Properties properties, String value, String key)
610   {
611     if (value != null)
612     {
613       if (properties.containsKey(key))
614         getLog().debug(
615             "Overwriting property " + key + "=" + properties.getProperty(key) +
616             " with the value " + value
617             );
618       else
619         getLog().debug("Using the value " + value + " for property " + key);
620       properties.setProperty(key, value);
621     }
622   }
623
624   private void addMappings(MetadataSources sources, ModificationTracker tracker)
625       throws MojoFailureException
626   {
627     getLog().debug("Adding explicitly configured mappings...");
628     if (mappings != null)
629     {
630       try
631       {
632         for (String filename : mappings.split("[\\s,]+"))
633         {
634           // First try the filename as absolute/relative path
635           File file = new File(filename);
636           if (!file.exists())
637           {
638             // If the file was not found, search for it in the resource-directories
639             for (Resource resource : project.getResources())
640             {
641               file = new File(resource.getDirectory() + File.separator + filename);
642               if (file.exists())
643                 break;
644             }
645           }
646           if (file.exists())
647           {
648             if (file.isDirectory())
649               // TODO: add support to read all mappings under a directory
650               throw new MojoFailureException(file.getAbsolutePath() + " is a directory");
651             if (tracker.check(filename, new FileInputStream(file)))
652               getLog().debug("Found new or modified mapping-file: " + filename);
653             else
654               getLog().debug("mapping-file unchanged: " + filename);
655
656             sources.addFile(file);
657           }
658           else
659             throw new MojoFailureException("File " + filename + " could not be found in any of the configured resource-directories!");
660         }
661       }
662       catch (IOException e)
663       {
664         throw new MojoFailureException("Cannot calculate MD5 sums!", e);
665       }
666     }
667   }
668
669   private void addAnnotatedClasses(
670       MetadataSources sources,
671       ClassLoaderService classLoaderService,
672       ModificationTracker tracker
673       )
674       throws
675         MojoFailureException,
676         MojoExecutionException
677
678   {
679     try
680     {
681       AnnotationDB db = new AnnotationDB();
682       File dir;
683
684       dir = new File(outputDirectory);
685       if (dir.exists())
686       {
687         getLog().info("Scanning directory " + dir.getAbsolutePath() + " for annotated classes...");
688         URL dirUrl = dir.toURI().toURL();
689         db.scanArchives(dirUrl);
690       }
691
692       if (scanTestClasses)
693       {
694         dir = new File(testOutputDirectory);
695         if (dir.exists())
696         {
697           getLog().info("Scanning directory " + dir.getAbsolutePath() + " for annotated classes...");
698           URL dirUrl = dir.toURI().toURL();
699           db.scanArchives(dirUrl);
700         }
701       }
702
703       if (scanDependencies != null)
704       {
705         Matcher matcher = SPLIT.matcher(scanDependencies);
706         while (matcher.find())
707         {
708           getLog().info("Scanning dependencies for scope " + matcher.group());
709           for (Artifact artifact : project.getDependencyArtifacts())
710           {
711             if (!artifact.getScope().equalsIgnoreCase(matcher.group()))
712               continue;
713             if (artifact.getFile() == null)
714             {
715               getLog().warn("Cannot scan dependency " + artifact.getId() + ": no JAR-file available!");
716               continue;
717             }
718             getLog().info("Scanning dependency " + artifact.getId() + " for annotated classes...");
719             db.scanArchives(artifact.getFile().toURI().toURL());
720           }
721         }
722       }
723
724       Set<String> classes = new HashSet<String>();
725       if (db.getAnnotationIndex().containsKey(Entity.class.getName()))
726         classes.addAll(db.getAnnotationIndex().get(Entity.class.getName()));
727       if (db.getAnnotationIndex().containsKey(MappedSuperclass.class.getName()))
728         classes.addAll(db.getAnnotationIndex().get(MappedSuperclass.class.getName()));
729       if (db.getAnnotationIndex().containsKey(Embeddable.class.getName()))
730         classes.addAll(db.getAnnotationIndex().get(Embeddable.class.getName()));
731
732       Set<String> packages = new HashSet<String>();
733
734       for (String name : classes)
735       {
736         Class<?> annotatedClass = classLoaderService.classForName(name);
737         String packageName = annotatedClass.getPackage().getName();
738         if (!packages.contains(packageName))
739         {
740           InputStream is =
741               annotatedClass.getResourceAsStream("package-info.class");
742           if (is == null)
743           {
744             // No compiled package-info available: no package-level annotations!
745             getLog().debug("Package " + packageName + " is not annotated.");
746           }
747           else
748           {
749             if (tracker.check(packageName, is))
750               getLog().debug("New or modified package: " + packageName);
751             else
752               getLog().debug("Unchanged package: " + packageName);
753             getLog().info("Adding annotated package " + packageName);
754             sources.addPackage(packageName);
755           }
756           packages.add(packageName);
757         }
758         String resourceName = annotatedClass.getName();
759         resourceName =
760             resourceName.substring(
761                 resourceName.lastIndexOf(".") + 1,
762                 resourceName.length()
763                 ) + ".class";
764         InputStream is =
765             annotatedClass
766                 .getResourceAsStream(resourceName);
767         if (tracker.check(name, is))
768           getLog().debug("New or modified class: " + name);
769         else
770           getLog().debug("Unchanged class: " + name);
771         getLog().info("Adding annotated class " + annotatedClass);
772         sources.addAnnotatedClass(annotatedClass);
773       }
774     }
775     catch (Exception e)
776     {
777       getLog().error("Error while scanning!", e);
778       throw new MojoFailureException(e.getMessage());
779     }
780   }
781
782   private Properties loadPersistenceUnit(
783       ClassLoaderService classLoaderService,
784       Properties properties
785       )
786       throws
787         MojoFailureException
788   {
789     PersistenceXmlParser parser =
790         new PersistenceXmlParser(
791             classLoaderService,
792             PersistenceUnitTransactionType.RESOURCE_LOCAL
793              );
794
795     List<ParsedPersistenceXmlDescriptor> units = parser.doResolve(properties);
796
797     if (persistenceUnit == null)
798     {
799       switch (units.size())
800       {
801         case 0:
802           getLog().info("Found no META-INF/persistence.xml.");
803           return new Properties();
804         case 1:
805           getLog().info("Using persistence-unit " + units.get(0).getName());
806           return units.get(0).getProperties();
807         default:
808           StringBuilder builder = new StringBuilder();
809           builder.append("No name provided and multiple persistence units found: ");
810           Iterator<ParsedPersistenceXmlDescriptor> it = units.iterator();
811           builder.append(it.next().getName());
812           while (it.hasNext())
813           {
814             builder.append(", ");
815             builder.append(it.next().getName());
816           }
817           builder.append('.');
818           throw new MojoFailureException(builder.toString());
819       }
820     }
821
822     for (ParsedPersistenceXmlDescriptor unit : units)
823     {
824       getLog().debug("Found persistence-unit " + unit.getName());
825       if (!unit.getName().equals(persistenceUnit))
826         continue;
827
828       // See if we (Hibernate) are the persistence provider
829       if (!ProviderChecker.isProvider(unit, properties))
830       {
831         getLog().debug("Wrong provider: " + unit.getProviderClassName());
832         continue;
833       }
834
835       getLog().info("Using persistence-unit " + unit.getName());
836       return unit.getProperties();
837     }
838
839     throw new MojoFailureException("Could not find persistence-unit " + persistenceUnit);
840   }
841 }