1 package org.codehaus.mojo.jaxb2.javageneration;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
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 * <p>Abstract superclass for Mojos generating Java source or binaries from XML schema(s) by invoking the JAXB XJC
53 * binding compiler. Most of the Configuration options for the AbstractJavaGeneratorMojo are set or copied to the
54 * XJC directly; refer to their documentation in the <a href="https://jaxb.java.net/">JAXB Reference Implementation</a>
55 * site.</p>
56 *
57 * @author <a href="mailto:lj@jguru.se">Lennart Jörelid</a>
58 * @see <a href="https://jaxb.java.net/">The JAXB Reference Implementation</a>
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 * <p>Corresponding XJC parameter: {@code catalog}.</p>
68 * <p>Specify catalog files to resolve external entity references.
69 * Supports TR9401, XCatalog, and OASIS XML Catalog format.</p>
70 */
71 @Parameter
72 protected File catalog;
73
74 /**
75 * <strong>Deprecated - will be removed in a future release</strong>
76 * <p>From plugin version 2.4, this parameter will not be used.
77 * Instead, episode files are generated by default with all JAXB operations.</p>
78 * <p>Starting with plugin version 2.4, use the parameter {@link #episodeFileName} to provide a custom
79 * name of the generated episode File (or rely on the standard file name {@link #STANDARD_EPISODE_FILENAME}).</p>
80 *
81 * @since 2.0
82 * @deprecated
83 */
84 @Deprecated
85 @Parameter(defaultValue = "true")
86 protected boolean generateEpisode;
87
88 /**
89 * <p>Corresponding XJC parameter: {@code episode}.</p>
90 * <p>Generate an episode file with the supplied name from this XJC compilation, so that other schemas that rely
91 * on this schema can be compiled later and rely on classes that are generated from this compilation.
92 * The generated episode file is simply a JAXB customization file (but with vendor extensions), normally known
93 * as a <em>binding file</em> with the suffix <code>.xjb</code>.</p>
94 * <p>If the <code>episodeFileName</code> parameter is not given, the episode file name is synthesized on the form
95 * <code>"episode_" + executionID + ".xjb"</code> - typically something like <em>episode_my_xjc.xjb</em>, but
96 * it depends on the actual ID given in the execution element:</p>
97 * <pre>
98 * <code>
99 * <executions>
100 * <execution>
101 * <id>my_xjc</id>
102 * <goals>
103 * <goal>xjc</goal>
104 * </goals>
105 * </execution>
106 * </executions>
107 * </code>
108 * </pre>
109 *
110 * @see #STANDARD_EPISODE_FILENAME
111 * @since 2.4
112 */
113 @Parameter
114 protected String episodeFileName;
115
116 /**
117 * <p>Sets the HTTP/HTTPS proxy to be used by the XJC, on the format
118 * {@code [user[:password]@]proxyHost[:proxyPort]}.
119 * All information is retrieved from the active proxy within the standard maven settings file.</p>
120 */
121 @Parameter(defaultValue = "${settings}", readonly = true)
122 protected Settings settings;
123
124 /**
125 * <p>Defines the content type of sources for the XJC. To simplify usage of the JAXB2 maven plugin,
126 * all source files are assumed to have the same type of content.</p>
127 * <p>This parameter replaces the previous multiple-choice boolean configuration options for the
128 * jaxb2-maven-plugin (i.e. dtd, xmlschema, relaxng, relaxng-compact, wsdl), and
129 * corresponds to setting one of those flags as an XJC argument.</p>
130 *
131 * @since 2.0
132 */
133 @Parameter(defaultValue = "XmlSchema")
134 protected SourceContentType sourceType;
135
136 /**
137 * <p>Corresponding XJC parameter: {@code npa}.</p>
138 * <p>Suppress the generation of package level annotations into {@code package-info.java}.
139 * Using this switch causes the generated code to internalize those annotations into the other
140 * generated classes.</p>
141 *
142 * @since 2.0
143 */
144 @Parameter(defaultValue = "false")
145 protected boolean noPackageLevelAnnotations;
146
147 /**
148 * <p>Corresponding XJC parameter: {@code no-header}.</p>
149 * <p>Suppress the generation of a file header comment that includes some note and timestamp.
150 * Using this makes the generated code more diff-friendly.</p>
151 *
152 * @since 2.0
153 */
154 @Parameter(defaultValue = "false")
155 protected boolean noGeneratedHeaderComments;
156
157 /**
158 * <p>Corresponding XJC parameter: {@code mark-generated}.</p>
159 * <p>This feature causes all of the generated code to have {@code @Generated} annotation.</p>
160 *
161 * @since 2.0
162 */
163 @Parameter(defaultValue = "false")
164 protected boolean addGeneratedAnnotation;
165
166 /**
167 * <p>Corresponding XJC parameter: {@code nv}.</p>
168 * <p>By default, the XJC binding compiler performs strict validation of the source schema before processing it.
169 * Use this option to disable strict schema validation. This does not mean that the binding compiler will not
170 * perform any validation, it simply means that it will perform less-strict validation.</p>
171 *
172 * @since 2.0
173 */
174 @Parameter(defaultValue = "false")
175 protected boolean laxSchemaValidation;
176
177 /**
178 * <p>Corresponding XJC parameter: {@code quiet}.</p>
179 * <p>Suppress compiler output, such as progress information and warnings.</p>
180 */
181 @Parameter(defaultValue = "false")
182 protected boolean quiet;
183
184 /**
185 * <p>Corresponding XJC parameter: {@code verbose}.</p>
186 * <p>Tells XJC to be extra verbose, such as printing informational messages or displaying stack traces.</p>
187 */
188 @Parameter(property = "xjc.verbose", defaultValue = "false")
189 protected boolean verbose;
190
191 /**
192 * <p>Corresponding XJC parameter: {@code extension}.</p>
193 * <p>By default, the XJC binding compiler strictly enforces the rules outlined in the Compatibility chapter of
194 * the JAXB Specification. Appendix E.2 defines a set of W3C XML Schema features that are not completely
195 * supported by JAXB v1.0. In some cases, you may be allowed to use them in the "-extension" mode enabled by
196 * this switch. In the default (strict) mode, you are also limited to using only the binding customizations
197 * defined in the specification.</p>
198 */
199 @Parameter(defaultValue = "true")
200 protected boolean extension;
201
202 /**
203 * Fails the Mojo execution if no XSDs/schemas are found.
204 *
205 * @since 1.3
206 */
207 @Parameter(defaultValue = "true")
208 protected boolean failOnNoSchemas;
209
210 /**
211 * <p>Removes all files from the output directory before running XJC.</p>
212 */
213 @Parameter(defaultValue = "true")
214 protected boolean clearOutputDir;
215
216 /**
217 * <p>Corresponding XJC parameter: {@code readOnly}.</p>
218 * <p>By default, the XJC binding compiler does not write-protect the Java source files it generates.
219 * Use this option to force the XJC binding compiler to mark the generated Java sources read-only.</p>
220 *
221 * @since 2.0
222 */
223 @Parameter(defaultValue = "false")
224 protected boolean readOnly;
225
226 /**
227 * <p>List of ordered extra arguments to the XJC command. Each extra argument is interpreted as a word, intended
228 * to be copied verbatim to the XJC argument list with spaces in between:</p>
229 * <pre>
230 * <code>
231 * <configuration>
232 * ...
233 * <arguments>
234 * <argument>-Xfluent-api</argument>
235 * <argument>somefile</argument>
236 * </arguments>
237 * </configuration>
238 * </code>
239 * </pre>
240 * <p>The arguments configured above yields the following extra arguments to the XJC command:
241 * <code>-Xfluent-api -episode somefile</code></p>
242 *
243 * @since 2.0
244 * @deprecated This should be removed in the 2.0+ release, as all arguments should be handled by other parameters.
245 */
246 @Parameter(property = "xjc.arguments")
247 protected List<String> arguments;
248
249 /**
250 * <p>Corresponding XJC parameter: {@code enableIntrospection}.</p>
251 * <p>Enable correct generation of Boolean getters/setters to enable Bean Introspection APIs.</p>
252 *
253 * @since 1.4
254 */
255 @Parameter(defaultValue = "false")
256 private boolean enableIntrospection;
257
258 /**
259 * <p>Corresponding XJC parameter: {@code p}.</p>
260 * <p>The package under which the source files will be generated. Quoting the XJC documentation:
261 * "Specifying a target package via this command-line option overrides any binding customization for package
262 * name and the default package name algorithm defined in the specification".</p>
263 */
264 @Parameter
265 protected String packageName;
266
267 /**
268 * <p>Corresponding XJC parameter: {@code target}.</p>
269 * <p>Permitted values: {@code "2.0"} and {@code "2.1"}. Avoid generating code that relies on JAXB newer than the
270 * version given. This will allow the generated code to run with JAXB 2.0 runtime (such as JavaSE 6.)</p>
271 *
272 * @since 1.3
273 */
274 @Parameter
275 protected String target;
276
277 /**
278 * <p>If provided, this parameter indicates that the XSDs used by XJC to generate Java code should be
279 * copied into the resulting artifact of this project (the JAR, WAR or whichever artifact type is generated).
280 * The value of the {@code xsdPathWithinArtifact} parameter is the relative path within the artifact where
281 * all source XSDs are copied to (hence the name "XSD Path Within Artifact").</p>
282 * <p>The target directory is created within the artifact if it does not already exist.
283 * If the {@code xsdPathWithinArtifact} parameter is not given, the XSDs used to generate Java code are
284 * <em>not</em> included within the project's artifact.</p>
285 * <p><em>Example:</em>Adding the sample configuration below would copy all source XSDs to the given directory
286 * within the resulting JAR (and/or test-JAR). If the directory {@code META-INF/jaxb/xsd} does not exist, it
287 * will be created.</p>
288 * <pre>
289 * <code>
290 * <configuration>
291 * ...
292 * <xsdPathWithinArtifact>META-INF/jaxb/xsd</xsdPathWithinArtifact>
293 * </configuration>
294 * </code>
295 * </pre>
296 * <p><strong>Note</strong>: This parameter was previously called {@code includeSchemasOutputPath}
297 * in the 1.x versions of this plugin, but was renamed and re-documented for improved usability and clarity.</p>
298 *
299 * @since 2.0
300 */
301 @Parameter
302 protected String xsdPathWithinArtifact;
303
304 /**
305 * <p>If set to <code>true</code>, the system property <code>enableExternalEntityProcessing</code> is set for the
306 * duration of the Java generation by this plugin, permitting DTD sources to use external entity URIs such as
307 * <code>file://</code>. Typically, this is used in conjunction with the <code>sourceType</code> similar to the
308 * configuration snippet below where DTDs are used as sourceType and read from the <code>src/main/dtd</code>
309 * directory. This implies a <code>file://</code> URI.</p>
310 * <pre>
311 * <code>
312 * <configuration>
313 * ...
314 * <sourceType>dtd</sourceType>
315 * <sources>
316 * <source>src/main/dtd</source>
317 * </sources>
318 * <externalEntityProcessing>true</externalEntityProcessing>
319 * </configuration>
320 * </code>
321 * </pre>
322 *
323 * @since 2.4
324 */
325 @Parameter(defaultValue = "false")
326 protected boolean externalEntityProcessing;
327
328 /**
329 * <p>Java generation is required if any of the file products is outdated/stale.</p>
330 * {@inheritDoc}
331 */
332 @Override
333 protected boolean isReGenerationRequired() {
334
335 //
336 // Use the stale flag method to identify if we should re-generate the java source code from the supplied
337 // Xml Schema. Basically, we should regenerate the JAXB code if:
338 //
339 // a) The staleFile does not exist
340 // b) The staleFile exists and is older than one of the sources (XSD or XJB files).
341 // "Older" is determined by comparing the modification timestamp of the staleFile and the source files.
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 // Can't determine if the staleFile is younger than this sourceXSD.
369 // Re-generate to be on the safe side.
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 // All done.
404 return stale;
405 }
406
407 /**
408 * {@inheritDoc}
409 */
410 @Override
411 protected boolean performExecution() throws MojoExecutionException, MojoFailureException {
412
413 boolean updateStaleFileTimestamp = false;
414
415 try {
416
417 // Setup the Tool's execution environment
418 ToolExecutionEnvironment environment = null;
419 try {
420
421 // Create a LocaleFacet if the user has configured an explicit Locale for the tool.
422 final LocaleFacet localeFacet = locale == null ? null : LocaleFacet.createFor(locale, getLog());
423
424 // Create the ToolExecutionEnvironment
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 // Add any extra configured EnvironmentFacets, as configured in the POM.
432 if (extraFacets != null) {
433 for (EnvironmentFacet current : extraFacets) {
434 environment.add(current);
435 }
436 }
437
438 // Handle extended properties?
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 // XJC overwrites proxy properties if so inclined, so we use this facet to save them
452 for (String key : PROXY_PROPERTY_KEYS) {
453 environment.add(new SystemPropertySaveEnvironmentFacet(key, getLog()));
454 }
455
456 // Setup the environment.
457 environment.setup();
458
459 // Compile the XJC arguments
460 final String[] xjcArguments = getXjcArguments(environment.getClassPathAsArgument(), episodeFileName);
461
462 // Ensure that the outputDirectory exists, but only clear it if does not already
463 FileSystemUtilities.createDirectory(getOutputDirectory(), clearOutputDir);
464
465 // Do we need to re-create the episode file's parent directory.
466 final boolean reCreateEpisodeFileParentDirectory = generateEpisode && clearOutputDir;
467 if (reCreateEpisodeFileParentDirectory) {
468 getEpisodeFile(episodeFileName);
469 }
470
471 // Check the system properties.
472 logSystemPropertiesAndBasedir();
473
474 // Fire XJC
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 // Indicate that the output directory was updated.
492 getBuildContext().refresh(getOutputDirectory());
493
494 // Update the modification timestamp of the staleFile.
495 updateStaleFileTimestamp = true;
496
497 } finally {
498
499 if (environment != null) {
500 environment.restore();
501 }
502 }
503
504 // Add the generated source root to the project, enabling tooling and other plugins to see them.
505 addGeneratedSourcesToProjectSourceRoot();
506
507 // Copy all source XSDs to the resulting artifact?
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 // Typical JAR path
522 // jar:file:/path/to/aJar.jar!/some/path/xsd/aResource.xsd
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 // TODO: Should we throw an exception here instead?
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 // Refresh the BuildContext
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 // All done.
559 return updateStaleFileTimestamp;
560 }
561
562 /**
563 * Override this method to acquire a List holding all URLs to the JAXB sources for which this
564 * AbstractJavaGeneratorMojo should generate Java files. Sources are assumed to be in the form given by
565 * the {@code sourceType} value.
566 *
567 * @return A non-null List holding URLs to sources for the XJC generation.
568 * @see #sourceType
569 */
570 @Override
571 protected abstract List<URL> getSources();
572
573 /**
574 * Override this method to retrieve a list of Files to all XJB files for which this
575 * AbstractJavaGeneratorMojo should generate Java files.
576 *
577 * @return A non-null List holding binding files.
578 */
579 protected abstract List<File> getSourceXJBs();
580
581 /**
582 * Adds any directories containing the generated XJC classes to the appropriate Project compilation sources;
583 * either {@code TestCompileSourceRoot} or {@code CompileSourceRoot} depending on the exact Mojo implementation
584 * of this AbstractJavaGeneratorMojo.
585 */
586 protected abstract void addGeneratedSourcesToProjectSourceRoot();
587
588 //
589 // Private helpers
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 // Add all flags on the form '-flagName'
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 // Add all arguments on the form '-argumentName argumentValue'
610 // (i.e. in 2 separate elements of the returned String[])
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 // We must add the -extension flag in order to generate the episode file.
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 // Shorten the argument?
643 // final String strippedXjbPath = FileSystemUtilities.relativize(
644 // current.getAbsolutePath(), getProject().getBasedir());
645
646 // Each XJB must be given as a separate argument.
647 builder.withPreCompiledArguments(Arrays.asList("-b", current.getAbsolutePath()));
648 }
649
650 final List<URL> sourceXSDs = getSources();
651 if (sourceXSDs.isEmpty()) {
652
653 // If we have no XSDs, we are not going to be able to run XJC.
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 // Shorten the argument if possible.
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 // All done.
677 return logAndReturnToolArguments(builder.build(), "XJC");
678 }
679
680 private String getProxyString(final Proxy activeProxy) {
681
682 // Check sanity
683 if (activeProxy == null) {
684 return null;
685 }
686
687 // The XJC proxy argument should be on the form
688 // [user[:password]@]proxyHost[:proxyPort]
689 //
690 // builder.withNamedArgument("httpproxy", httpproxy);
691 //
692 final StringBuilder proxyBuilder = new StringBuilder();
693 if (activeProxy.getUsername() != null) {
694
695 // Start with the username.
696 proxyBuilder.append(activeProxy.getUsername());
697
698 // Append the password if provided.
699 if (activeProxy.getPassword() != null) {
700 proxyBuilder.append(":").append(activeProxy.getPassword());
701 }
702
703 proxyBuilder.append("@");
704 }
705
706 // Append hostname and port.
707 proxyBuilder.append(activeProxy.getHost()).append(":").append(activeProxy.getPort());
708
709 // All done.
710 return proxyBuilder.toString();
711 }
712 }