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