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.model.Resource;
24 import org.apache.maven.plugin.MojoExecutionException;
25 import org.apache.maven.plugin.MojoFailureException;
26 import org.apache.maven.plugins.annotations.Parameter;
27 import org.apache.maven.settings.Proxy;
28 import org.apache.maven.settings.Settings;
29 import org.codehaus.mojo.jaxb2.AbstractJaxbMojo;
30 import org.codehaus.mojo.jaxb2.NoSchemasException;
31 import org.codehaus.mojo.jaxb2.shared.FileSystemUtilities;
32 import org.codehaus.mojo.jaxb2.shared.arguments.ArgumentBuilder;
33 import org.codehaus.mojo.jaxb2.shared.environment.EnvironmentFacet;
34 import org.codehaus.mojo.jaxb2.shared.environment.ToolExecutionEnvironment;
35 import org.codehaus.mojo.jaxb2.shared.environment.classloading.ThreadContextClassLoaderBuilder;
36 import org.codehaus.mojo.jaxb2.shared.environment.locale.LocaleFacet;
37 import org.codehaus.mojo.jaxb2.shared.environment.logging.LoggingHandlerEnvironmentFacet;
38 import org.codehaus.mojo.jaxb2.shared.environment.sysprops.SystemPropertyChangeEnvironmentFacet;
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 int XJC_COMPLETED_OK = 0;
63
64 /**
65 * <p>Corresponding XJC parameter: {@code catalog}.</p>
66 * <p>Specify catalog files to resolve external entity references.
67 * Supports TR9401, XCatalog, and OASIS XML Catalog format.</p>
68 */
69 @Parameter
70 protected File catalog;
71
72 /**
73 * <strong>Deprecated - will be removed in a future release</strong>
74 * <p>From plugin version 2.4, this parameter will not be used.
75 * Instead, episode files are generated by default with all JAXB operations.</p>
76 * <p>Starting with plugin version 2.4, use the parameter {@link #episodeFileName} to provide a custom
77 * name of the generated episode File (or rely on the standard file name {@link #STANDARD_EPISODE_FILENAME}).</p>
78 *
79 * @since 2.0
80 * @deprecated
81 */
82 @Deprecated
83 @Parameter(defaultValue = "true")
84 protected boolean generateEpisode;
85
86 /**
87 * <p>Corresponding XJC parameter: {@code episode}.</p>
88 * <p>Generate an episode file with the supplied name from this XJC compilation, so that other schemas that rely
89 * on this schema can be compiled later and rely on classes that are generated from this compilation.
90 * The generated episode file is simply a JAXB customization file (but with vendor extensions), normally known
91 * as a <em>binding file</em> with the suffix <code>.xjb</code>.</p>
92 * <p>If the <code>episodeFileName</code> parameter is not given, the episode file name is synthesized on the form
93 * <code>"episode_" + executionID + ".xjb"</code> - typically something like <em>episode_my_xjc.xjb</em>, but
94 * it depends on the actual ID given in the execution element:</p>
95 * <pre>
96 * <code>
97 * <executions>
98 * <execution>
99 * <id>my_xjc</id>
100 * <goals>
101 * <goal>xjc</goal>
102 * </goals>
103 * </execution>
104 * </executions>
105 * </code>
106 * </pre>
107 *
108 * @see #STANDARD_EPISODE_FILENAME
109 * @since 2.4
110 */
111 @Parameter
112 protected String episodeFileName;
113
114 /**
115 * <p>Sets the HTTP/HTTPS proxy to be used by the XJC, on the format
116 * {@code [user[:password]@]proxyHost[:proxyPort]}.
117 * All information is retrieved from the active proxy within the standard maven settings file.</p>
118 */
119 @Parameter(defaultValue = "${settings}", readonly = true)
120 protected Settings settings;
121
122 /**
123 * <p>Defines the content type of sources for the XJC. To simplify usage of the JAXB2 maven plugin,
124 * all source files are assumed to have the same type of content.</p>
125 * <p>This parameter replaces the previous multiple-choice boolean configuration options for the
126 * jaxb2-maven-plugin (i.e. dtd, xmlschema, relaxng, relaxng-compact, wsdl), and
127 * corresponds to setting one of those flags as an XJC argument.</p>
128 *
129 * @since 2.0
130 */
131 @Parameter(defaultValue = "XmlSchema")
132 protected SourceContentType sourceType;
133
134 /**
135 * <p>Corresponding XJC parameter: {@code npa}.</p>
136 * <p>Suppress the generation of package level annotations into {@code package-info.java}.
137 * Using this switch causes the generated code to internalize those annotations into the other
138 * generated classes.</p>
139 *
140 * @since 2.0
141 */
142 @Parameter(defaultValue = "false")
143 protected boolean noPackageLevelAnnotations;
144
145 /**
146 * <p>Corresponding XJC parameter: {@code no-header}.</p>
147 * <p>Suppress the generation of a file header comment that includes some note and timestamp.
148 * Using this makes the generated code more diff-friendly.</p>
149 *
150 * @since 2.0
151 */
152 @Parameter(defaultValue = "false")
153 protected boolean noGeneratedHeaderComments;
154
155 /**
156 * <p>Corresponding XJC parameter: {@code mark-generated}.</p>
157 * <p>This feature causes all of the generated code to have {@code @Generated} annotation.</p>
158 *
159 * @since 2.0
160 */
161 @Parameter(defaultValue = "false")
162 protected boolean addGeneratedAnnotation;
163
164 /**
165 * <p>Corresponding XJC parameter: {@code nv}.</p>
166 * <p>By default, the XJC binding compiler performs strict validation of the source schema before processing it.
167 * Use this option to disable strict schema validation. This does not mean that the binding compiler will not
168 * perform any validation, it simply means that it will perform less-strict validation.</p>
169 *
170 * @since 2.0
171 */
172 @Parameter(defaultValue = "false")
173 protected boolean laxSchemaValidation;
174
175 /**
176 * <p>Corresponding XJC parameter: {@code quiet}.</p>
177 * <p>Suppress compiler output, such as progress information and warnings.</p>
178 */
179 @Parameter(defaultValue = "false")
180 protected boolean quiet;
181
182 /**
183 * <p>Corresponding XJC parameter: {@code verbose}.</p>
184 * <p>Tells XJC to be extra verbose, such as printing informational messages or displaying stack traces.</p>
185 */
186 @Parameter(property = "xjc.verbose", defaultValue = "false")
187 protected boolean verbose;
188
189 /**
190 * <p>Corresponding XJC parameter: {@code extension}.</p>
191 * <p>By default, the XJC binding compiler strictly enforces the rules outlined in the Compatibility chapter of
192 * the JAXB Specification. Appendix E.2 defines a set of W3C XML Schema features that are not completely
193 * supported by JAXB v1.0. In some cases, you may be allowed to use them in the "-extension" mode enabled by
194 * this switch. In the default (strict) mode, you are also limited to using only the binding customizations
195 * defined in the specification.</p>
196 */
197 @Parameter(defaultValue = "true")
198 protected boolean extension;
199
200 /**
201 * Fails the Mojo execution if no XSDs/schemas are found.
202 *
203 * @since 1.3
204 */
205 @Parameter(defaultValue = "true")
206 protected boolean failOnNoSchemas;
207
208 /**
209 * <p>Removes all files from the output directory before running XJC.</p>
210 */
211 @Parameter(defaultValue = "true")
212 protected boolean clearOutputDir;
213
214 /**
215 * <p>Corresponding XJC parameter: {@code readOnly}.</p>
216 * <p>By default, the XJC binding compiler does not write-protect the Java source files it generates.
217 * Use this option to force the XJC binding compiler to mark the generated Java sources read-only.</p>
218 *
219 * @since 2.0
220 */
221 @Parameter(defaultValue = "false")
222 protected boolean readOnly;
223
224 /**
225 * <p>List of ordered extra arguments to the XJC command. Each extra argument is interpreted as a word, intended
226 * to be copied verbatim to the XJC argument list with spaces in between:</p>
227 * <pre>
228 * <code>
229 * <configuration>
230 * ...
231 * <arguments>
232 * <argument>-Xfluent-api</argument>
233 * <argument>somefile</argument>
234 * </arguments>
235 * </configuration>
236 * </code>
237 * </pre>
238 * <p>The arguments configured above yields the following extra arguments to the XJC command:
239 * <code>-Xfluent-api -episode somefile</code></p>
240 *
241 * @since 2.0
242 * @deprecated This should be removed in the 2.0+ release, as all arguments should be handled by other parameters.
243 */
244 @Parameter(property = "xjc.arguments")
245 protected List<String> arguments;
246
247 /**
248 * <p>Corresponding XJC parameter: {@code enableIntrospection}.</p>
249 * <p>Enable correct generation of Boolean getters/setters to enable Bean Introspection APIs.</p>
250 *
251 * @since 1.4
252 */
253 @Parameter(defaultValue = "false")
254 private boolean enableIntrospection;
255
256 /**
257 * <p>Corresponding XJC parameter: {@code p}.</p>
258 * <p>The package under which the source files will be generated. Quoting the XJC documentation:
259 * "Specifying a target package via this command-line option overrides any binding customization for package
260 * name and the default package name algorithm defined in the specification".</p>
261 */
262 @Parameter
263 protected String packageName;
264
265 /**
266 * <p>Corresponding XJC parameter: {@code target}.</p>
267 * <p>Permitted values: {@code "2.0"} and {@code "2.1"}. Avoid generating code that relies on JAXB newer than the
268 * version given. This will allow the generated code to run with JAXB 2.0 runtime (such as JavaSE 6.)</p>
269 *
270 * @since 1.3
271 */
272 @Parameter
273 protected String target;
274
275 /**
276 * <p>If provided, this parameter indicates that the XSDs used by XJC to generate Java code should be
277 * copied into the resulting artifact of this project (the JAR, WAR or whichever artifact type is generated).
278 * The value of the {@code xsdPathWithinArtifact} parameter is the relative path within the artifact where
279 * all source XSDs are copied to (hence the name "XSD Path Within Artifact").</p>
280 * <p>The target directory is created within the artifact if it does not already exist.
281 * If the {@code xsdPathWithinArtifact} parameter is not given, the XSDs used to generate Java code are
282 * <em>not</em> included within the project's artifact.</p>
283 * <p><em>Example:</em>Adding the sample configuration below would copy all source XSDs to the given directory
284 * within the resulting JAR (and/or test-JAR). If the directory {@code META-INF/jaxb/xsd} does not exist, it
285 * will be created.</p>
286 * <pre>
287 * <code>
288 * <configuration>
289 * ...
290 * <xsdPathWithinArtifact>META-INF/jaxb/xsd</xsdPathWithinArtifact>
291 * </configuration>
292 * </code>
293 * </pre>
294 * <p><strong>Note</strong>: This parameter was previously called {@code includeSchemasOutputPath}
295 * in the 1.x versions of this plugin, but was renamed and re-documented for improved usability and clarity.</p>
296 *
297 * @since 2.0
298 */
299 @Parameter
300 protected String xsdPathWithinArtifact;
301
302 /**
303 * <p>If set to <code>true</code>, the system property <code>enableExternalEntityProcessing</code> is set for the
304 * duration of the Java generation by this plugin, permitting DTD sources to use external entity URIs such as
305 * <code>file://</code>. Typically, this is used in conjunction with the <code>sourceType</code> similar to the
306 * configuration snippet below where DTDs are used as sourceType and read from the <code>src/main/dtd</code>
307 * directory. This implies a <code>file://</code> URI.</p>
308 * <pre>
309 * <code>
310 * <configuration>
311 * ...
312 * <sourceType>dtd</sourceType>
313 * <sources>
314 * <source>src/main/dtd</source>
315 * </sources>
316 * <externalEntityProcessing>true</externalEntityProcessing>
317 * </configuration>
318 * </code>
319 * </pre>
320 *
321 * @since 2.4
322 */
323 @Parameter(defaultValue = "false")
324 protected boolean externalEntityProcessing;
325
326 /**
327 * <p>Java generation is required if any of the file products is outdated/stale.</p>
328 * {@inheritDoc}
329 */
330 @Override
331 protected boolean isReGenerationRequired() {
332
333 //
334 // Use the stale flag method to identify if we should re-generate the java source code from the supplied
335 // Xml Schema. Basically, we should regenerate the JAXB code if:
336 //
337 // a) The staleFile does not exist
338 // b) The staleFile exists and is older than one of the sources (XSD or XJB files).
339 // "Older" is determined by comparing the modification timestamp of the staleFile and the source files.
340 //
341 final File staleFile = getStaleFile();
342 final String debugPrefix = "StaleFile [" + FileSystemUtilities.getCanonicalPath(staleFile) + "]";
343
344 boolean stale = !staleFile.exists();
345 if (stale) {
346 getLog().debug(debugPrefix + " not found. JAXB (re-)generation required.");
347 } else {
348
349 final List<URL> sourceXSDs = getSources();
350 final List<File> sourceXJBs = getSourceXJBs();
351
352 if (getLog().isDebugEnabled()) {
353 getLog().debug(debugPrefix + " found. Checking timestamps on source XSD and XJB "
354 + "files to determine if JAXB (re-)generation is required.");
355 }
356
357 final long staleFileLastModified = staleFile.lastModified();
358 for (URL current : sourceXSDs) {
359
360 final URLConnection sourceXsdConnection;
361 try {
362 sourceXsdConnection = current.openConnection();
363 sourceXsdConnection.connect();
364 } catch (Exception e) {
365
366 // Can't determine if the staleFile is younger than this sourceXSD.
367 // Re-generate to be on the safe side.
368 stale = true;
369 break;
370 }
371
372 try {
373 if (sourceXsdConnection.getLastModified() > staleFileLastModified) {
374
375 if (getLog().isDebugEnabled()) {
376 getLog().debug(current.toString() + " is newer than the stale flag file.");
377 }
378 stale = true;
379 }
380 } finally {
381 if (sourceXsdConnection instanceof HttpURLConnection) {
382 ((HttpURLConnection) sourceXsdConnection).disconnect();
383 }
384 }
385 }
386
387 for (File current : sourceXJBs) {
388 if (current.lastModified() > staleFileLastModified) {
389
390 if (getLog().isDebugEnabled()) {
391 getLog().debug(FileSystemUtilities.getCanonicalPath(current)
392 + " is newer than the stale flag file.");
393 }
394
395 stale = true;
396 break;
397 }
398 }
399 }
400
401 // All done.
402 return stale;
403 }
404
405 /**
406 * {@inheritDoc}
407 */
408 @Override
409 protected boolean performExecution() throws MojoExecutionException, MojoFailureException {
410
411 boolean updateStaleFileTimestamp = false;
412
413 try {
414
415 // Setup the Tool's execution environment
416 ToolExecutionEnvironment environment = null;
417 try {
418
419 // Create a LocaleFacet if the user has configured an explicit Locale for the tool.
420 final LocaleFacet localeFacet = locale == null ? null : LocaleFacet.createFor(locale, getLog());
421
422 // Create the ToolExecutionEnvironment
423 environment = new ToolExecutionEnvironment(getLog(),
424 ThreadContextClassLoaderBuilder.createFor(this.getClass(), getLog(), getEncoding(false))
425 .addPaths(getClasspath()),
426 LoggingHandlerEnvironmentFacet.create(getLog(), getClass(), getEncoding(false)),
427 localeFacet);
428
429 // Add any extra configured EnvironmentFacets, as configured in the POM.
430 if (extraFacets != null) {
431 for (EnvironmentFacet current : extraFacets) {
432 environment.add(current);
433 }
434 }
435
436 // Handle extended properties?
437 if (externalEntityProcessing) {
438
439 final List<SystemPropertyChangeEnvironmentFacet> sysPropChanges =
440 SystemPropertyChangeEnvironmentFacet.getBuilder(getLog())
441 .addOrChange("enableExternalEntityProcessing", "" + externalEntityProcessing)
442 .build();
443
444 for (SystemPropertyChangeEnvironmentFacet current : sysPropChanges) {
445 environment.add(current);
446 }
447 }
448
449 // Setup the environment.
450 environment.setup();
451
452 // Compile the XJC arguments
453 final String[] xjcArguments = getXjcArguments(environment.getClassPathAsArgument(), episodeFileName);
454
455 // Ensure that the outputDirectory exists, but only clear it if does not already
456 FileSystemUtilities.createDirectory(getOutputDirectory(), clearOutputDir);
457
458 // Do we need to re-create the episode file's parent directory.
459 final boolean reCreateEpisodeFileParentDirectory = generateEpisode && clearOutputDir;
460 if (reCreateEpisodeFileParentDirectory) {
461 getEpisodeFile(episodeFileName);
462 }
463
464 // Check the system properties.
465 logSystemPropertiesAndBasedir();
466
467 // Fire XJC
468 if (XJC_COMPLETED_OK != Driver.run(xjcArguments, new XjcLogAdapter(getLog()))) {
469
470 final StringBuilder errorMsgBuilder = new StringBuilder();
471 errorMsgBuilder.append("\n+=================== [XJC Error]\n");
472 errorMsgBuilder.append("|\n");
473
474 final List<URL> sourceXSDs = getSources();
475 for (int i = 0; i < sourceXSDs.size(); i++) {
476 errorMsgBuilder.append("| " + i + ": ").append(sourceXSDs.get(i).toString()).append("\n");
477 }
478
479 errorMsgBuilder.append("|\n");
480 errorMsgBuilder.append("+=================== [End XJC Error]\n");
481 throw new MojoExecutionException(errorMsgBuilder.toString());
482 }
483
484 // Indicate that the output directory was updated.
485 getBuildContext().refresh(getOutputDirectory());
486
487 // Update the modification timestamp of the staleFile.
488 updateStaleFileTimestamp = true;
489
490 } finally {
491
492 if (environment != null) {
493 environment.restore();
494 }
495 }
496
497 // Add the generated source root to the project, enabling tooling and other plugins to see them.
498 addGeneratedSourcesToProjectSourceRoot();
499
500 // Copy all source XSDs to the resulting artifact?
501 if (xsdPathWithinArtifact != null) {
502
503 final String buildOutputDirectory = getProject().getBuild().getOutputDirectory();
504 final File targetXsdDirectory = new File(buildOutputDirectory, xsdPathWithinArtifact);
505 FileUtils.forceMkdir(targetXsdDirectory);
506
507 for (URL current : getSources()) {
508
509 String fileName = null;
510 if ("file".equalsIgnoreCase(current.getProtocol())) {
511 fileName = new File(current.getPath()).getName();
512 } else if ("jar".equalsIgnoreCase(current.getProtocol())) {
513
514 // Typical JAR path
515 // jar:file:/path/to/aJar.jar!/some/path/xsd/aResource.xsd
516 final int bangIndex = current.toString().indexOf("!");
517 if (bangIndex == -1) {
518 throw new MojoExecutionException("Illegal JAR URL [" + current.toString()
519 + "]: lacks a '!'");
520 }
521
522 final String internalPath = current.toString().substring(bangIndex + 1);
523 fileName = new File(internalPath).getName();
524 } else {
525 throw new MojoExecutionException("Could not extract FileName from URL [" + current + "]");
526 }
527
528 final File targetFile = new File(targetXsdDirectory, fileName);
529 if (targetFile.exists()) {
530
531 // TODO: Should we throw an exception here instead?
532 getLog().warn("File [" + FileSystemUtilities.getCanonicalPath(targetFile)
533 + "] already exists. Not copying XSD file [" + current.getPath() + "] to it.");
534 }
535 IOUtil.copy(current.openStream(), new FileWriter(targetFile));
536 }
537
538 // Refresh the BuildContext
539 getBuildContext().refresh(targetXsdDirectory);
540 }
541 } catch (MojoExecutionException e) {
542 throw e;
543 } catch (NoSchemasException e) {
544 if (failOnNoSchemas) {
545 throw new MojoExecutionException("", e);
546 }
547 } catch (Exception e) {
548 throw new MojoExecutionException(e.getMessage(), e);
549 }
550
551 // All done.
552 return updateStaleFileTimestamp;
553 }
554
555 /**
556 * Override this method to acquire a List holding all URLs to the JAXB sources for which this
557 * AbstractJavaGeneratorMojo should generate Java files. Sources are assumed to be in the form given by
558 * the {@code sourceType} value.
559 *
560 * @return A non-null List holding URLs to sources for the XJC generation.
561 * @see #sourceType
562 */
563 @Override
564 protected abstract List<URL> getSources();
565
566 /**
567 * Override this method to retrieve a list of Files to all XJB files for which this
568 * AbstractJavaGeneratorMojo should generate Java files.
569 *
570 * @return A non-null List holding binding files.
571 */
572 protected abstract List<File> getSourceXJBs();
573
574 /**
575 * Adds any directories containing the generated XJC classes to the appropriate Project compilation sources;
576 * either {@code TestCompileSourceRoot} or {@code CompileSourceRoot} depending on the exact Mojo implementation
577 * of this AbstractJavaGeneratorMojo.
578 */
579 protected abstract void addGeneratedSourcesToProjectSourceRoot();
580
581 //
582 // Private helpers
583 //
584
585 private String[] getXjcArguments(final String classPath, final String episodeFileNameOrNull)
586 throws MojoExecutionException, NoSchemasException {
587
588 final ArgumentBuilder builder = new ArgumentBuilder();
589
590 // Add all flags on the form '-flagName'
591 builder.withFlag(true, sourceType.getXjcArgument());
592 builder.withFlag(noPackageLevelAnnotations, "npa");
593 builder.withFlag(laxSchemaValidation, "nv");
594 builder.withFlag(verbose, "verbose");
595 builder.withFlag(quiet, "quiet");
596 builder.withFlag(enableIntrospection, "enableIntrospection");
597 builder.withFlag(extension, "extension");
598 builder.withFlag(readOnly, "readOnly");
599 builder.withFlag(noGeneratedHeaderComments, "no-header");
600 builder.withFlag(addGeneratedAnnotation, "mark-generated");
601
602 // Add all arguments on the form '-argumentName argumentValue'
603 // (i.e. in 2 separate elements of the returned String[])
604 builder.withNamedArgument("httpproxy", getProxyString(settings.getActiveProxy()));
605 builder.withNamedArgument("encoding", getEncoding(true));
606 builder.withNamedArgument("p", packageName);
607 builder.withNamedArgument("target", target);
608 builder.withNamedArgument("d", getOutputDirectory().getAbsolutePath());
609 builder.withNamedArgument("classpath", classPath);
610
611 // We must add the -extension flag in order to generate the episode file.
612 if (!extension) {
613
614 if (getLog().isInfoEnabled()) {
615 getLog().info("Adding 'extension' flag to XJC arguments, to generate an episode "
616 + "file named '" + (episodeFileName == null ? STANDARD_EPISODE_FILENAME : episodeFileName)
617 + "'. (XJCs 'episode' argument requires that the 'extension' argument is provided).");
618 }
619 }
620 builder.withFlag(true, "extension");
621
622 final File episodeFile = getEpisodeFile(episodeFileNameOrNull);
623 builder.withNamedArgument("episode", FileSystemUtilities.getCanonicalPath(episodeFile));
624
625 if (catalog != null) {
626 builder.withNamedArgument("catalog", FileSystemUtilities.getCanonicalPath(catalog));
627 }
628
629 if (arguments != null) {
630 builder.withPreCompiledArguments(arguments);
631 }
632
633 for (File current : getSourceXJBs()) {
634
635 // Shorten the argument?
636 // final String strippedXjbPath = FileSystemUtilities.relativize(
637 // current.getAbsolutePath(), getProject().getBasedir());
638
639 // Each XJB must be given as a separate argument.
640 builder.withPreCompiledArguments(Arrays.asList("-b", current.getAbsolutePath()));
641 }
642
643 final List<URL> sourceXSDs = getSources();
644 if (sourceXSDs.isEmpty()) {
645
646 // If we have no XSDs, we are not going to be able to run XJC.
647 getLog().warn("No XSD files found. Please check your plugin configuration.");
648 throw new NoSchemasException();
649
650 } else {
651
652 final List<String> unwrappedSourceXSDs = new ArrayList<String>();
653 for (URL current : sourceXSDs) {
654
655 // Shorten the argument if possible.
656 if ("file".equalsIgnoreCase(current.getProtocol())) {
657 unwrappedSourceXSDs.add(FileSystemUtilities.relativize(
658 current.getPath(),
659 new File(System.getProperty("user.dir")),
660 true));
661 } else {
662 unwrappedSourceXSDs.add(current.toString());
663 }
664 }
665
666 builder.withPreCompiledArguments(unwrappedSourceXSDs);
667 }
668
669 // All done.
670 return logAndReturnToolArguments(builder.build(), "XJC");
671 }
672
673 private String getProxyString(final Proxy activeProxy) {
674
675 // Check sanity
676 if (activeProxy == null) {
677 return null;
678 }
679
680 // The XJC proxy argument should be on the form
681 // [user[:password]@]proxyHost[:proxyPort]
682 //
683 // builder.withNamedArgument("httpproxy", httpproxy);
684 //
685 final StringBuilder proxyBuilder = new StringBuilder();
686 if (activeProxy.getUsername() != null) {
687
688 // Start with the username.
689 proxyBuilder.append(activeProxy.getUsername());
690
691 // Append the password if provided.
692 if (activeProxy.getPassword() != null) {
693 proxyBuilder.append(":").append(activeProxy.getPassword());
694 }
695
696 proxyBuilder.append("@");
697 }
698
699 // Append hostname and port.
700 proxyBuilder.append(activeProxy.getHost()).append(":").append(activeProxy.getPort());
701
702 // All done.
703 return proxyBuilder.toString();
704 }
705 }