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 ParsedPersistenceXmlDescriptor unit =
494 loadPersistenceUnit(classLoaderService, properties);
495 if (unit != null)
496 properties.putAll(unit.getProperties());
497
498
499 configure(properties, tracker);
500
501
502 if(tracker.track(properties))
503 getLog().debug("Configuration has changed.");
504 else
505 getLog().debug("Configuration unchanged.");
506
507
508 final File output = getOutputFile(filename);
509
510 checkOutputFile(output, tracker);
511
512
513 final StandardServiceRegistry serviceRegistry =
514 new StandardServiceRegistryBuilder(bootstrapServiceRegitry)
515 .applySettings(properties)
516 .addService(ConnectionProvider.class, connectionProvider)
517 .build();
518 final MetadataSources sources = new MetadataSources(serviceRegistry);
519
520
521 completeClassPath(classLoader);
522
523
524 if (config != null)
525 {
526 for (MappingReference mapping : config.getMappingReferences())
527 mapping.apply(sources);
528 }
529
530 Set<String> classes;
531 if (unit == null)
532 {
533
534 if (scanClasses == null)
535 scanClasses = true;
536 Set<URL> urls = new HashSet<URL>();
537 if (scanClasses)
538 addRoot(urls, outputDirectory);
539 if (scanTestClasses)
540 addRoot(urls, testOutputDirectory);
541 addDependencies(urls);
542 classes = scanUrls(urls);
543 }
544 else
545 {
546
547 if (scanClasses == null)
548 scanClasses = !unit.isExcludeUnlistedClasses();
549 Set<URL> urls = new HashSet<URL>();
550 if (scanClasses)
551 {
552
553
554
555
556 urls.add(unit.getPersistenceUnitRootUrl());
557 for (URL url : unit.getJarFileUrls())
558 urls.add(url);
559 }
560 if (scanTestClasses)
561 addRoot(urls, testOutputDirectory);
562 classes = scanUrls(urls);
563 for (String className : unit.getManagedClassNames())
564 classes.add(className);
565
566
567
568
569 boolean error = false;
570 InputStream is;
571 is = classLoader.getResourceAsStream("META-INF/orm.xml");
572 if (is != null)
573 {
574 getLog().info("Adding default JPA-XML-mapping from META-INF/orm.xml");
575 try
576 {
577 tracker.track("META-INF/orm.xml", is);
578 sources.addResource("META-INF/orm.xml");
579 }
580 catch (IOException e)
581 {
582 getLog().error("cannot read META-INF/orm.xml: " + e);
583 error = true;
584 }
585 }
586 else
587 {
588 getLog().debug("no META-INF/orm.xml found");
589 }
590
591
592
593
594 for (String mapping : unit.getMappingFileNames())
595 {
596 getLog().info("Adding explicitly configured mapping from " + mapping);
597 is = classLoader.getResourceAsStream(mapping);
598 if (is != null)
599 {
600 try
601 {
602 tracker.track(mapping, is);
603 sources.addResource(mapping);
604 }
605 catch (IOException e)
606 {
607 getLog().info("cannot read mapping-file " + mapping + ": " + e);
608 error = true;
609 }
610 }
611 else
612 {
613 getLog().error("cannot find mapping-file " + mapping);
614 error = true;
615 }
616 }
617 if (error)
618 throw new MojoFailureException(
619 "error, while reading mappings configured in persistence-unit \"" +
620 unit.getName() +
621 "\""
622 );
623 }
624
625
626 for (String className : classes)
627 addAnnotated(className, sources, classLoaderService, tracker);
628
629
630 addMappings(sources, tracker);
631
632
633 if (!tracker.modified())
634 {
635 getLog().info("Mapping and configuration unchanged.");
636 if (force)
637 getLog().info("Generation/execution is forced!");
638 else
639 {
640 getLog().info("Skipping schema generation!");
641 project.getProperties().setProperty(SKIPPED, "true");
642 return;
643 }
644 }
645
646
647
648 try
649 {
650 new FileOutputStream(output).getChannel().truncate(0).close();
651 }
652 catch (IOException e)
653 {
654 String error =
655 "Error while truncating " + output.getAbsolutePath() + ": "
656 + e.getMessage();
657 getLog().warn(error);
658 throw new MojoExecutionException(error);
659 }
660
661
662 connectionProvider.open(classLoaderService, properties);
663
664 MetadataBuilder metadataBuilder = sources.getMetadataBuilder();
665
666 StrategySelector strategySelector =
667 serviceRegistry.getService(StrategySelector.class);
668
669 if (properties.containsKey(IMPLICIT_NAMING_STRATEGY))
670 {
671 metadataBuilder.applyImplicitNamingStrategy(
672 strategySelector.resolveStrategy(
673 ImplicitNamingStrategy.class,
674 properties.getProperty(IMPLICIT_NAMING_STRATEGY)
675 )
676 );
677 }
678
679 if (properties.containsKey(PHYSICAL_NAMING_STRATEGY))
680 {
681 metadataBuilder.applyPhysicalNamingStrategy(
682 strategySelector.resolveStrategy(
683 PhysicalNamingStrategy.class,
684 properties.getProperty(PHYSICAL_NAMING_STRATEGY)
685 )
686 );
687 }
688
689
690 Map settings = new HashMap();
691 settings.putAll(
692 serviceRegistry
693 .getService(ConfigurationService.class)
694 .getSettings()
695 );
696 ExceptionHandlerCollectingImpl handler =
697 new ExceptionHandlerCollectingImpl();
698 ExecutionOptions options =
699 SchemaManagementToolCoordinator
700 .buildExecutionOptions(settings, handler);
701 final EnumSet<TargetType> targetTypes = EnumSet.of(TargetType.SCRIPT);
702 if (execute)
703 targetTypes.add(TargetType.DATABASE);
704 TargetDescriptor target = new TargetDescriptor()
705 {
706 @Override
707 public EnumSet<TargetType> getTargetTypes()
708 {
709 return targetTypes;
710 }
711
712 @Override
713 public ScriptTargetOutput getScriptTargetOutput()
714 {
715 String charset =
716 (String)
717 serviceRegistry
718 .getService(ConfigurationService.class)
719 .getSettings()
720 .get(AvailableSettings.HBM2DDL_CHARSET_NAME);
721 return new ScriptTargetOutputToFile(output, charset);
722 }
723 };
724
725
726
727
728
729
730 Thread thread = Thread.currentThread();
731 ClassLoader contextClassLoader = thread.getContextClassLoader();
732 try
733 {
734 thread.setContextClassLoader(classLoader);
735 build((MetadataImplementor)metadataBuilder.build(), options, target);
736 if (handler.getExceptions().size() > 0)
737 {
738 StringBuilder builder = new StringBuilder();
739 builder.append("Hibernate failed:");
740 for (Exception e : handler.getExceptions())
741 {
742 builder.append("\n * ");
743 builder.append(e.getMessage());
744 AbstractSchemaMojo.printStrackTrace(builder, e);
745 builder.append("\n");
746 }
747 String error = builder.toString();
748 getLog().error(error);
749 throw new MojoFailureException(error);
750 }
751 }
752 finally
753 {
754 thread.setContextClassLoader(contextClassLoader);
755
756 checkOutputFile(output, tracker);
757 }
758 }
759 catch (MojoExecutionException e)
760 {
761 tracker.failed();
762 throw e;
763 }
764 catch (MojoFailureException e)
765 {
766 tracker.failed();
767 throw e;
768 }
769 catch (RuntimeException e)
770 {
771 tracker.failed();
772 throw e;
773 }
774 finally
775 {
776
777 tracker.save();
778
779
780 connectionProvider.close();
781
782
783 MavenLogAppender.endPluginLog(this);
784 }
785 }
786
787
788 abstract void build(
789 MetadataImplementor metadata,
790 ExecutionOptions options,
791 TargetDescriptor target
792 )
793 throws
794 MojoFailureException,
795 MojoExecutionException;
796
797
798 private MutableClassLoader createClassLoader() throws MojoExecutionException
799 {
800 try
801 {
802 getLog().debug("Creating ClassLoader for project-dependencies...");
803 LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
804 File file;
805
806 file = new File(testOutputDirectory);
807 if (!file.exists())
808 {
809 getLog().info("Creating test-output-directory: " + testOutputDirectory);
810 file.mkdirs();
811 }
812 urls.add(file.toURI().toURL());
813
814 file = new File(outputDirectory);
815 if (!file.exists())
816 {
817 getLog().info("Creating output-directory: " + outputDirectory);
818 file.mkdirs();
819 }
820 urls.add(file.toURI().toURL());
821
822 return new MutableClassLoader(urls, getLog());
823 }
824 catch (Exception e)
825 {
826 getLog().error("Error while creating ClassLoader!", e);
827 throw new MojoExecutionException(e.getMessage());
828 }
829 }
830
831 private void completeClassPath(MutableClassLoader classLoader)
832 throws
833 MojoExecutionException
834 {
835 try
836 {
837 getLog().debug("Completing class-paths of the ClassLoader for project-dependencies...");
838 List<String> classpathFiles = project.getCompileClasspathElements();
839 if (scanTestClasses)
840 classpathFiles.addAll(project.getTestClasspathElements());
841 LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
842 for (String pathElement : classpathFiles)
843 {
844 getLog().debug("Dependency: " + pathElement);
845 urls.add(new File(pathElement).toURI().toURL());
846 }
847 classLoader.add(urls);
848 }
849 catch (Exception e)
850 {
851 getLog().error("Error while creating ClassLoader!", e);
852 throw new MojoExecutionException(e.getMessage());
853 }
854 }
855
856 private Map loadProperties(ConfigLoader configLoader)
857 throws
858 MojoExecutionException
859 {
860
861 if (hibernateProperties == null)
862 {
863 try
864 {
865 return configLoader.loadProperties("hibernate.properties");
866 }
867 catch (ConfigurationException e)
868 {
869 getLog().debug(e.getMessage());
870 return Collections.EMPTY_MAP;
871 }
872 }
873 else
874 {
875 try
876 {
877 File file = new File(hibernateProperties);
878 if (file.exists())
879 {
880 getLog().info("Reading settings from file " + hibernateProperties + "...");
881 return configLoader.loadProperties(file);
882 }
883 else
884 return configLoader.loadProperties(hibernateProperties);
885 }
886 catch (ConfigurationException e)
887 {
888 getLog().error("Error while reading properties!", e);
889 throw new MojoExecutionException(e.getMessage());
890 }
891 }
892 }
893
894 private LoadedConfig loadConfig(ConfigLoader configLoader)
895 throws MojoExecutionException
896 {
897
898 if (hibernateConfig == null)
899 {
900 try
901 {
902 return configLoader.loadConfigXmlResource("hibernate.cfg.xml");
903 }
904 catch (ConfigurationException e)
905 {
906 getLog().debug(e.getMessage());
907 return null;
908 }
909 }
910 else
911 {
912 try
913 {
914 File file = new File(hibernateConfig);
915 if (file.exists())
916 {
917 getLog().info("Reading configuration from file " + hibernateConfig + "...");
918 return configLoader.loadConfigXmlFile(file);
919 }
920 else
921 {
922 return configLoader.loadConfigXmlResource(hibernateConfig);
923 }
924 }
925 catch (ConfigurationException e)
926 {
927 getLog().error("Error while reading configuration!", e);
928 throw new MojoExecutionException(e.getMessage());
929 }
930 }
931 }
932
933 private void configure(Properties properties, ModificationTracker tracker)
934 throws MojoFailureException
935 {
936
937
938
939
940 if (tracker.check(EXECUTE, execute.toString()) && execute)
941 {
942 getLog().info(
943 "hibernate.schema.execute was switched on: " +
944 "forcing generation/execution of SQL"
945 );
946 tracker.touch();
947 }
948 configure(properties, execute, EXECUTE);
949
950
951
952
953
954
955 configure(properties, dialect, DIALECT);
956 configure(properties, delimiter, HBM2DDL_DELIMITER);
957 configure(properties, format, FORMAT_SQL);
958 configure(properties, createNamespaces, HBM2DLL_CREATE_NAMESPACES);
959 configure(properties, implicitNamingStrategy, IMPLICIT_NAMING_STRATEGY);
960 configure(properties, physicalNamingStrategy, PHYSICAL_NAMING_STRATEGY);
961 configure(properties, outputDirectory, OUTPUTDIRECTORY);
962 configure(properties, scanDependencies, SCAN_DEPENDENCIES);
963 configure(properties, scanTestClasses, SCAN_TESTCLASSES);
964 configure(properties, testOutputDirectory, TEST_OUTPUTDIRECTORY);
965
966
967
968
969
970
971 if (show == null)
972 show = Boolean.valueOf(properties.getProperty(SHOW_SQL));
973 else
974 properties.setProperty(SHOW_SQL, show.toString());
975
976
977
978
979
980 configure(properties, driver, DRIVER, JPA_JDBC_DRIVER);
981 configure(properties, url, URL, JPA_JDBC_URL);
982 configure(properties, username, USER, JPA_JDBC_USER);
983 configure(properties, password, PASS, JPA_JDBC_PASSWORD);
984
985 if (properties.isEmpty())
986 {
987 getLog().error("No properties set!");
988 throw new MojoFailureException("Hibernate configuration is missing!");
989 }
990
991 getLog().info("Gathered configuration:");
992 for (Entry<Object,Object> entry : properties.entrySet())
993 getLog().info(" " + entry.getKey() + " = " + entry.getValue());
994 }
995
996 private void configure(
997 Properties properties,
998 String value,
999 String key,
1000 String alternativeKey
1001 )
1002 {
1003 configure(properties, value, key);
1004
1005 if (properties.containsKey(alternativeKey))
1006 {
1007 if (properties.containsKey(key))
1008 {
1009 getLog().warn(
1010 "Ignoring property " + alternativeKey + "=\"" +
1011 properties.getProperty(alternativeKey) +
1012 "\" in favour for property " + key + "=\"" +
1013 properties.getProperty(key) + "\""
1014 );
1015 properties.remove(alternativeKey);
1016 }
1017 else
1018 {
1019 value = properties.getProperty(alternativeKey);
1020 properties.remove(alternativeKey);
1021 getLog().info(
1022 "Using value \"" + value + "\" from property " + alternativeKey +
1023 " for property " + key
1024 );
1025 properties.setProperty(key, value);
1026 }
1027 }
1028 }
1029
1030 private void configure(Properties properties, String value, String key)
1031 {
1032 if (value != null)
1033 {
1034 if (properties.containsKey(key))
1035 {
1036 if (!properties.getProperty(key).equals(value))
1037 {
1038 getLog().info(
1039 "Overwriting property " + key + "=\"" +
1040 properties.getProperty(key) +
1041 "\" with value \"" + value + "\""
1042 );
1043 properties.setProperty(key, value);
1044 }
1045 }
1046 else
1047 {
1048 getLog().debug("Using value \"" + value + "\" for property " + key);
1049 properties.setProperty(key, value);
1050 }
1051 }
1052 }
1053
1054 private void configure(Properties properties, Boolean value, String key)
1055 {
1056 configure(properties, value == null ? null : value.toString(), key);
1057 }
1058
1059 private File getOutputFile(String filename)
1060 throws
1061 MojoExecutionException
1062 {
1063 File output = new File(filename);
1064
1065 if (!output.isAbsolute())
1066 {
1067
1068 output = new File(buildDirectory, filename);
1069 }
1070 getLog().debug("Output file: " + output.getPath());
1071
1072
1073 File outFileParentDir = output.getParentFile();
1074 if (null != outFileParentDir && !outFileParentDir.exists())
1075 {
1076 try
1077 {
1078 getLog().info(
1079 "Creating directory path for output file:" +
1080 outFileParentDir.getPath()
1081 );
1082 outFileParentDir.mkdirs();
1083 }
1084 catch (Exception e)
1085 {
1086 String error =
1087 "Error creating directory path for output file: " + e.getMessage();
1088 getLog().error(error);
1089 throw new MojoExecutionException(error);
1090 }
1091 }
1092
1093 try
1094 {
1095 output.createNewFile();
1096 }
1097 catch (IOException e)
1098 {
1099 String error = "Error creating output file: " + e.getMessage();
1100 getLog().error(error);
1101 throw new MojoExecutionException(error);
1102 }
1103
1104 if (!output.canWrite())
1105 {
1106 String error =
1107 "Output file " + output.getAbsolutePath() + " is not writable!";
1108 getLog().error(error);
1109 throw new MojoExecutionException(error);
1110 }
1111
1112 return output;
1113 }
1114
1115 private void checkOutputFile(File output, ModificationTracker tracker)
1116 throws
1117 MojoExecutionException
1118 {
1119 try
1120 {
1121 if (output.exists())
1122 tracker.track(SCRIPT, new FileInputStream(output));
1123 else
1124 tracker.track(SCRIPT, ZonedDateTime.now().toString());
1125 }
1126 catch (IOException e)
1127 {
1128 String error =
1129 "Error while checking the generated script: " + e.getMessage();
1130 getLog().error(error);
1131 throw new MojoExecutionException(error);
1132 }
1133 }
1134
1135 private void addMappings(MetadataSources sources, ModificationTracker tracker)
1136 throws MojoFailureException
1137 {
1138 getLog().debug("Adding explicitly configured mappings...");
1139 if (mappings != null)
1140 {
1141 try
1142 {
1143 for (String filename : mappings.split("[\\s,]+"))
1144 {
1145
1146 File file = new File(filename);
1147 if (!file.exists())
1148 {
1149
1150 for (Resource resource : project.getResources())
1151 {
1152 file = new File(resource.getDirectory() + File.separator + filename);
1153 if (file.exists())
1154 break;
1155 }
1156 }
1157 if (file.exists())
1158 {
1159 if (file.isDirectory())
1160
1161 throw new MojoFailureException(file.getAbsolutePath() + " is a directory");
1162 if (tracker.track(filename, new FileInputStream(file)))
1163 getLog().debug("Found new or modified mapping-file: " + filename);
1164 else
1165 getLog().debug("Mapping-file unchanged: " + filename);
1166
1167 sources.addFile(file);
1168 }
1169 else
1170 throw new MojoFailureException("File " + filename + " could not be found in any of the configured resource-directories!");
1171 }
1172 }
1173 catch (IOException e)
1174 {
1175 throw new MojoFailureException("Cannot calculate MD5 sums!", e);
1176 }
1177 }
1178 }
1179
1180 private void addRoot(Set<URL> urls, String path) throws MojoFailureException
1181 {
1182 try
1183 {
1184 File dir = new File(path);
1185 if (dir.exists())
1186 {
1187 getLog().info("Adding " + dir.getAbsolutePath() + " to the list of roots to scan...");
1188 urls.add(dir.toURI().toURL());
1189 }
1190 else
1191 getLog().warn(
1192 "the directory cannot be scanned for annotated classes, " +
1193 "because it does not exist: " +
1194 dir.getAbsolutePath()
1195 );
1196 }
1197 catch (MalformedURLException e)
1198 {
1199 getLog().error("error while adding the project-root to the list of roots to scan!", e);
1200 throw new MojoFailureException(e.getMessage());
1201 }
1202 }
1203
1204 private void addDependencies(Set<URL> urls) throws MojoFailureException
1205 {
1206 try
1207 {
1208 if (scanDependencies != null)
1209 {
1210 Matcher matcher = SPLIT.matcher(scanDependencies);
1211 while (matcher.find())
1212 {
1213 getLog().info("Adding dependencies from scope " + matcher.group() + " to the list of roots to scan");
1214 for (Artifact artifact : project.getDependencyArtifacts())
1215 {
1216 if (!artifact.getScope().equalsIgnoreCase(matcher.group()))
1217 continue;
1218 if (artifact.getFile() == null)
1219 {
1220 getLog().warn("Cannot add dependency " + artifact.getId() + ": no JAR-file available!");
1221 continue;
1222 }
1223 getLog().info("Adding dependencies from scope " + artifact.getId() + " to the list of roots to scan");
1224 urls.add(artifact.getFile().toURI().toURL());
1225 }
1226 }
1227 }
1228 }
1229 catch (MalformedURLException e)
1230 {
1231 getLog().error("Error while adding dependencies to the list of roots to scan!", e);
1232 throw new MojoFailureException(e.getMessage());
1233 }
1234 }
1235
1236 private Set<String> scanUrls(Set<URL> scanRoots)
1237 throws
1238 MojoFailureException
1239 {
1240 try
1241 {
1242 AnnotationDB db = new AnnotationDB();
1243 for (URL root : scanRoots)
1244 db.scanArchives(root);
1245
1246 Set<String> classes = new HashSet<String>();
1247 if (db.getAnnotationIndex().containsKey(Entity.class.getName()))
1248 classes.addAll(db.getAnnotationIndex().get(Entity.class.getName()));
1249 if (db.getAnnotationIndex().containsKey(MappedSuperclass.class.getName()))
1250 classes.addAll(db.getAnnotationIndex().get(MappedSuperclass.class.getName()));
1251 if (db.getAnnotationIndex().containsKey(Embeddable.class.getName()))
1252 classes.addAll(db.getAnnotationIndex().get(Embeddable.class.getName()));
1253
1254 return classes;
1255 }
1256 catch (Exception e)
1257 {
1258 getLog().error("Error while scanning!", e);
1259 throw new MojoFailureException(e.getMessage());
1260 }
1261 }
1262
1263 private void addAnnotated(
1264 String name,
1265 MetadataSources sources,
1266 ClassLoaderService classLoaderService,
1267 ModificationTracker tracker
1268 )
1269 throws
1270 MojoFailureException,
1271 MojoExecutionException
1272 {
1273 try
1274 {
1275 getLog().info("Adding annotated resource: " + name);
1276 String packageName = null;
1277
1278 boolean error = false;
1279 try
1280 {
1281 Class<?> annotatedClass = classLoaderService.classForName(name);
1282 String resourceName = annotatedClass.getName();
1283 resourceName =
1284 resourceName.substring(
1285 resourceName.lastIndexOf(".") + 1,
1286 resourceName.length()
1287 ) + ".class";
1288 InputStream is = annotatedClass.getResourceAsStream(resourceName);
1289 if (is != null)
1290 {
1291 if (tracker.track(name, is))
1292 getLog().debug("New or modified class: " + name);
1293 else
1294 getLog().debug("Unchanged class: " + name);
1295 sources.addAnnotatedClass(annotatedClass);
1296 packageName = annotatedClass.getPackage().getName();
1297 }
1298 else
1299 {
1300 getLog().error("cannot find ressource " + resourceName + " for class " + name);
1301 error = true;
1302 }
1303 }
1304 catch(ClassLoadingException e)
1305 {
1306 packageName = name;
1307 }
1308 if (error)
1309 {
1310 throw new MojoExecutionException("error while inspecting annotated class " + name);
1311 }
1312
1313 while (packageName != null)
1314 {
1315 if (packages.contains(packageName))
1316 return;
1317 String resource = packageName.replace('.', '/') + "/package-info.class";
1318 InputStream is = classLoaderService.locateResourceStream(resource);
1319 if (is == null)
1320 {
1321
1322 getLog().debug("Package " + packageName + " is not annotated.");
1323 }
1324 else
1325 {
1326 if (tracker.track(packageName, is))
1327 getLog().debug("New or modified package: " + packageName);
1328 else
1329 getLog().debug("Unchanged package: " + packageName);
1330 getLog().info("Adding annotations from package " + packageName);
1331 sources.addPackage(packageName);
1332 }
1333 packages.add(packageName);
1334 int i = packageName.lastIndexOf('.');
1335 if (i < 0)
1336 packageName = null;
1337 else
1338 packageName = packageName.substring(0,i);
1339 }
1340 }
1341 catch (Exception e)
1342 {
1343 getLog().error("Error while adding the annotated class " + name, e);
1344 throw new MojoFailureException(e.getMessage());
1345 }
1346 }
1347
1348 private ParsedPersistenceXmlDescriptor loadPersistenceUnit(
1349 ClassLoaderService classLoaderService,
1350 Properties properties
1351 )
1352 throws
1353 MojoFailureException
1354 {
1355 PersistenceXmlParser parser =
1356 new PersistenceXmlParser(
1357 classLoaderService,
1358 PersistenceUnitTransactionType.RESOURCE_LOCAL
1359 );
1360
1361 Map<String, ParsedPersistenceXmlDescriptor> units =
1362 parser.doResolve(properties);
1363
1364 if (persistenceUnit == null)
1365 {
1366 Iterator<String> names = units.keySet().iterator();
1367 if (!names.hasNext())
1368 {
1369 getLog().info("Found no META-INF/persistence.xml.");
1370 return null;
1371 }
1372
1373 String name = names.next();
1374 if (!names.hasNext())
1375 {
1376 getLog().info("Using persistence-unit " + name);
1377 return units.get(name);
1378 }
1379
1380 StringBuilder builder = new StringBuilder();
1381 builder.append("No name provided and multiple persistence units found: ");
1382 builder.append(name);
1383 while(names.hasNext())
1384 {
1385 builder.append(", ");
1386 builder.append(names.next());
1387 }
1388 builder.append('.');
1389 throw new MojoFailureException(builder.toString());
1390 }
1391
1392 if (units.containsKey(persistenceUnit))
1393 {
1394 getLog().info("Using configured persistence-unit " + persistenceUnit);
1395 return units.get(persistenceUnit);
1396 }
1397
1398 throw new MojoFailureException("Could not find persistence-unit " + persistenceUnit);
1399 }
1400
1401
1402 public static void printStrackTrace(StringBuilder builder, Throwable t)
1403 {
1404 while (t != null)
1405 {
1406 builder.append("\n\tCause: ");
1407 builder.append(t.getMessage() == null ? "" : t.getMessage().replaceAll("\\s+", " "));
1408 for (StackTraceElement trace : t.getStackTrace())
1409 {
1410 builder.append("\n\t");
1411 builder.append(trace.getClassName());
1412 builder.append(".");
1413 builder.append(trace.getMethodName());
1414 builder.append("():");
1415 builder.append(trace.getLineNumber());
1416 }
1417 t = t.getCause();
1418 }
1419 }
1420 }