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