1 package org.codehaus.mojo.jaxb2.javageneration;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import com.sun.tools.xjc.Driver;
23 import org.apache.maven.plugin.MojoExecutionException;
24 import org.apache.maven.plugin.MojoFailureException;
25 import org.apache.maven.plugins.annotations.Parameter;
26 import org.apache.maven.settings.Proxy;
27 import org.apache.maven.settings.Settings;
28 import org.codehaus.mojo.jaxb2.AbstractJaxbMojo;
29 import org.codehaus.mojo.jaxb2.NoSchemasException;
30 import org.codehaus.mojo.jaxb2.shared.FileSystemUtilities;
31 import org.codehaus.mojo.jaxb2.shared.arguments.ArgumentBuilder;
32 import org.codehaus.mojo.jaxb2.shared.environment.EnvironmentFacet;
33 import org.codehaus.mojo.jaxb2.shared.environment.ToolExecutionEnvironment;
34 import org.codehaus.mojo.jaxb2.shared.environment.classloading.ThreadContextClassLoaderBuilder;
35 import org.codehaus.mojo.jaxb2.shared.environment.locale.LocaleFacet;
36 import org.codehaus.mojo.jaxb2.shared.environment.logging.LoggingHandlerEnvironmentFacet;
37 import org.codehaus.mojo.jaxb2.shared.environment.sysprops.SystemPropertyChangeEnvironmentFacet;
38 import org.codehaus.mojo.jaxb2.shared.environment.sysprops.SystemPropertySaveEnvironmentFacet;
39 import org.codehaus.plexus.util.FileUtils;
40 import org.codehaus.plexus.util.IOUtil;
41
42 import java.io.File;
43 import java.io.FileWriter;
44 import java.net.HttpURLConnection;
45 import java.net.URL;
46 import java.net.URLConnection;
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.List;
50
51
52
53
54
55
56
57
58
59
60 public abstract class AbstractJavaGeneratorMojo extends AbstractJaxbMojo {
61
62 private static final List<String> PROXY_PROPERTY_KEYS = Arrays.asList("http.proxyHost", "http.proxyPort", "https.proxyHost", "https.proxyPort");
63
64 private static final int XJC_COMPLETED_OK = 0;
65
66
67
68
69
70
71 @Parameter
72 protected File catalog;
73
74
75
76
77
78
79
80
81
82
83
84 @Deprecated
85 @Parameter(defaultValue = "true")
86 protected boolean generateEpisode;
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 @Parameter
114 protected String episodeFileName;
115
116
117
118
119
120
121 @Parameter(defaultValue = "${settings}", readonly = true)
122 protected Settings settings;
123
124
125
126
127
128
129
130
131
132
133 @Parameter(defaultValue = "XmlSchema")
134 protected SourceContentType sourceType;
135
136
137
138
139
140
141
142
143
144 @Parameter(defaultValue = "false")
145 protected boolean noPackageLevelAnnotations;
146
147
148
149
150
151
152
153
154 @Parameter(defaultValue = "false")
155 protected boolean noGeneratedHeaderComments;
156
157
158
159
160
161
162
163 @Parameter(defaultValue = "false")
164 protected boolean addGeneratedAnnotation;
165
166
167
168
169
170
171
172
173
174 @Parameter(defaultValue = "false")
175 protected boolean laxSchemaValidation;
176
177
178
179
180
181 @Parameter(defaultValue = "false")
182 protected boolean quiet;
183
184
185
186
187
188 @Parameter(property = "xjc.verbose", defaultValue = "false")
189 protected boolean verbose;
190
191
192
193
194
195
196
197
198
199 @Parameter(defaultValue = "true")
200 protected boolean extension;
201
202
203
204
205
206
207 @Parameter(defaultValue = "true")
208 protected boolean failOnNoSchemas;
209
210
211
212
213 @Parameter(defaultValue = "true")
214 protected boolean clearOutputDir;
215
216
217
218
219
220
221
222
223 @Parameter(defaultValue = "false")
224 protected boolean readOnly;
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246 @Parameter(property = "xjc.arguments")
247 protected List<String> arguments;
248
249
250
251
252
253
254
255 @Parameter(defaultValue = "false")
256 private boolean enableIntrospection;
257
258
259
260
261
262
263
264 @Parameter
265 protected String packageName;
266
267
268
269
270
271
272
273
274 @Parameter
275 protected String target;
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301 @Parameter
302 protected String xsdPathWithinArtifact;
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325 @Parameter(defaultValue = "false")
326 protected boolean externalEntityProcessing;
327
328
329
330
331
332 @Override
333 protected boolean isReGenerationRequired() {
334
335
336
337
338
339
340
341
342
343 final File staleFile = getStaleFile();
344 final String debugPrefix = "StaleFile [" + FileSystemUtilities.getCanonicalPath(staleFile) + "]";
345
346 boolean stale = !staleFile.exists();
347 if (stale) {
348 getLog().debug(debugPrefix + " not found. JAXB (re-)generation required.");
349 } else {
350
351 final List<URL> sourceXSDs = getSources();
352 final List<File> sourceXJBs = getSourceXJBs();
353
354 if (getLog().isDebugEnabled()) {
355 getLog().debug(debugPrefix + " found. Checking timestamps on source XSD and XJB "
356 + "files to determine if JAXB (re-)generation is required.");
357 }
358
359 final long staleFileLastModified = staleFile.lastModified();
360 for (URL current : sourceXSDs) {
361
362 final URLConnection sourceXsdConnection;
363 try {
364 sourceXsdConnection = current.openConnection();
365 sourceXsdConnection.connect();
366 } catch (Exception e) {
367
368
369
370 stale = true;
371 break;
372 }
373
374 try {
375 if (sourceXsdConnection.getLastModified() > staleFileLastModified) {
376
377 if (getLog().isDebugEnabled()) {
378 getLog().debug(current.toString() + " is newer than the stale flag file.");
379 }
380 stale = true;
381 }
382 } finally {
383 if (sourceXsdConnection instanceof HttpURLConnection) {
384 ((HttpURLConnection) sourceXsdConnection).disconnect();
385 }
386 }
387 }
388
389 for (File current : sourceXJBs) {
390 if (current.lastModified() > staleFileLastModified) {
391
392 if (getLog().isDebugEnabled()) {
393 getLog().debug(FileSystemUtilities.getCanonicalPath(current)
394 + " is newer than the stale flag file.");
395 }
396
397 stale = true;
398 break;
399 }
400 }
401 }
402
403
404 return stale;
405 }
406
407
408
409
410 @Override
411 protected boolean performExecution() throws MojoExecutionException, MojoFailureException {
412
413 boolean updateStaleFileTimestamp = false;
414
415 try {
416
417
418 ToolExecutionEnvironment environment = null;
419 try {
420
421
422 final LocaleFacet localeFacet = locale == null ? null : LocaleFacet.createFor(locale, getLog());
423
424
425 environment = new ToolExecutionEnvironment(getLog(),
426 ThreadContextClassLoaderBuilder.createFor(this.getClass(), getLog(), getEncoding(false))
427 .addPaths(getClasspath()),
428 LoggingHandlerEnvironmentFacet.create(getLog(), getClass(), getEncoding(false)),
429 localeFacet);
430
431
432 if (extraFacets != null) {
433 for (EnvironmentFacet current : extraFacets) {
434 environment.add(current);
435 }
436 }
437
438
439 if (externalEntityProcessing) {
440
441 final List<SystemPropertyChangeEnvironmentFacet> sysPropChanges =
442 SystemPropertyChangeEnvironmentFacet.getBuilder(getLog())
443 .addOrChange("enableExternalEntityProcessing", "" + externalEntityProcessing)
444 .build();
445
446 for (SystemPropertyChangeEnvironmentFacet current : sysPropChanges) {
447 environment.add(current);
448 }
449 }
450
451
452 for (String key : PROXY_PROPERTY_KEYS) {
453 environment.add(new SystemPropertySaveEnvironmentFacet(key, getLog()));
454 }
455
456
457 environment.setup();
458
459
460 final String[] xjcArguments = getXjcArguments(environment.getClassPathAsArgument(), episodeFileName);
461
462
463 FileSystemUtilities.createDirectory(getOutputDirectory(), clearOutputDir);
464
465
466 final boolean reCreateEpisodeFileParentDirectory = generateEpisode && clearOutputDir;
467 if (reCreateEpisodeFileParentDirectory) {
468 getEpisodeFile(episodeFileName);
469 }
470
471
472 logSystemPropertiesAndBasedir();
473
474
475 if (XJC_COMPLETED_OK != Driver.run(xjcArguments, new XjcLogAdapter(getLog()))) {
476
477 final StringBuilder errorMsgBuilder = new StringBuilder();
478 errorMsgBuilder.append("\n+=================== [XJC Error]\n");
479 errorMsgBuilder.append("|\n");
480
481 final List<URL> sourceXSDs = getSources();
482 for (int i = 0; i < sourceXSDs.size(); i++) {
483 errorMsgBuilder.append("| " + i + ": ").append(sourceXSDs.get(i).toString()).append("\n");
484 }
485
486 errorMsgBuilder.append("|\n");
487 errorMsgBuilder.append("+=================== [End XJC Error]\n");
488 throw new MojoExecutionException(errorMsgBuilder.toString());
489 }
490
491
492 getBuildContext().refresh(getOutputDirectory());
493
494
495 updateStaleFileTimestamp = true;
496
497 } finally {
498
499 if (environment != null) {
500 environment.restore();
501 }
502 }
503
504
505 addGeneratedSourcesToProjectSourceRoot();
506
507
508 if (xsdPathWithinArtifact != null) {
509
510 final String buildOutputDirectory = getProject().getBuild().getOutputDirectory();
511 final File targetXsdDirectory = new File(buildOutputDirectory, xsdPathWithinArtifact);
512 FileUtils.forceMkdir(targetXsdDirectory);
513
514 for (URL current : getSources()) {
515
516 String fileName = null;
517 if ("file".equalsIgnoreCase(current.getProtocol())) {
518 fileName = new File(current.getPath()).getName();
519 } else if ("jar".equalsIgnoreCase(current.getProtocol())) {
520
521
522
523 final int bangIndex = current.toString().indexOf("!");
524 if (bangIndex == -1) {
525 throw new MojoExecutionException("Illegal JAR URL [" + current.toString()
526 + "]: lacks a '!'");
527 }
528
529 final String internalPath = current.toString().substring(bangIndex + 1);
530 fileName = new File(internalPath).getName();
531 } else {
532 throw new MojoExecutionException("Could not extract FileName from URL [" + current + "]");
533 }
534
535 final File targetFile = new File(targetXsdDirectory, fileName);
536 if (targetFile.exists()) {
537
538
539 getLog().warn("File [" + FileSystemUtilities.getCanonicalPath(targetFile)
540 + "] already exists. Not copying XSD file [" + current.getPath() + "] to it.");
541 }
542 IOUtil.copy(current.openStream(), new FileWriter(targetFile));
543 }
544
545
546 getBuildContext().refresh(targetXsdDirectory);
547 }
548 } catch (MojoExecutionException e) {
549 throw e;
550 } catch (NoSchemasException e) {
551 if (failOnNoSchemas) {
552 throw new MojoExecutionException("", e);
553 }
554 } catch (Exception e) {
555 throw new MojoExecutionException(e.getMessage(), e);
556 }
557
558
559 return updateStaleFileTimestamp;
560 }
561
562
563
564
565
566
567
568
569
570 @Override
571 protected abstract List<URL> getSources();
572
573
574
575
576
577
578
579 protected abstract List<File> getSourceXJBs();
580
581
582
583
584
585
586 protected abstract void addGeneratedSourcesToProjectSourceRoot();
587
588
589
590
591
592 private String[] getXjcArguments(final String classPath, final String episodeFileNameOrNull)
593 throws MojoExecutionException, NoSchemasException {
594
595 final ArgumentBuilder builder = new ArgumentBuilder();
596
597
598 builder.withFlag(true, sourceType.getXjcArgument());
599 builder.withFlag(noPackageLevelAnnotations, "npa");
600 builder.withFlag(laxSchemaValidation, "nv");
601 builder.withFlag(verbose, "verbose");
602 builder.withFlag(quiet, "quiet");
603 builder.withFlag(enableIntrospection, "enableIntrospection");
604 builder.withFlag(extension, "extension");
605 builder.withFlag(readOnly, "readOnly");
606 builder.withFlag(noGeneratedHeaderComments, "no-header");
607 builder.withFlag(addGeneratedAnnotation, "mark-generated");
608
609
610
611 builder.withNamedArgument("httpproxy", getProxyString(settings.getActiveProxy()));
612 builder.withNamedArgument("encoding", getEncoding(true));
613 builder.withNamedArgument("p", packageName);
614 builder.withNamedArgument("target", target);
615 builder.withNamedArgument("d", getOutputDirectory().getAbsolutePath());
616 builder.withNamedArgument("classpath", classPath);
617
618
619 if (!extension) {
620
621 if (getLog().isInfoEnabled()) {
622 getLog().info("Adding 'extension' flag to XJC arguments, to generate an episode "
623 + "file named '" + (episodeFileName == null ? STANDARD_EPISODE_FILENAME : episodeFileName)
624 + "'. (XJCs 'episode' argument requires that the 'extension' argument is provided).");
625 }
626 }
627 builder.withFlag(true, "extension");
628
629 final File episodeFile = getEpisodeFile(episodeFileNameOrNull);
630 builder.withNamedArgument("episode", FileSystemUtilities.getCanonicalPath(episodeFile));
631
632 if (catalog != null) {
633 builder.withNamedArgument("catalog", FileSystemUtilities.getCanonicalPath(catalog));
634 }
635
636 if (arguments != null) {
637 builder.withPreCompiledArguments(arguments);
638 }
639
640 for (File current : getSourceXJBs()) {
641
642
643
644
645
646
647 builder.withPreCompiledArguments(Arrays.asList("-b", current.getAbsolutePath()));
648 }
649
650 final List<URL> sourceXSDs = getSources();
651 if (sourceXSDs.isEmpty()) {
652
653
654 getLog().warn("No XSD files found. Please check your plugin configuration.");
655 throw new NoSchemasException();
656
657 } else {
658
659 final List<String> unwrappedSourceXSDs = new ArrayList<String>();
660 for (URL current : sourceXSDs) {
661
662
663 if ("file".equalsIgnoreCase(current.getProtocol())) {
664 unwrappedSourceXSDs.add(FileSystemUtilities.relativize(
665 current.getPath(),
666 new File(System.getProperty("user.dir")),
667 true));
668 } else {
669 unwrappedSourceXSDs.add(current.toString());
670 }
671 }
672
673 builder.withPreCompiledArguments(unwrappedSourceXSDs);
674 }
675
676
677 return logAndReturnToolArguments(builder.build(), "XJC");
678 }
679
680 private String getProxyString(final Proxy activeProxy) {
681
682
683 if (activeProxy == null) {
684 return null;
685 }
686
687
688
689
690
691
692 final StringBuilder proxyBuilder = new StringBuilder();
693 if (activeProxy.getUsername() != null) {
694
695
696 proxyBuilder.append(activeProxy.getUsername());
697
698
699 if (activeProxy.getPassword() != null) {
700 proxyBuilder.append(":").append(activeProxy.getPassword());
701 }
702
703 proxyBuilder.append("@");
704 }
705
706
707 proxyBuilder.append(activeProxy.getHost()).append(":").append(activeProxy.getPort());
708
709
710 return proxyBuilder.toString();
711 }
712 }