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.FileOutputStream;
8 import java.io.IOException;
9 import java.io.InputStream;
10 import java.net.MalformedURLException;
11 import java.net.URL;
12 import java.security.NoSuchAlgorithmException;
13 import java.time.ZonedDateTime;
14 import java.util.Collections;
15 import java.util.EnumSet;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.Iterator;
19 import java.util.LinkedHashSet;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Map.Entry;
23 import java.util.Properties;
24 import java.util.Set;
25 import java.util.regex.Matcher;
26 import java.util.regex.Pattern;
27 import javax.persistence.Embeddable;
28 import javax.persistence.Entity;
29 import javax.persistence.MappedSuperclass;
30 import javax.persistence.spi.PersistenceUnitTransactionType;
31 import org.apache.maven.artifact.Artifact;
32 import org.apache.maven.model.Resource;
33 import org.apache.maven.plugin.AbstractMojo;
34 import org.apache.maven.plugin.MojoExecutionException;
35 import org.apache.maven.plugin.MojoFailureException;
36 import org.apache.maven.project.MavenProject;
37 import org.hibernate.boot.MetadataBuilder;
38 import org.hibernate.boot.MetadataSources;
39 import org.hibernate.boot.cfgxml.internal.ConfigLoader;
40 import org.hibernate.boot.cfgxml.spi.LoadedConfig;
41 import org.hibernate.boot.cfgxml.spi.MappingReference;
42 import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
43 import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
44 import org.hibernate.boot.registry.BootstrapServiceRegistry;
45 import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
46 import org.hibernate.boot.registry.StandardServiceRegistry;
47 import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
48 import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
49 import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
50 import org.hibernate.boot.registry.selector.spi.StrategySelector;
51 import org.hibernate.boot.spi.MetadataImplementor;
52 import org.hibernate.cfg.AvailableSettings;
53 import static org.hibernate.cfg.AvailableSettings.DIALECT;
54 import static org.hibernate.cfg.AvailableSettings.DRIVER;
55 import static org.hibernate.cfg.AvailableSettings.FORMAT_SQL;
56 import static org.hibernate.cfg.AvailableSettings.HBM2DDL_DELIMITER;
57 import static org.hibernate.cfg.AvailableSettings.HBM2DLL_CREATE_NAMESPACES;
58 import static org.hibernate.cfg.AvailableSettings.IMPLICIT_NAMING_STRATEGY;
59 import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_DRIVER;
60 import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_PASSWORD;
61 import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_URL;
62 import static org.hibernate.cfg.AvailableSettings.JPA_JDBC_USER;
63 import static org.hibernate.cfg.AvailableSettings.PASS;
64 import static org.hibernate.cfg.AvailableSettings.PHYSICAL_NAMING_STRATEGY;
65 import static org.hibernate.cfg.AvailableSettings.SHOW_SQL;
66 import static org.hibernate.cfg.AvailableSettings.USER;
67 import static org.hibernate.cfg.AvailableSettings.URL;
68 import org.hibernate.engine.config.spi.ConfigurationService;
69 import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
70 import org.hibernate.internal.util.config.ConfigurationException;
71 import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor;
72 import org.hibernate.jpa.boot.internal.PersistenceXmlParser;
73 import org.hibernate.tool.schema.TargetType;
74 import org.hibernate.tool.schema.internal.ExceptionHandlerCollectingImpl;
75 import org.hibernate.tool.schema.internal.exec.ScriptTargetOutputToFile;
76 import org.hibernate.tool.schema.spi.ExecutionOptions;
77 import org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator;
78 import org.hibernate.tool.schema.spi.ScriptTargetOutput;
79 import org.hibernate.tool.schema.spi.TargetDescriptor;
80 import org.scannotation.AnnotationDB;
81
82
83
84
85
86
87
88
89
90 public abstract class AbstractSchemaMojo extends AbstractMojo
91 {
92 public final static String EXECUTE = "hibernate.schema.execute";
93 public final static String OUTPUTDIRECTORY = "project.build.outputDirectory";
94 public final static String SCAN_CLASSES = "hibernate.schema.scan.classes";
95 public final static String SCAN_DEPENDENCIES = "hibernate.schema.scan.dependencies";
96 public final static String SCAN_TESTCLASSES = "hibernate.schema.scan.test_classes";
97 public final static String TEST_OUTPUTDIRECTORY = "project.build.testOutputDirectory";
98 public final static String SKIPPED = "hibernate.schema.skipped";
99 public final static String SCRIPT = "hibernate.schema.script";
100
101 private final static Pattern SPLIT = Pattern.compile("[^,\\s]+");
102
103 private final Set<String> packages = new HashSet<String>();
104
105
106
107
108
109
110
111
112
113
114
115 private MavenProject project;
116
117
118
119
120
121
122
123
124
125
126 private String buildDirectory;
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145 private Boolean execute;
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167 private boolean skip;
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187 private boolean force;
188
189
190
191
192
193
194
195 private String dialect;
196
197
198
199
200
201
202
203
204
205
206
207
208
209 private String delimiter;
210
211
212
213
214
215
216
217 private Boolean show;
218
219
220
221
222
223
224
225 private Boolean format;
226
227
228
229
230
231
232
233 private Boolean createNamespaces;
234
235
236
237
238
239
240
241 private String implicitNamingStrategy;
242
243
244
245
246
247
248
249 private String physicalNamingStrategy;
250
251
252
253
254
255
256
257
258
259
260
261 private Boolean scanClasses;
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278 private String outputDirectory;
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297 private String scanDependencies;
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315 private Boolean scanTestClasses;
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335 private String testOutputDirectory;
336
337
338
339
340
341
342
343
344
345
346 private String driver;
347
348
349
350
351
352
353
354 private String url;
355
356
357
358
359
360
361
362 private String username;
363
364
365
366
367
368
369
370 private String password;
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390 private String hibernateProperties;
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409 private String hibernateConfig;
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428 private String persistenceUnit;
429
430
431
432
433
434
435
436
437 private String mappings;
438
439
440
441 public final void execute(String filename)
442 throws
443 MojoFailureException,
444 MojoExecutionException
445 {
446 if (skip)
447 {
448 getLog().info("Execution of hibernate-maven-plugin was skipped!");
449 project.getProperties().setProperty(SKIPPED, "true");
450 return;
451 }
452
453 ModificationTracker tracker;
454 try
455 {
456 tracker = new ModificationTracker(buildDirectory, filename, getLog());
457 }
458 catch (NoSuchAlgorithmException e)
459 {
460 throw new MojoFailureException("Digest-Algorithm MD5 is missing!", e);
461 }
462
463 final SimpleConnectionProvider connectionProvider =
464 new SimpleConnectionProvider(getLog());
465
466 try
467 {
468
469 MavenLogAppender.startPluginLog(this);
470
471
472 tracker.load();
473
474
475 MutableClassLoader classLoader = createClassLoader();
476
477
478 BootstrapServiceRegistry bootstrapServiceRegitry =
479 new BootstrapServiceRegistryBuilder()
480 .applyClassLoader(classLoader)
481 .build();
482 ClassLoaderService classLoaderService =
483 bootstrapServiceRegitry.getService(ClassLoaderService.class);
484
485 Properties properties = new Properties();
486 ConfigLoader configLoader = new ConfigLoader(bootstrapServiceRegitry);
487
488
489 properties.putAll(loadProperties(configLoader));
490 LoadedConfig config = loadConfig(configLoader);
491 if (config != null)
492 properties.putAll(config.getConfigurationValues());
493
494
495 addDirectDependenciesClassPath(classLoader);
496
497
498 ParsedPersistenceXmlDescriptor unit =
499 loadPersistenceUnit(classLoaderService, properties);
500 if (unit != null)
501 properties.putAll(unit.getProperties());
502
503
504 configure(properties, tracker);
505
506
507 if(tracker.track(properties))
508 getLog().debug("Configuration has changed.");
509 else
510 getLog().debug("Configuration unchanged.");
511
512
513 final File output = getOutputFile(filename);
514
515 checkOutputFile(output, tracker);
516
517
518 final StandardServiceRegistry serviceRegistry =
519 new StandardServiceRegistryBuilder(bootstrapServiceRegitry)
520 .applySettings(properties)
521 .addService(ConnectionProvider.class, connectionProvider)
522 .build();
523 final MetadataSources sources = new MetadataSources(serviceRegistry);
524
525
526 addAllDependenciesToClassPath(classLoader);
527
528
529 if (config != null)
530 {
531 for (MappingReference mapping : config.getMappingReferences())
532 mapping.apply(sources);
533 }
534
535 Set<String> classes;
536 if (unit == null)
537 {
538
539 if (scanClasses == null)
540 scanClasses = true;
541 Set<URL> urls = new HashSet<URL>();
542 getLog().debug("Compiling the dependencies, that are scanned for annotated classes");
543 if (scanClasses)
544 addRoot(urls, outputDirectory);
545 if (scanTestClasses)
546 addRoot(urls, testOutputDirectory);
547 addDependencies(urls);
548 classes = scanUrls(urls);
549 }
550 else
551 {
552
553 if (scanClasses == null)
554 scanClasses = !unit.isExcludeUnlistedClasses();
555 getLog().debug("Compiling the dependencies, that are scanned for annotated classes");
556
557 Set<URL> urls = new HashSet<URL>();
558 if (scanClasses)
559 {
560 getLog().debug("Only dependencies relative to persistent-unit " + unit.getName() + " are scanned!");
561
562
563
564
565 getLog().debug(" - adding " + unit.getPersistenceUnitRootUrl());
566 urls.add(unit.getPersistenceUnitRootUrl());
567 for (URL url : unit.getJarFileUrls())
568 {
569 getLog().debug(" - adding " + url);
570 urls.add(url);
571 }
572 if (scanTestClasses)
573 addRoot(urls, testOutputDirectory);
574 }
575 else
576 getLog().debug("Scanning of unlisted classes is prohibited in persistent-unit " + unit.getName());
577 classes = scanUrls(urls);
578 for (String className : unit.getManagedClassNames())
579 classes.add(className);
580
581
582
583
584 boolean error = false;
585 InputStream is;
586 is = classLoader.getResourceAsStream("META-INF/orm.xml");
587 if (is != null)
588 {
589 getLog().info("Adding default JPA-XML-mapping from META-INF/orm.xml");
590 try
591 {
592 tracker.track("META-INF/orm.xml", is);
593 sources.addResource("META-INF/orm.xml");
594 }
595 catch (IOException e)
596 {
597 getLog().error("cannot read META-INF/orm.xml: " + e);
598 error = true;
599 }
600 }
601 else
602 {
603 getLog().debug("no META-INF/orm.xml found");
604 }
605
606
607
608
609 getLog().info("Adding mappings from persistence-unit " + unit.getName());
610 for (String mapping : unit.getMappingFileNames())
611 {
612 getLog().info(" - adding " + mapping);
613 is = classLoader.getResourceAsStream(mapping);
614 if (is != null)
615 {
616 try
617 {
618 tracker.track(mapping, is);
619 sources.addResource(mapping);
620 }
621 catch (IOException e)
622 {
623 getLog().info("cannot read mapping-file " + mapping + ": " + e);
624 error = true;
625 }
626 }
627 else
628 {
629 getLog().error("cannot find mapping-file " + mapping);
630 error = true;
631 }
632 }
633 if (error)
634 throw new MojoFailureException(
635 "error, while reading mappings configured in persistence-unit \"" +
636 unit.getName() +
637 "\""
638 );
639 }
640
641
642 for (String className : classes)
643 addAnnotated(className, sources, classLoaderService, tracker);
644
645
646 addMappings(sources, tracker);
647
648
649 if (!tracker.modified())
650 {
651 getLog().info("Mapping and configuration unchanged.");
652 if (force)
653 getLog().info("Generation/execution is forced!");
654 else
655 {
656 getLog().info("Skipping schema generation!");
657 project.getProperties().setProperty(SKIPPED, "true");
658 return;
659 }
660 }
661
662
663
664 try
665 {
666 new FileOutputStream(output).getChannel().truncate(0).close();
667 }
668 catch (IOException e)
669 {
670 String error =
671 "Error while truncating " + output.getAbsolutePath() + ": "
672 + e.getMessage();
673 getLog().warn(error);
674 throw new MojoExecutionException(error);
675 }
676
677
678 connectionProvider.open(classLoaderService, properties);
679
680 MetadataBuilder metadataBuilder = sources.getMetadataBuilder();
681
682 StrategySelector strategySelector =
683 serviceRegistry.getService(StrategySelector.class);
684
685 if (properties.containsKey(IMPLICIT_NAMING_STRATEGY))
686 {
687 metadataBuilder.applyImplicitNamingStrategy(
688 strategySelector.resolveStrategy(
689 ImplicitNamingStrategy.class,
690 properties.getProperty(IMPLICIT_NAMING_STRATEGY)
691 )
692 );
693 }
694
695 if (properties.containsKey(PHYSICAL_NAMING_STRATEGY))
696 {
697 metadataBuilder.applyPhysicalNamingStrategy(
698 strategySelector.resolveStrategy(
699 PhysicalNamingStrategy.class,
700 properties.getProperty(PHYSICAL_NAMING_STRATEGY)
701 )
702 );
703 }
704
705
706 Map settings = new HashMap();
707 settings.putAll(
708 serviceRegistry
709 .getService(ConfigurationService.class)
710 .getSettings()
711 );
712 ExceptionHandlerCollectingImpl handler =
713 new ExceptionHandlerCollectingImpl();
714 ExecutionOptions options =
715 SchemaManagementToolCoordinator
716 .buildExecutionOptions(settings, handler);
717 final EnumSet<TargetType> targetTypes = EnumSet.of(TargetType.SCRIPT);
718 if (execute)
719 targetTypes.add(TargetType.DATABASE);
720 TargetDescriptor target = new TargetDescriptor()
721 {
722 @Override
723 public EnumSet<TargetType> getTargetTypes()
724 {
725 return targetTypes;
726 }
727
728 @Override
729 public ScriptTargetOutput getScriptTargetOutput()
730 {
731 String charset =
732 (String)
733 serviceRegistry
734 .getService(ConfigurationService.class)
735 .getSettings()
736 .get(AvailableSettings.HBM2DDL_CHARSET_NAME);
737 return new ScriptTargetOutputToFile(output, charset);
738 }
739 };
740
741
742
743
744
745
746 Thread thread = Thread.currentThread();
747 ClassLoader contextClassLoader = thread.getContextClassLoader();
748 try
749 {
750 thread.setContextClassLoader(classLoader);
751 build((MetadataImplementor)metadataBuilder.build(), options, target);
752 if (handler.getExceptions().size() > 0)
753 {
754 StringBuilder builder = new StringBuilder();
755 builder.append("Hibernate failed:");
756 for (Exception e : handler.getExceptions())
757 {
758 builder.append("\n * ");
759 builder.append(e.getMessage());
760 AbstractSchemaMojo.printStrackTrace(builder, e);
761 builder.append("\n");
762 }
763 String error = builder.toString();
764 getLog().error(error);
765 throw new MojoFailureException(error);
766 }
767 }
768 finally
769 {
770 thread.setContextClassLoader(contextClassLoader);
771
772 checkOutputFile(output, tracker);
773 }
774 }
775 catch (MojoExecutionException e)
776 {
777 tracker.failed();
778 throw e;
779 }
780 catch (MojoFailureException e)
781 {
782 tracker.failed();
783 throw e;
784 }
785 catch (RuntimeException e)
786 {
787 tracker.failed();
788 throw e;
789 }
790 finally
791 {
792
793 tracker.save();
794
795
796 connectionProvider.close();
797
798
799 MavenLogAppender.endPluginLog(this);
800 }
801 }
802
803
804 abstract void build(
805 MetadataImplementor metadata,
806 ExecutionOptions options,
807 TargetDescriptor target
808 )
809 throws
810 MojoFailureException,
811 MojoExecutionException;
812
813
814 private MutableClassLoader createClassLoader() throws MojoExecutionException
815 {
816 try
817 {
818 getLog().debug("Creating ClassLoader for project-dependencies...");
819 LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
820 File file;
821
822 file = new File(testOutputDirectory);
823 if (!file.exists())
824 {
825 getLog().info("Creating test-output-directory: " + testOutputDirectory);
826 file.mkdirs();
827 }
828 urls.add(file.toURI().toURL());
829
830 file = new File(outputDirectory);
831 if (!file.exists())
832 {
833 getLog().info("Creating output-directory: " + outputDirectory);
834 file.mkdirs();
835 }
836 urls.add(file.toURI().toURL());
837
838 return new MutableClassLoader(urls, getLog());
839 }
840 catch (Exception e)
841 {
842 getLog().error("Error while creating ClassLoader!", e);
843 throw new MojoExecutionException(e.getMessage());
844 }
845 }
846
847 private void addDirectDependenciesClassPath(MutableClassLoader classLoader)
848 throws
849 MojoExecutionException
850 {
851 try
852 {
853 getLog().debug("Adding all direct project-dependencies to the ClassLoader...");
854 LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
855 addDependencies(urls);
856 if (scanTestClasses)
857 addRoot(urls, testOutputDirectory);
858 classLoader.add(urls);
859 }
860 catch (Exception e)
861 {
862 getLog().error("Error while creating ClassLoader!", e);
863 throw new MojoExecutionException(e.getMessage());
864 }
865 }
866
867 private void addAllDependenciesToClassPath(MutableClassLoader classLoader)
868 throws
869 MojoExecutionException
870 {
871 try
872 {
873 getLog().debug("Adding all project-dependencies to the ClassLoader...");
874 List<String> classpathFiles = project.getCompileClasspathElements();
875 classpathFiles.addAll(project.getTestClasspathElements());
876 LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
877 for (String pathElement : classpathFiles)
878 {
879 getLog().debug(" - adding " + pathElement);
880 urls.add(new File(pathElement).toURI().toURL());
881 }
882 classLoader.add(urls);
883 }
884 catch (Exception e)
885 {
886 getLog().error("Error while creating ClassLoader!", e);
887 throw new MojoExecutionException(e.getMessage());
888 }
889 }
890
891 private Map loadProperties(ConfigLoader configLoader)
892 throws
893 MojoExecutionException
894 {
895
896 if (hibernateProperties == null)
897 {
898 try
899 {
900 return configLoader.loadProperties("hibernate.properties");
901 }
902 catch (ConfigurationException e)
903 {
904 getLog().debug(e.getMessage());
905 return Collections.EMPTY_MAP;
906 }
907 }
908 else
909 {
910 try
911 {
912 File file = new File(hibernateProperties);
913 if (file.exists())
914 {
915 getLog().info("Reading settings from file " + hibernateProperties + "...");
916 return configLoader.loadProperties(file);
917 }
918 else
919 return configLoader.loadProperties(hibernateProperties);
920 }
921 catch (ConfigurationException e)
922 {
923 getLog().error("Error while reading properties!", e);
924 throw new MojoExecutionException(e.getMessage());
925 }
926 }
927 }
928
929 private LoadedConfig loadConfig(ConfigLoader configLoader)
930 throws MojoExecutionException
931 {
932
933 if (hibernateConfig == null)
934 {
935 try
936 {
937 return configLoader.loadConfigXmlResource("hibernate.cfg.xml");
938 }
939 catch (ConfigurationException e)
940 {
941 getLog().debug(e.getMessage());
942 return null;
943 }
944 }
945 else
946 {
947 try
948 {
949 File file = new File(hibernateConfig);
950 if (file.exists())
951 {
952 getLog().info("Reading configuration from file " + hibernateConfig + "...");
953 return configLoader.loadConfigXmlFile(file);
954 }
955 else
956 {
957 return configLoader.loadConfigXmlResource(hibernateConfig);
958 }
959 }
960 catch (ConfigurationException e)
961 {
962 getLog().error("Error while reading configuration!", e);
963 throw new MojoExecutionException(e.getMessage());
964 }
965 }
966 }
967
968 private void configure(Properties properties, ModificationTracker tracker)
969 throws MojoFailureException
970 {
971
972
973
974
975 if (tracker.check(EXECUTE, execute.toString()) && execute)
976 {
977 getLog().info(
978 "hibernate.schema.execute was switched on: " +
979 "forcing generation/execution of SQL"
980 );
981 tracker.touch();
982 }
983 configure(properties, execute, EXECUTE);
984
985
986
987
988
989
990 configure(properties, dialect, DIALECT);
991 configure(properties, delimiter, HBM2DDL_DELIMITER);
992 configure(properties, format, FORMAT_SQL);
993 configure(properties, createNamespaces, HBM2DLL_CREATE_NAMESPACES);
994 configure(properties, implicitNamingStrategy, IMPLICIT_NAMING_STRATEGY);
995 configure(properties, physicalNamingStrategy, PHYSICAL_NAMING_STRATEGY);
996 configure(properties, outputDirectory, OUTPUTDIRECTORY);
997 configure(properties, scanDependencies, SCAN_DEPENDENCIES);
998 configure(properties, scanTestClasses, SCAN_TESTCLASSES);
999 configure(properties, testOutputDirectory, TEST_OUTPUTDIRECTORY);
1000
1001
1002
1003
1004
1005
1006 if (show == null)
1007 show = Boolean.valueOf(properties.getProperty(SHOW_SQL));
1008 else
1009 properties.setProperty(SHOW_SQL, show.toString());
1010
1011
1012
1013
1014
1015 configure(properties, driver, DRIVER, JPA_JDBC_DRIVER);
1016 configure(properties, url, URL, JPA_JDBC_URL);
1017 configure(properties, username, USER, JPA_JDBC_USER);
1018 configure(properties, password, PASS, JPA_JDBC_PASSWORD);
1019
1020 if (properties.isEmpty())
1021 {
1022 getLog().error("No properties set!");
1023 throw new MojoFailureException("Hibernate configuration is missing!");
1024 }
1025
1026 getLog().info("Gathered configuration:");
1027 for (Entry<Object,Object> entry : properties.entrySet())
1028 getLog().info(" " + entry.getKey() + " = " + entry.getValue());
1029 }
1030
1031 private void configure(
1032 Properties properties,
1033 String value,
1034 String key,
1035 String alternativeKey
1036 )
1037 {
1038 configure(properties, value, key);
1039
1040 if (properties.containsKey(alternativeKey))
1041 {
1042 if (properties.containsKey(key))
1043 {
1044 getLog().warn(
1045 "Ignoring property " + alternativeKey + "=\"" +
1046 properties.getProperty(alternativeKey) +
1047 "\" in favour for property " + key + "=\"" +
1048 properties.getProperty(key) + "\""
1049 );
1050 properties.remove(alternativeKey);
1051 }
1052 else
1053 {
1054 value = properties.getProperty(alternativeKey);
1055 properties.remove(alternativeKey);
1056 getLog().info(
1057 "Using value \"" + value + "\" from property " + alternativeKey +
1058 " for property " + key
1059 );
1060 properties.setProperty(key, value);
1061 }
1062 }
1063 }
1064
1065 private void configure(Properties properties, String value, String key)
1066 {
1067 if (value != null)
1068 {
1069 if (properties.containsKey(key))
1070 {
1071 if (!properties.getProperty(key).equals(value))
1072 {
1073 getLog().info(
1074 "Overwriting property " + key + "=\"" +
1075 properties.getProperty(key) +
1076 "\" with value \"" + value + "\""
1077 );
1078 properties.setProperty(key, value);
1079 }
1080 }
1081 else
1082 {
1083 getLog().debug("Using value \"" + value + "\" for property " + key);
1084 properties.setProperty(key, value);
1085 }
1086 }
1087 }
1088
1089 private void configure(Properties properties, Boolean value, String key)
1090 {
1091 configure(properties, value == null ? null : value.toString(), key);
1092 }
1093
1094 private File getOutputFile(String filename)
1095 throws
1096 MojoExecutionException
1097 {
1098 File output = new File(filename);
1099
1100 if (!output.isAbsolute())
1101 {
1102
1103 output = new File(buildDirectory, filename);
1104 }
1105 getLog().debug("Output file: " + output.getPath());
1106
1107
1108 File outFileParentDir = output.getParentFile();
1109 if (null != outFileParentDir && !outFileParentDir.exists())
1110 {
1111 try
1112 {
1113 getLog().info(
1114 "Creating directory path for output file:" +
1115 outFileParentDir.getPath()
1116 );
1117 outFileParentDir.mkdirs();
1118 }
1119 catch (Exception e)
1120 {
1121 String error =
1122 "Error creating directory path for output file: " + e.getMessage();
1123 getLog().error(error);
1124 throw new MojoExecutionException(error);
1125 }
1126 }
1127
1128 try
1129 {
1130 output.createNewFile();
1131 }
1132 catch (IOException e)
1133 {
1134 String error = "Error creating output file: " + e.getMessage();
1135 getLog().error(error);
1136 throw new MojoExecutionException(error);
1137 }
1138
1139 if (!output.canWrite())
1140 {
1141 String error =
1142 "Output file " + output.getAbsolutePath() + " is not writable!";
1143 getLog().error(error);
1144 throw new MojoExecutionException(error);
1145 }
1146
1147 return output;
1148 }
1149
1150 private void checkOutputFile(File output, ModificationTracker tracker)
1151 throws
1152 MojoExecutionException
1153 {
1154 try
1155 {
1156 if (output.exists())
1157 tracker.track(SCRIPT, new FileInputStream(output));
1158 else
1159 tracker.track(SCRIPT, ZonedDateTime.now().toString());
1160 }
1161 catch (IOException e)
1162 {
1163 String error =
1164 "Error while checking the generated script: " + e.getMessage();
1165 getLog().error(error);
1166 throw new MojoExecutionException(error);
1167 }
1168 }
1169
1170 private void addMappings(MetadataSources sources, ModificationTracker tracker)
1171 throws MojoFailureException
1172 {
1173 getLog().debug("Adding explicitly configured mappings...");
1174 if (mappings != null)
1175 {
1176 try
1177 {
1178 for (String filename : mappings.split("[\\s,]+"))
1179 {
1180
1181 File file = new File(filename);
1182 if (!file.exists())
1183 {
1184
1185 for (Resource resource : project.getResources())
1186 {
1187 file = new File(resource.getDirectory() + File.separator + filename);
1188 if (file.exists())
1189 break;
1190 }
1191 }
1192 if (file.exists())
1193 {
1194 if (file.isDirectory())
1195
1196 throw new MojoFailureException(file.getAbsolutePath() + " is a directory");
1197 if (tracker.track(filename, new FileInputStream(file)))
1198 getLog().debug(" - found new or modified mapping-file: " + filename);
1199 else
1200 getLog().debug(" - mapping-file unchanged: " + filename);
1201
1202 sources.addFile(file);
1203 }
1204 else
1205 throw new MojoFailureException("File " + filename + " could not be found in any of the configured resource-directories!");
1206 }
1207 }
1208 catch (IOException e)
1209 {
1210 throw new MojoFailureException("Cannot calculate MD5 sums!", e);
1211 }
1212 }
1213 }
1214
1215 private void addRoot(Set<URL> urls, String path) throws MojoFailureException
1216 {
1217 try
1218 {
1219 File dir = new File(path);
1220 if (dir.exists())
1221 {
1222 getLog().info(" - adding " + dir.getAbsolutePath());
1223 urls.add(dir.toURI().toURL());
1224 }
1225 else
1226 getLog().warn(
1227 "The directory cannot be scanned for annotated classes, " +
1228 "because it does not exist: " +
1229 dir.getAbsolutePath()
1230 );
1231 }
1232 catch (MalformedURLException e)
1233 {
1234 getLog().error("error while adding the project-root to the list of roots to scan!", e);
1235 throw new MojoFailureException(e.getMessage());
1236 }
1237 }
1238
1239 private void addDependencies(Set<URL> urls) throws MojoFailureException
1240 {
1241 try
1242 {
1243 if (scanDependencies != null)
1244 {
1245 Matcher matcher = SPLIT.matcher(scanDependencies);
1246 while (matcher.find())
1247 {
1248 getLog().debug("Adding dependencies from scope " + matcher.group() + " to the list of roots to scan");
1249 for (Artifact artifact : project.getDependencyArtifacts())
1250 {
1251 if (!artifact.getScope().equalsIgnoreCase(matcher.group()))
1252 continue;
1253 if (artifact.getFile() == null)
1254 {
1255 getLog().warn("Cannot add dependency " + artifact.getId() + ": no JAR-file available!");
1256 continue;
1257 }
1258 getLog().debug(" - adding " + artifact.getId());
1259 urls.add(artifact.getFile().toURI().toURL());
1260 }
1261 }
1262 }
1263 }
1264 catch (MalformedURLException e)
1265 {
1266 getLog().error("Error while adding dependencies to the list of roots to scan!", e);
1267 throw new MojoFailureException(e.getMessage());
1268 }
1269 }
1270
1271 private Set<String> scanUrls(Set<URL> scanRoots)
1272 throws
1273 MojoFailureException
1274 {
1275 try
1276 {
1277 AnnotationDB db = new AnnotationDB();
1278 for (URL root : scanRoots)
1279 db.scanArchives(root);
1280
1281 Set<String> classes = new HashSet<String>();
1282 if (db.getAnnotationIndex().containsKey(Entity.class.getName()))
1283 classes.addAll(db.getAnnotationIndex().get(Entity.class.getName()));
1284 if (db.getAnnotationIndex().containsKey(MappedSuperclass.class.getName()))
1285 classes.addAll(db.getAnnotationIndex().get(MappedSuperclass.class.getName()));
1286 if (db.getAnnotationIndex().containsKey(Embeddable.class.getName()))
1287 classes.addAll(db.getAnnotationIndex().get(Embeddable.class.getName()));
1288
1289 return classes;
1290 }
1291 catch (Exception e)
1292 {
1293 getLog().error("Error while scanning!", e);
1294 throw new MojoFailureException(e.getMessage());
1295 }
1296 }
1297
1298 private void addAnnotated(
1299 String name,
1300 MetadataSources sources,
1301 ClassLoaderService classLoaderService,
1302 ModificationTracker tracker
1303 )
1304 throws
1305 MojoFailureException,
1306 MojoExecutionException
1307 {
1308 try
1309 {
1310 getLog().info("Adding annotated resource: " + name);
1311 String packageName = null;
1312
1313 boolean error = false;
1314 try
1315 {
1316 Class<?> annotatedClass = classLoaderService.classForName(name);
1317 String resourceName = annotatedClass.getName();
1318 resourceName =
1319 resourceName.substring(
1320 resourceName.lastIndexOf(".") + 1,
1321 resourceName.length()
1322 ) + ".class";
1323 InputStream is = annotatedClass.getResourceAsStream(resourceName);
1324 if (is != null)
1325 {
1326 if (tracker.track(name, is))
1327 getLog().debug("New or modified class: " + name);
1328 else
1329 getLog().debug("Unchanged class: " + name);
1330 sources.addAnnotatedClass(annotatedClass);
1331 packageName = annotatedClass.getPackage().getName();
1332 }
1333 else
1334 {
1335 getLog().error("cannot find ressource " + resourceName + " for class " + name);
1336 error = true;
1337 }
1338 }
1339 catch(ClassLoadingException e)
1340 {
1341 packageName = name;
1342 }
1343 if (error)
1344 {
1345 throw new MojoExecutionException("error while inspecting annotated class " + name);
1346 }
1347
1348 while (packageName != null)
1349 {
1350 if (packages.contains(packageName))
1351 return;
1352 String resource = packageName.replace('.', '/') + "/package-info.class";
1353 InputStream is = classLoaderService.locateResourceStream(resource);
1354 if (is == null)
1355 {
1356
1357 getLog().debug("Package " + packageName + " is not annotated.");
1358 }
1359 else
1360 {
1361 if (tracker.track(packageName, is))
1362 getLog().debug("New or modified package: " + packageName);
1363 else
1364 getLog().debug("Unchanged package: " + packageName);
1365 getLog().info("Adding annotations from package " + packageName);
1366 sources.addPackage(packageName);
1367 }
1368 packages.add(packageName);
1369 int i = packageName.lastIndexOf('.');
1370 if (i < 0)
1371 packageName = null;
1372 else
1373 packageName = packageName.substring(0,i);
1374 }
1375 }
1376 catch (Exception e)
1377 {
1378 getLog().error("Error while adding the annotated class " + name, e);
1379 throw new MojoFailureException(e.getMessage());
1380 }
1381 }
1382
1383 private ParsedPersistenceXmlDescriptor loadPersistenceUnit(
1384 ClassLoaderService classLoaderService,
1385 Properties properties
1386 )
1387 throws
1388 MojoFailureException
1389 {
1390 PersistenceXmlParser parser =
1391 new PersistenceXmlParser(
1392 classLoaderService,
1393 PersistenceUnitTransactionType.RESOURCE_LOCAL
1394 );
1395
1396 Map<String, ParsedPersistenceXmlDescriptor> units =
1397 parser.doResolve(properties);
1398
1399 if (persistenceUnit == null)
1400 {
1401 Iterator<String> names = units.keySet().iterator();
1402 if (!names.hasNext())
1403 {
1404 getLog().info("Found no META-INF/persistence.xml.");
1405 return null;
1406 }
1407
1408 String name = names.next();
1409 if (!names.hasNext())
1410 {
1411 getLog().info("Using persistence-unit " + name);
1412 return units.get(name);
1413 }
1414
1415 StringBuilder builder = new StringBuilder();
1416 builder.append("No name provided and multiple persistence units found: ");
1417 builder.append(name);
1418 while(names.hasNext())
1419 {
1420 builder.append(", ");
1421 builder.append(names.next());
1422 }
1423 builder.append('.');
1424 throw new MojoFailureException(builder.toString());
1425 }
1426
1427 if (units.containsKey(persistenceUnit))
1428 {
1429 getLog().info("Using configured persistence-unit " + persistenceUnit);
1430 return units.get(persistenceUnit);
1431 }
1432
1433 throw new MojoFailureException("Could not find persistence-unit " + persistenceUnit);
1434 }
1435
1436
1437 public static void printStrackTrace(StringBuilder builder, Throwable t)
1438 {
1439 while (t != null)
1440 {
1441 builder.append("\n\tCause: ");
1442 builder.append(t.getMessage() == null ? "" : t.getMessage().replaceAll("\\s+", " "));
1443 for (StackTraceElement trace : t.getStackTrace())
1444 {
1445 builder.append("\n\t");
1446 builder.append(trace.getClassName());
1447 builder.append(".");
1448 builder.append(trace.getMethodName());
1449 builder.append("():");
1450 builder.append(trace.getLineNumber());
1451 }
1452 t = t.getCause();
1453 }
1454 }
1455 }