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.plugin.AbstractMojo;
23 import org.apache.maven.plugin.MojoExecution;
24 import org.apache.maven.plugin.MojoExecutionException;
25 import org.apache.maven.plugin.MojoFailureException;
26 import org.apache.maven.plugin.logging.Log;
27 import org.apache.maven.plugins.annotations.Component;
28 import org.apache.maven.plugins.annotations.Parameter;
29 import org.apache.maven.project.MavenProject;
30 import org.codehaus.mojo.jaxb2.shared.FileSystemUtilities;
31 import org.codehaus.mojo.jaxb2.shared.Validate;
32 import org.codehaus.mojo.jaxb2.shared.environment.classloading.ThreadContextClassLoaderBuilder;
33 import org.codehaus.mojo.jaxb2.shared.filters.Filter;
34 import org.codehaus.mojo.jaxb2.shared.filters.pattern.PatternFileFilter;
35 import org.codehaus.mojo.jaxb2.shared.version.DependencyInfo;
36 import org.codehaus.mojo.jaxb2.shared.version.DependsFileParser;
37 import org.sonatype.plexus.build.incremental.BuildContext;
38
39 import java.io.File;
40 import java.io.IOException;
41 import java.net.URL;
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.Collections;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.SortedMap;
48 import java.util.TreeMap;
49 import java.util.regex.Pattern;
50
51
52
53
54
55
56
57 public abstract class AbstractJaxbMojo extends AbstractMojo {
58
59
60
61
62 public static final String STANDARD_EPISODE_FILENAME = "sun-jaxb.episode";
63
64
65
66
67 public static final String NEWLINE = System.getProperty("line.separator");
68
69
70
71
72 public static final Pattern CONTAINS_WHITESPACE = Pattern.compile("(\\S*\\s+\\S*)+", Pattern.UNICODE_CASE);
73
74
75
76
77
78 public static final List<Filter<File>> STANDARD_EXCLUDE_FILTERS;
79
80 private static final List<String> RELEVANT_GROUPIDS =
81 Arrays.asList("org.glassfish.jaxb", "javax.xml.bind");
82 private static final String OWN_ARTIFACT_ID = "jaxb2-maven-plugin";
83 private static final String SYSTEM_FILE_ENCODING_PROPERTY = "file.encoding";
84 private static final String[] STANDARD_EXCLUDE_SUFFIXES = {"README.*", "\\.xml", "\\.txt"};
85
86 static {
87
88
89 final List<Filter<File>> tmp = new ArrayList<Filter<File>>();
90 tmp.add(new PatternFileFilter(Arrays.asList(STANDARD_EXCLUDE_SUFFIXES), true));
91
92
93 STANDARD_EXCLUDE_FILTERS = (List<Filter<File>>) Collections.unmodifiableList(tmp);
94 }
95
96
97
98
99
100 @Component
101 private BuildContext buildContext;
102
103
104
105
106 @Parameter(defaultValue = "${project}", readonly = true)
107 private MavenProject project;
108
109
110
111
112
113
114
115 @Parameter(defaultValue = "${mojoExecution}", readonly = true)
116 private MojoExecution execution;
117
118
119
120
121
122
123
124
125
126
127 @Parameter(defaultValue = "${project.build.directory}/jaxb2", readonly = true, required = true)
128 protected File staleFileDirectory;
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145 @Parameter(defaultValue = "${project.build.sourceEncoding}")
146 private String encoding;
147
148
149
150
151
152
153
154 protected final BuildContext getBuildContext() {
155 return getInjectedObject(buildContext, "buildContext");
156 }
157
158
159
160
161 protected final MavenProject getProject() {
162 return getInjectedObject(project, "project");
163 }
164
165
166
167
168 public MojoExecution getExecution() {
169 return getInjectedObject(execution, "execution");
170 }
171
172
173
174
175 @Override
176 public final void execute() throws MojoExecutionException, MojoFailureException {
177
178
179 final Log log = getLog();
180 final boolean isDebugEnabled = log.isDebugEnabled();
181 final boolean isInfoEnabled = log.isInfoEnabled();
182
183
184 if (shouldExecutionBeSkipped()) {
185
186 if (isDebugEnabled) {
187 log.debug("Skipping execution, as instructed.");
188 }
189 return;
190 }
191
192
193 if (isDebugEnabled) {
194 logPluginAndJaxbDependencyInfo();
195 }
196
197
198 if (isReGenerationRequired()) {
199
200 if (performExecution()) {
201
202
203
204 updateStaleFileTimestamp();
205
206
207 buildContext.refresh(getOutputDirectory());
208
209 } else if (isInfoEnabled) {
210 log.info("Not updating staleFile timestamp as instructed.");
211 }
212 } else if (isInfoEnabled) {
213 log.info("No changes detected in schema or binding files - skipping JAXB generation.");
214 }
215 }
216
217
218
219
220
221
222 protected abstract boolean shouldExecutionBeSkipped();
223
224
225
226
227
228 protected abstract boolean isReGenerationRequired();
229
230
231
232
233
234
235
236
237
238
239
240 protected abstract boolean performExecution() throws MojoExecutionException, MojoFailureException;
241
242
243
244
245
246
247
248
249 protected abstract List<URL> getSources();
250
251
252
253
254
255
256 protected abstract File getOutputDirectory();
257
258
259
260
261
262
263
264
265
266 protected abstract List<String> getClasspath() throws MojoExecutionException;
267
268
269
270
271
272
273
274
275 @SuppressWarnings("all")
276 protected void warnAboutIncorrectPluginConfiguration(final String propertyName, final String description) {
277
278 final StringBuilder builder = new StringBuilder();
279 builder.append("\n+=================== [Incorrect Plugin Configuration Detected]\n");
280 builder.append("|\n");
281 builder.append("| Property : " + propertyName + "\n");
282 builder.append("| Problem : " + description + "\n");
283 builder.append("|\n");
284 builder.append("+=================== [End Incorrect Plugin Configuration Detected]\n\n");
285 getLog().warn(builder.toString().replace("\n", NEWLINE));
286 }
287
288
289
290
291
292
293 protected final String[] logAndReturnToolArguments(final String[] arguments, final String toolName) {
294
295
296 Validate.notNull(arguments, "arguments");
297
298 if (getLog().isDebugEnabled()) {
299
300 final StringBuilder argBuilder = new StringBuilder();
301 argBuilder.append("\n+=================== [" + arguments.length + " " + toolName + " Arguments]\n");
302 argBuilder.append("|\n");
303 for (int i = 0; i < arguments.length; i++) {
304 argBuilder.append("| [").append(i).append("]: ").append(arguments[i]).append("\n");
305 }
306 argBuilder.append("|\n");
307 argBuilder.append("+=================== [End " + arguments.length + " " + toolName + " Arguments]\n\n");
308 getLog().debug(argBuilder.toString().replace("\n", NEWLINE));
309 }
310
311
312 return arguments;
313 }
314
315
316
317
318
319
320
321
322
323 protected abstract String getStaleFileName();
324
325
326
327
328
329
330 protected final File getStaleFile() {
331 final String staleFileName = "."
332 + (getExecution() == null ? "nonExecutionJaxb" : getExecution().getExecutionId())
333 + "-" + getStaleFileName();
334 return new File(staleFileDirectory, staleFileName);
335 }
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353 protected final String getEncoding(final boolean warnIfConfiguredEncodingDiffersFromFileEncoding) {
354
355
356 final boolean configuredEncoding = encoding != null;
357 final String fileEncoding = System.getProperty(SYSTEM_FILE_ENCODING_PROPERTY);
358 final String effectiveEncoding = configuredEncoding ? encoding : fileEncoding;
359
360
361 if (warnIfConfiguredEncodingDiffersFromFileEncoding
362 && !fileEncoding.equalsIgnoreCase(effectiveEncoding)
363 && getLog().isWarnEnabled()) {
364 getLog().warn("Configured encoding [" + effectiveEncoding
365 + "] differs from encoding given in system property '" + SYSTEM_FILE_ENCODING_PROPERTY
366 + "' [" + fileEncoding + "]");
367 }
368
369 if (getLog().isDebugEnabled()) {
370 getLog().debug("Using " + (configuredEncoding ? "explicitly configured" : "system property")
371 + " encoding [" + effectiveEncoding + "]");
372 }
373
374
375 return effectiveEncoding;
376 }
377
378
379
380
381
382
383
384
385
386
387 protected File getEpisodeFile(final String customEpisodeFileName) throws MojoExecutionException {
388
389
390 final String effectiveEpisodeFileName = customEpisodeFileName == null
391 ? "sun-jaxb.episode"
392 : customEpisodeFileName;
393 Validate.notEmpty(effectiveEpisodeFileName, "effectiveEpisodeFileName");
394
395
396 final File generatedMetaInfDirectory = new File(getOutputDirectory(), "META-INF");
397
398 if (!generatedMetaInfDirectory.exists()) {
399
400 FileSystemUtilities.createDirectory(generatedMetaInfDirectory, false);
401 if (getLog().isDebugEnabled()) {
402 getLog().debug("Created episode directory ["
403 + FileSystemUtilities.getCanonicalPath(generatedMetaInfDirectory) + "]: " +
404 generatedMetaInfDirectory.exists());
405 }
406 }
407
408
409 return new File(generatedMetaInfDirectory, effectiveEpisodeFileName);
410 }
411
412
413
414
415
416 private void logPluginAndJaxbDependencyInfo() {
417
418 if (getLog().isDebugEnabled()) {
419 final StringBuilder builder = new StringBuilder();
420 builder.append("\n+=================== [Brief Plugin Build Dependency Information]\n");
421 builder.append("|\n");
422 builder.append("| Note: These dependencies pertain to what was used to build *the plugin*.\n");
423 builder.append("| Check project dependencies to see the ones used in *your build*.\n");
424 builder.append("|\n");
425
426
427 final SortedMap<String, String> versionMap = DependsFileParser.getVersionMap(OWN_ARTIFACT_ID);
428
429 builder.append("|\n");
430 builder.append("| Plugin's own information\n");
431 builder.append("| GroupId : " + versionMap.get(DependsFileParser.OWN_GROUPID_KEY) + "\n");
432 builder.append("| ArtifactID : " + versionMap.get(DependsFileParser.OWN_ARTIFACTID_KEY) + "\n");
433 builder.append("| Version : " + versionMap.get(DependsFileParser.OWN_VERSION_KEY) + "\n");
434 builder.append("| Buildtime : " + versionMap.get(DependsFileParser.BUILDTIME_KEY) + "\n");
435 builder.append("|\n");
436 builder.append("| Plugin's JAXB-related dependencies\n");
437 builder.append("|\n");
438
439 final SortedMap<String, DependencyInfo> diMap = DependsFileParser.createDependencyInfoMap(versionMap);
440
441 int dependencyIndex = 0;
442 for (Map.Entry<String, DependencyInfo> current : diMap.entrySet()) {
443
444 final String key = current.getKey().trim();
445 for (String currentRelevantGroupId : RELEVANT_GROUPIDS) {
446 if (key.startsWith(currentRelevantGroupId)) {
447
448 final DependencyInfo di = current.getValue();
449 builder.append("| " + (++dependencyIndex) + ") [" + di.getArtifactId() + "]\n");
450 builder.append("| GroupId : " + di.getGroupId() + "\n");
451 builder.append("| ArtifactID : " + di.getArtifactId() + "\n");
452 builder.append("| Version : " + di.getVersion() + "\n");
453 builder.append("| Scope : " + di.getScope() + "\n");
454 builder.append("| Type : " + di.getType() + "\n");
455 builder.append("|\n");
456 }
457 }
458 }
459
460 builder.append("+=================== [End Brief Plugin Build Dependency Information]\n\n");
461 getLog().debug(builder.toString().replace("\n", NEWLINE));
462 }
463 }
464
465 private <T> T getInjectedObject(final T objectOrNull, final String objectName) {
466
467 if (objectOrNull == null) {
468 getLog().error(
469 "Found null '" + objectName + "', implying that Maven @Component injection was not done properly.");
470 }
471
472 return objectOrNull;
473 }
474
475 private void updateStaleFileTimestamp() throws MojoExecutionException {
476
477 final File staleFile = getStaleFile();
478 if (!staleFile.exists()) {
479
480
481 FileSystemUtilities.createDirectory(staleFile.getParentFile(), false);
482
483 try {
484 staleFile.createNewFile();
485
486 if (getLog().isDebugEnabled()) {
487 getLog().debug("Created staleFile [" + FileSystemUtilities.getCanonicalPath(staleFile) + "]");
488 }
489 } catch (IOException e) {
490 throw new MojoExecutionException("Could not create staleFile.", e);
491 }
492
493 } else {
494 if (!staleFile.setLastModified(System.currentTimeMillis())) {
495 getLog().warn("Failed updating modification time of staleFile ["
496 + FileSystemUtilities.getCanonicalPath(staleFile) + "]");
497 }
498 }
499 }
500
501
502
503
504 protected void logSystemPropertiesAndBasedir() {
505 if (getLog().isDebugEnabled()) {
506
507 final StringBuilder builder = new StringBuilder();
508
509 builder.append("\n+=================== [System properties]\n");
510 builder.append("|\n");
511
512
513 final SortedMap<String, Object> props = new TreeMap<String, Object>();
514 props.put("basedir", FileSystemUtilities.getCanonicalPath(getProject().getBasedir()));
515
516 for (Map.Entry current : System.getProperties().entrySet()) {
517 props.put("" + current.getKey(), current.getValue());
518 }
519 for (Map.Entry<String, Object> current : props.entrySet()) {
520 builder.append("| [" + current.getKey() + "]: " + current.getValue() + "\n");
521 }
522
523 builder.append("|\n");
524 builder.append("+=================== [End System properties]\n");
525
526
527 builder.append("\n+=================== [ThreadContext ClassLoader Root Resources]\n");
528 builder.append("|\n");
529
530 for (URL current : ThreadContextClassLoaderBuilder.getRootResources(
531 Thread.currentThread().getContextClassLoader())) {
532 builder.append("| ").append(current.toString()).append("\n");
533 }
534 builder.append("|\n");
535 builder.append("+=================== [End ThreadContext ClassLoader Root Resources]\n");
536
537
538 getLog().debug(builder.toString().replace("\n", NEWLINE));
539 }
540 }
541 }