1 package org.codehaus.mojo.jaxb2;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.model.Resource;
23 import org.apache.maven.plugin.AbstractMojo;
24 import org.apache.maven.plugin.MojoExecution;
25 import org.apache.maven.plugin.MojoExecutionException;
26 import org.apache.maven.plugin.MojoFailureException;
27 import org.apache.maven.plugin.logging.Log;
28 import org.apache.maven.plugins.annotations.Component;
29 import org.apache.maven.plugins.annotations.Parameter;
30 import org.apache.maven.project.MavenProject;
31 import org.codehaus.mojo.jaxb2.shared.FileSystemUtilities;
32 import org.codehaus.mojo.jaxb2.shared.Validate;
33 import org.codehaus.mojo.jaxb2.shared.environment.EnvironmentFacet;
34 import org.codehaus.mojo.jaxb2.shared.filters.Filter;
35 import org.codehaus.mojo.jaxb2.shared.filters.pattern.FileFilterAdapter;
36 import org.codehaus.mojo.jaxb2.shared.filters.pattern.PatternFileFilter;
37 import org.codehaus.mojo.jaxb2.shared.version.DependencyInfo;
38 import org.codehaus.mojo.jaxb2.shared.version.DependsFileParser;
39 import org.sonatype.plexus.build.incremental.BuildContext;
40
41 import java.io.File;
42 import java.io.FileFilter;
43 import java.io.IOException;
44 import java.net.URL;
45 import java.nio.file.Path;
46 import java.nio.file.Paths;
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.Collections;
50 import java.util.List;
51 import java.util.Locale;
52 import java.util.Map;
53 import java.util.SortedMap;
54 import java.util.TreeMap;
55 import java.util.concurrent.atomic.AtomicInteger;
56 import java.util.regex.Pattern;
57
58
59
60
61
62
63
64 public abstract class AbstractJaxbMojo extends AbstractMojo {
65
66
67
68
69 public static final String STANDARD_EPISODE_FILENAME = "sun-jaxb.episode";
70
71
72
73
74
75 public static final String PACKAGE_INFO_FILENAME = "package-info.java";
76
77
78
79
80 public static final String NEWLINE = System.getProperty("line.separator");
81
82
83
84
85 public static final Pattern CONTAINS_WHITESPACE = Pattern.compile("(\\S*\\s+\\S*)+", Pattern.UNICODE_CASE);
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118 public static final List<Filter<File>> STANDARD_EXCLUDE_FILTERS;
119
120 private static final List<String> RELEVANT_GROUPIDS =
121 Arrays.asList("com.sun.xml.bind", "jakarta.xml.bind");
122 private static final String OWN_ARTIFACT_ID = "jaxb2-maven-plugin";
123 private static final String SYSTEM_FILE_ENCODING_PROPERTY = "file.encoding";
124 private static final String[] STANDARD_EXCLUDE_SUFFIXES = {"README.*", "\\.xml", "\\.txt"};
125 private static final String[] STANDARD_PRELOADED_CLASSES = {
126 "com.sun.tools.xjc.addon.episode.package-info",
127 "com.sun.tools.xjc.reader.xmlschema.bindinfo.package-info",
128 "org.glassfish.jaxb.core.v2.model.core.package-info",
129 "org.glassfish.jaxb.runtime.v2.model.runtime.package-info",
130 "org.glassfish.jaxb.core.v2.schemagen.episode.package-info",
131 "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.package-info"
132 };
133
134 static {
135
136
137 final List<Filter<File>> tmp = new ArrayList<Filter<File>>();
138 tmp.add(new PatternFileFilter(Arrays.asList(STANDARD_EXCLUDE_SUFFIXES), true));
139 tmp.add(new FileFilterAdapter(new FileFilter() {
140 @Override
141 public boolean accept(final File aFileOrDir) {
142
143
144 if (aFileOrDir == null) {
145 return false;
146 }
147
148 final String name = aFileOrDir.getName();
149
150
151 return name.startsWith(".")
152 || (aFileOrDir.isDirectory() && name.equals("CVS"));
153
154 }
155 }));
156
157
158 STANDARD_EXCLUDE_FILTERS = Collections.unmodifiableList(tmp);
159
160
161
162 try {
163
164 final ClassLoader cl = AbstractJaxbMojo.class.getClassLoader();
165
166 for(String current : STANDARD_PRELOADED_CLASSES) {
167 cl.loadClass(current);
168 }
169
170 } catch (ClassNotFoundException ex) {
171 throw new Error(ex);
172 }
173 }
174
175
176
177
178
179 @Component
180 private BuildContext buildContext;
181
182
183
184
185 @Parameter(defaultValue = "${project}", readonly = true)
186 private MavenProject project;
187
188
189
190
191
192
193
194 @Parameter(defaultValue = "${mojoExecution}", readonly = true)
195 private MojoExecution execution;
196
197
198
199
200
201
202
203
204
205
206 @Parameter(defaultValue = "${project.build.directory}/jaxb2", readonly = true, required = true)
207 protected File staleFileDirectory;
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224 @Parameter(defaultValue = "${project.build.sourceEncoding}")
225 private String encoding;
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247 @Parameter(required = false)
248 protected String locale;
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274 @Parameter(required = false)
275 protected List<EnvironmentFacet> extraFacets;
276
277
278
279
280
281
282
283 protected abstract void addResource(final Resource resource);
284
285
286
287
288
289
290
291 protected final BuildContext getBuildContext() {
292 return getInjectedObject(buildContext, "buildContext");
293 }
294
295
296
297
298 protected final MavenProject getProject() {
299 return getInjectedObject(project, "project");
300 }
301
302
303
304
305 public MojoExecution getExecution() {
306 return getInjectedObject(execution, "execution");
307 }
308
309
310
311
312 @Override
313 public final void execute() throws MojoExecutionException, MojoFailureException {
314
315
316 final Log log = getLog();
317 final boolean isDebugEnabled = log.isDebugEnabled();
318 final boolean isInfoEnabled = log.isInfoEnabled();
319
320
321 if (shouldExecutionBeSkipped()) {
322
323 if (isDebugEnabled) {
324 log.debug("Skipping execution, as instructed.");
325 }
326 return;
327 }
328
329
330 if (isDebugEnabled) {
331 logPluginAndJaxbDependencyInfo();
332 }
333
334
335 if (isReGenerationRequired()) {
336
337 if (performExecution()) {
338
339
340
341 updateStaleFileTimestamp();
342
343
344 buildContext.refresh(getOutputDirectory());
345
346 } else if (isInfoEnabled) {
347 log.info("Not updating staleFile timestamp as instructed.");
348 }
349 } else if (isInfoEnabled) {
350 log.info("No changes detected in schema or binding files - skipping JAXB generation.");
351 }
352
353
354 if (getOutputDirectory().exists() && getOutputDirectory().isDirectory()) {
355
356 final String canonicalPathToOutputDirectory = FileSystemUtilities.getCanonicalPath(getOutputDirectory());
357
358 if (log.isDebugEnabled()) {
359 log.debug("Adding existing JAXB outputDirectory [" + canonicalPathToOutputDirectory
360 + "] to Maven's sources.");
361 }
362
363
364 getProject().addCompileSourceRoot(canonicalPathToOutputDirectory);
365 }
366 }
367
368
369
370
371
372
373 protected abstract boolean shouldExecutionBeSkipped();
374
375
376
377
378
379 protected abstract boolean isReGenerationRequired();
380
381
382
383
384
385
386
387
388
389
390
391 protected abstract boolean performExecution() throws MojoExecutionException, MojoFailureException;
392
393
394
395
396
397
398
399
400 protected abstract List<URL> getSources();
401
402
403
404
405
406
407 protected abstract File getOutputDirectory();
408
409
410
411
412
413
414
415
416
417 protected abstract List<String> getClasspath() throws MojoExecutionException;
418
419
420
421
422
423
424
425
426 @SuppressWarnings("all")
427 protected void warnAboutIncorrectPluginConfiguration(final String propertyName, final String description) {
428
429 final StringBuilder builder = new StringBuilder();
430 builder.append("\n+=================== [Incorrect Plugin Configuration Detected]\n");
431 builder.append("|\n");
432 builder.append("| Property : " + propertyName + "\n");
433 builder.append("| Problem : " + description + "\n");
434 builder.append("|\n");
435 builder.append("+=================== [End Incorrect Plugin Configuration Detected]\n\n");
436 getLog().warn(builder.toString().replace("\n", NEWLINE));
437 }
438
439
440
441
442
443
444 protected final String[] logAndReturnToolArguments(final String[] arguments, final String toolName) {
445
446
447 Validate.notNull(arguments, "arguments");
448
449 if (getLog().isDebugEnabled()) {
450
451 final StringBuilder argBuilder = new StringBuilder();
452 argBuilder.append("\n+=================== [" + arguments.length + " " + toolName + " Arguments]\n");
453 argBuilder.append("|\n");
454 for (int i = 0; i < arguments.length; i++) {
455 argBuilder.append("| [").append(i).append("]: ").append(arguments[i]).append("\n");
456 }
457 argBuilder.append("|\n");
458 argBuilder.append("+=================== [End " + arguments.length + " " + toolName + " Arguments]\n\n");
459 getLog().debug(argBuilder.toString().replace("\n", NEWLINE));
460 }
461
462
463 return arguments;
464 }
465
466
467
468
469
470
471
472
473
474 protected abstract String getStaleFileName();
475
476
477
478
479
480
481 protected final File getStaleFile() {
482 final String staleFileName = "."
483 + (getExecution() == null ? "nonExecutionJaxb" : getExecution().getExecutionId())
484 + "-" + getStaleFileName();
485 return new File(staleFileDirectory, staleFileName);
486 }
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502 protected final String getEncoding(final boolean warnIfPlatformEncoding) {
503
504
505 final boolean configuredEncoding = encoding != null;
506 final String fileEncoding = System.getProperty(SYSTEM_FILE_ENCODING_PROPERTY);
507 final String effectiveEncoding = configuredEncoding ? encoding : fileEncoding;
508
509
510 if (!configuredEncoding && warnIfPlatformEncoding) {
511 getLog().warn("Using platform encoding [" + effectiveEncoding + "], i.e. build is platform dependent!");
512 } else if (getLog().isDebugEnabled()) {
513 getLog().debug("Using " + (configuredEncoding ? "explicitly configured" : "system property")
514 + " encoding [" + effectiveEncoding + "]");
515 }
516
517
518 return effectiveEncoding;
519 }
520
521
522
523
524
525
526
527
528
529
530
531 protected File getEpisodeFile(final String episodeFileName) throws MojoExecutionException {
532
533
534 final String executionID = getExecution() != null && getExecution().getExecutionId() != null
535 ? getExecution().getExecutionId()
536 : null;
537
538 final String effectiveEpisodeFileName = episodeFileName == null
539 ? (executionID == null ? STANDARD_EPISODE_FILENAME : "episode_" + executionID)
540 : episodeFileName;
541 if (effectiveEpisodeFileName.isEmpty()) {
542 throw new MojoExecutionException("Cannot handle null or empty JAXB Episode filename. "
543 + "Check 'episodeFileName' configuration property.");
544 }
545
546
547 final Path episodePath;
548 final File generatedJaxbEpisodeDirectory;
549 try {
550 final Path path = Paths.get(getOutputDirectory().getAbsolutePath(), "META-INF", "JAXB");
551 episodePath = java.nio.file.Files.createDirectories(path);
552 generatedJaxbEpisodeDirectory = episodePath.toFile();
553
554 if (getLog().isInfoEnabled()) {
555 getLog().info("Created EpisodePath [" + episodePath.toString() + "]: " +
556 (generatedJaxbEpisodeDirectory.exists() && generatedJaxbEpisodeDirectory.isDirectory()));
557 }
558
559 } catch (IOException e) {
560 throw new MojoExecutionException("Could not create output directory.", e);
561 }
562
563 if (!generatedJaxbEpisodeDirectory.exists() || !generatedJaxbEpisodeDirectory.isDirectory()) {
564 throw new MojoExecutionException("Could not create directory [" + episodePath.toString() + "]");
565 }
566
567
568 File episodeFile = new File(generatedJaxbEpisodeDirectory, effectiveEpisodeFileName + ".xjb");
569 final AtomicInteger index = new AtomicInteger(1);
570 while (episodeFile.exists()) {
571 episodeFile = new File(generatedJaxbEpisodeDirectory,
572 effectiveEpisodeFileName + "_" + index.getAndIncrement() + ".xjb");
573 }
574
575
576 final Resource outputDirectoryResource = new Resource();
577 outputDirectoryResource.setDirectory(getOutputDirectory().getAbsolutePath());
578 outputDirectoryResource.setIncludes(Collections.singletonList("**/" + episodeFile.getName()));
579 this.addResource(outputDirectoryResource);
580
581
582 return episodeFile;
583 }
584
585
586
587
588
589 private void logPluginAndJaxbDependencyInfo() {
590
591 if (getLog().isDebugEnabled()) {
592 final StringBuilder builder = new StringBuilder();
593 builder.append("\n+=================== [Brief Plugin Build Dependency Information]\n");
594 builder.append("|\n");
595 builder.append("| Note: These dependencies pertain to what was used to build *the plugin*.\n");
596 builder.append("| Check project dependencies to see the ones used in *your build*.\n");
597 builder.append("|\n");
598
599
600 final SortedMap<String, String> versionMap = DependsFileParser.getVersionMap(OWN_ARTIFACT_ID);
601
602 builder.append("|\n");
603 builder.append("| Plugin's own information\n");
604 builder.append("| GroupId : " + versionMap.get(DependsFileParser.OWN_GROUPID_KEY) + "\n");
605 builder.append("| ArtifactID : " + versionMap.get(DependsFileParser.OWN_ARTIFACTID_KEY) + "\n");
606 builder.append("| Version : " + versionMap.get(DependsFileParser.OWN_VERSION_KEY) + "\n");
607 builder.append("| Buildtime : " + versionMap.get(DependsFileParser.BUILDTIME_KEY) + "\n");
608 builder.append("|\n");
609 builder.append("| Plugin's JAXB-related dependencies\n");
610 builder.append("|\n");
611
612 final SortedMap<String, DependencyInfo> diMap = DependsFileParser.createDependencyInfoMap(versionMap);
613
614 int dependencyIndex = 0;
615 for (Map.Entry<String, DependencyInfo> current : diMap.entrySet()) {
616
617 final String key = current.getKey().trim();
618 for (String currentRelevantGroupId : RELEVANT_GROUPIDS) {
619 if (key.startsWith(currentRelevantGroupId)) {
620
621 final DependencyInfo di = current.getValue();
622 builder.append("| " + (++dependencyIndex) + ") [" + di.getArtifactId() + "]\n");
623 builder.append("| GroupId : " + di.getGroupId() + "\n");
624 builder.append("| ArtifactID : " + di.getArtifactId() + "\n");
625 builder.append("| Version : " + di.getVersion() + "\n");
626 builder.append("| Scope : " + di.getScope() + "\n");
627 builder.append("| Type : " + di.getType() + "\n");
628 builder.append("|\n");
629 }
630 }
631 }
632
633 builder.append("+=================== [End Brief Plugin Build Dependency Information]\n\n");
634 getLog().debug(builder.toString().replace("\n", NEWLINE));
635 }
636 }
637
638 private <T> T getInjectedObject(final T objectOrNull, final String objectName) {
639
640 if (objectOrNull == null) {
641 getLog().error(
642 "Found null '" + objectName + "', implying that Maven @Component injection was not done properly.");
643 }
644
645 return objectOrNull;
646 }
647
648 private void updateStaleFileTimestamp() throws MojoExecutionException {
649
650 final File staleFile = getStaleFile();
651 if (!staleFile.exists()) {
652
653
654 FileSystemUtilities.createDirectory(staleFile.getParentFile(), false);
655
656 try {
657 staleFile.createNewFile();
658
659 if (getLog().isDebugEnabled()) {
660 getLog().debug("Created staleFile [" + FileSystemUtilities.getCanonicalPath(staleFile) + "]");
661 }
662 } catch (IOException e) {
663 throw new MojoExecutionException("Could not create staleFile.", e);
664 }
665
666 } else {
667 if (!staleFile.setLastModified(System.currentTimeMillis())) {
668 getLog().warn("Failed updating modification time of staleFile ["
669 + FileSystemUtilities.getCanonicalPath(staleFile) + "]");
670 }
671 }
672 }
673
674
675
676
677 protected void logSystemPropertiesAndBasedir() {
678 if (getLog().isDebugEnabled()) {
679
680 final StringBuilder builder = new StringBuilder();
681
682 builder.append("\n+=================== [System properties]\n");
683 builder.append("|\n");
684
685
686 final SortedMap<String, Object> props = new TreeMap<String, Object>();
687 props.put("basedir", FileSystemUtilities.getCanonicalPath(getProject().getBasedir()));
688
689 for (Map.Entry<Object, Object> current : System.getProperties().entrySet()) {
690 props.put("" + current.getKey(), current.getValue());
691 }
692 for (Map.Entry<String, Object> current : props.entrySet()) {
693 builder.append("| [" + current.getKey() + "]: " + current.getValue() + "\n");
694 }
695
696 builder.append("|\n");
697 builder.append("+=================== [End System properties]\n");
698
699
700 getLog().debug(builder.toString().replace("\n", NEWLINE));
701 }
702 }
703 }