All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codehaus.mojo.jaxb2.javageneration.AbstractJavaGeneratorMojo Maven / Gradle / Ivy

Go to download

Mojo's JAXB-2 Maven plugin is used to create an object graph from XSDs based on the JAXB 2.x implementation and to generate XSDs from JAXB annotated Java classes.

There is a newer version: 3.2.0
Show newest version
package org.codehaus.mojo.jaxb2.javageneration;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import com.sun.tools.xjc.Driver;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.settings.Proxy;
import org.apache.maven.settings.Settings;
import org.codehaus.mojo.jaxb2.AbstractJaxbMojo;
import org.codehaus.mojo.jaxb2.NoSchemasException;
import org.codehaus.mojo.jaxb2.shared.FileSystemUtilities;
import org.codehaus.mojo.jaxb2.shared.arguments.ArgumentBuilder;
import org.codehaus.mojo.jaxb2.shared.environment.EnvironmentFacet;
import org.codehaus.mojo.jaxb2.shared.environment.ToolExecutionEnvironment;
import org.codehaus.mojo.jaxb2.shared.environment.classloading.ThreadContextClassLoaderBuilder;
import org.codehaus.mojo.jaxb2.shared.environment.locale.LocaleFacet;
import org.codehaus.mojo.jaxb2.shared.environment.logging.LoggingHandlerEnvironmentFacet;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.IOUtil;

import java.io.File;
import java.io.FileWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 

Abstract superclass for Mojos generating Java source or binaries from XML schema(s) by invoking the JAXB XJC * binding compiler. Most of the Configuration options for the AbstractJavaGeneratorMojo are set or copied to the * XJC directly; refer to their documentation in the JAXB Reference Implementation * site.

* * @author Lennart Jörelid * @see The JAXB Reference Implementation */ public abstract class AbstractJavaGeneratorMojo extends AbstractJaxbMojo { private static final int XJC_COMPLETED_OK = 0; /** *

Corresponding XJC parameter: {@code catalog}.

*

Specify catalog files to resolve external entity references. * Supports TR9401, XCatalog, and OASIS XML Catalog format.

*/ @Parameter protected File catalog; /** *

Corresponding XJC parameter: {@code episode}.

*

Generate an episode file from this compilation, so that other schemas that rely on this schema can be * compiled later and rely on classes that are generated from this compilation. The generated episode file is * really just a JAXB customization file (but with vendor extensions.)

*

If this parameter is {@code true}, the episode file generated is called {@code META-INF/sun-jaxb.episode}, * and included in the artifact.

* * @see #STANDARD_EPISODE_FILENAME * @since 2.0 */ @Parameter(defaultValue = "true") protected boolean generateEpisode; /** *

Sets the HTTP/HTTPS proxy to be used by the XJC, on the format * {@code [user[:password]@]proxyHost[:proxyPort]}. * All information is retrieved from the active proxy within the standard maven settings file.

*/ @Parameter(defaultValue = "${settings}", readonly = true) protected Settings settings; /** *

Defines the content type of sources for the XJC. To simplify usage of the JAXB2 maven plugin, * all source files are assumed to have the same type of content.

*

This parameter replaces the previous multiple-choice boolean configuration options for the * jaxb2-maven-plugin (i.e. dtd, xmlschema, relaxng, relaxng-compact, wsdl), and * corresponds to setting one of those flags as an XJC argument.

* * @since 2.0 */ @Parameter(defaultValue = "XmlSchema") protected SourceContentType sourceType; /** *

Corresponding XJC parameter: {@code npa}.

*

Suppress the generation of package level annotations into {@code package-info.java}. * Using this switch causes the generated code to internalize those annotations into the other * generated classes.

* * @since 2.0 */ @Parameter(defaultValue = "false") protected boolean noPackageLevelAnnotations; /** *

Corresponding XJC parameter: {@code no-header}.

*

Suppress the generation of a file header comment that includes some note and timestamp. * Using this makes the generated code more diff-friendly.

* * @since 2.0 */ @Parameter(defaultValue = "false") protected boolean noGeneratedHeaderComments; /** *

Corresponding XJC parameter: {@code mark-generated}.

*

This feature causes all of the generated code to have {@code @Generated} annotation.

* * @since 2.0 */ @Parameter(defaultValue = "false") protected boolean addGeneratedAnnotation; /** *

Corresponding XJC parameter: {@code nv}.

*

By default, the XJC binding compiler performs strict validation of the source schema before processing it. * Use this option to disable strict schema validation. This does not mean that the binding compiler will not * perform any validation, it simply means that it will perform less-strict validation.

* * @since 2.0 */ @Parameter(defaultValue = "false") protected boolean laxSchemaValidation; /** *

Corresponding XJC parameter: {@code quiet}.

*

Suppress compiler output, such as progress information and warnings.

*/ @Parameter(defaultValue = "false") protected boolean quiet; /** *

Corresponding XJC parameter: {@code verbose}.

*

Tells XJC to be extra verbose, such as printing informational messages or displaying stack traces.

*/ @Parameter(property = "xjc.verbose", defaultValue = "false") protected boolean verbose; /** *

Corresponding XJC parameter: {@code extension}.

*

By default, the XJC binding compiler strictly enforces the rules outlined in the Compatibility chapter of * the JAXB Specification. Appendix E.2 defines a set of W3C XML Schema features that are not completely * supported by JAXB v1.0. In some cases, you may be allowed to use them in the "-extension" mode enabled by * this switch. In the default (strict) mode, you are also limited to using only the binding customizations * defined in the specification.

*/ @Parameter(defaultValue = "false") protected boolean extension; /** * Fails the Mojo execution if no XSDs/schemas are found. * * @since 1.3 */ @Parameter(defaultValue = "true") protected boolean failOnNoSchemas; /** *

Removes all files from the output directory before running XJC.

*/ @Parameter(defaultValue = "true") protected boolean clearOutputDir; /** *

Corresponding XJC parameter: {@code readOnly}.

*

By default, the XJC binding compiler does not write-protect the Java source files it generates. * Use this option to force the XJC binding compiler to mark the generated Java sources read-only.

* * @since 2.0 */ @Parameter(defaultValue = "false") protected boolean readOnly; /** *

List of ordered extra arguments to the XJC command. Each extra argument is interpreted as a word, intended * to be copied verbatim to the XJC argument list with spaces in between:

*
     * 
     *   <configuration>
     *   ...
     *       <arguments>
     *          <argument>-Xfluent-api</argument>
     *          <argument>somefile</argument>
     *      </arguments>
     *   </configuration>
     * 
     * 
*

The arguments configured above yields the following extra arguments to the XJC command: * -Xfluent-api -episode somefile

* * @since 2.0 * @deprecated This should be removed in the 2.0+ release, as all arguments should be handled by other parameters. */ @Parameter(property = "xjc.arguments") protected List arguments; /** *

Corresponding XJC parameter: {@code enableIntrospection}.

*

Enable correct generation of Boolean getters/setters to enable Bean Introspection APIs.

* * @since 1.4 */ @Parameter(defaultValue = "false") private boolean enableIntrospection; /** *

Corresponding XJC parameter: {@code p}.

*

The package under which the source files will be generated. Quoting the XJC documentation: * "Specifying a target package via this command-line option overrides any binding customization for package * name and the default package name algorithm defined in the specification".

*/ @Parameter protected String packageName; /** *

Corresponding XJC parameter: {@code target}.

*

Permitted values: {@code "2.0"} and {@code "2.1"}. Avoid generating code that relies on JAXB newer than the * version given. This will allow the generated code to run with JAXB 2.0 runtime (such as JavaSE 6.)

* * @since 1.3 */ @Parameter protected String target; /** *

If provided, this parameter indicates that the XSDs used by XJC to generate Java code should be * copied into the resulting artifact of this project (the JAR, WAR or whichever artifact type is generated). * The value of the {@code xsdPathWithinArtifact} parameter is the relative path within the artifact where * all source XSDs are copied to (hence the name "XSD Path Within Artifact").

*

The target directory is created within the artifact if it does not already exist. * If the {@code xsdPathWithinArtifact} parameter is not given, the XSDs used to generate Java code are * not included within the project's artifact.

*

Example:Adding the sample configuration below would copy all source XSDs to the given directory * within the resulting JAR (and/or test-JAR). If the directory {@code META-INF/jaxb/xsd} does not exist, it * will be created.

*
     *     
     *         <configuration>
     *             ...
     *             <xsdPathWithinArtifact>META-INF/jaxb/xsd</xsdPathWithinArtifact>
     *         </configuration>
     *     
     * 
*

Note: This parameter was previously called {@code includeSchemasOutputPath} * in the 1.x versions of this plugin, but was renamed and re-documented for improved usability and clarity.

* * @since 2.0 */ @Parameter protected String xsdPathWithinArtifact; /** *

Java generation is required if any of the file products is outdated/stale.

* {@inheritDoc} */ @Override protected boolean isReGenerationRequired() { // // Use the stale flag method to identify if we should re-generate the java source code from the supplied // Xml Schema. Basically, we should regenerate the JAXB code if: // // a) The staleFile does not exist // b) The staleFile exists and is older than one of the sources (XSD or XJB files). // "Older" is determined by comparing the modification timestamp of the staleFile and the source files. // final File staleFile = getStaleFile(); final String debugPrefix = "StaleFile [" + FileSystemUtilities.getCanonicalPath(staleFile) + "]"; boolean stale = !staleFile.exists(); if (stale) { getLog().debug(debugPrefix + " not found. JAXB (re-)generation required."); } else { final List sourceXSDs = getSources(); final List sourceXJBs = getSourceXJBs(); if (getLog().isDebugEnabled()) { getLog().debug(debugPrefix + " found. Checking timestamps on source XSD and XJB " + "files to determine if JAXB (re-)generation is required."); } final long staleFileLastModified = staleFile.lastModified(); for (URL current : sourceXSDs) { final URLConnection sourceXsdConnection; try { sourceXsdConnection = current.openConnection(); sourceXsdConnection.connect(); } catch (Exception e) { // Can't determine if the staleFile is younger than this sourceXSD. // Re-generate to be on the safe side. stale = true; break; } try { if (sourceXsdConnection.getLastModified() > staleFileLastModified) { if (getLog().isDebugEnabled()) { getLog().debug(current.toString() + " is newer than the stale flag file."); } stale = true; } } finally { if (sourceXsdConnection instanceof HttpURLConnection) { ((HttpURLConnection) sourceXsdConnection).disconnect(); } } } for (File current : sourceXJBs) { if (current.lastModified() > staleFileLastModified) { if (getLog().isDebugEnabled()) { getLog().debug(FileSystemUtilities.getCanonicalPath(current) + " is newer than the stale flag file."); } stale = true; break; } } } // All done. return stale; } /** * {@inheritDoc} */ @Override protected boolean performExecution() throws MojoExecutionException, MojoFailureException { boolean updateStaleFileTimestamp = false; try { // Setup the Tool's execution environment ToolExecutionEnvironment environment = null; try { // Create a LocaleFacet if the user has configured an explicit Locale for the tool. final LocaleFacet localeFacet = locale == null ? null : LocaleFacet.createFor(locale, getLog()); // Create the ToolExecutionEnvironment environment = new ToolExecutionEnvironment(getLog(), ThreadContextClassLoaderBuilder.createFor(this.getClass(), getLog(), getEncoding(false)) .addPaths(getClasspath()), LoggingHandlerEnvironmentFacet.create(getLog(), getClass(), getEncoding(false)), localeFacet); // Add any extra configured EnvironmentFacets, as configured in the POM. if (extraFacets != null) { for (EnvironmentFacet current : extraFacets) { environment.add(current); } } // Setup the environment. environment.setup(); // Compile the XJC arguments final String[] xjcArguments = getXjcArguments( environment.getClassPathAsArgument(), STANDARD_EPISODE_FILENAME); // Ensure that the outputDirectory exists, but only clear it if does not already FileSystemUtilities.createDirectory(getOutputDirectory(), clearOutputDir); // Do we need to re-create the episode file's parent directory. final boolean reCreateEpisodeFileParentDirectory = generateEpisode && clearOutputDir; if (reCreateEpisodeFileParentDirectory) { getEpisodeFile(STANDARD_EPISODE_FILENAME); } // Check the system properties. logSystemPropertiesAndBasedir(); // Fire XJC if (XJC_COMPLETED_OK != Driver.run(xjcArguments, new XjcLogAdapter(getLog()))) { final StringBuilder errorMsgBuilder = new StringBuilder(); errorMsgBuilder.append("\n+=================== [XJC Error]\n"); errorMsgBuilder.append("|\n"); final List sourceXSDs = getSources(); for (int i = 0; i < sourceXSDs.size(); i++) { errorMsgBuilder.append("| " + i + ": ").append(sourceXSDs.get(i).toString()).append("\n"); } errorMsgBuilder.append("|\n"); errorMsgBuilder.append("+=================== [End XJC Error]\n"); throw new MojoExecutionException(errorMsgBuilder.toString()); } // Indicate that the output directory was updated. getBuildContext().refresh(getOutputDirectory()); // Update the modification timestamp of the staleFile. updateStaleFileTimestamp = true; } finally { if (environment != null) { environment.restore(); } } // Add the generated source root to the project, enabling tooling and other plugins to see them. addGeneratedSourcesToProjectSourceRoot(); // Copy all source XSDs to the resulting artifact? if (xsdPathWithinArtifact != null) { final String buildOutputDirectory = getProject().getBuild().getOutputDirectory(); final File targetXsdDirectory = new File(buildOutputDirectory, xsdPathWithinArtifact); FileUtils.forceMkdir(targetXsdDirectory); for (URL current : getSources()) { String fileName = null; if ("file".equalsIgnoreCase(current.getProtocol())) { fileName = new File(current.getPath()).getName(); } else if ("jar".equalsIgnoreCase(current.getProtocol())) { // Typical JAR path // jar:file:/path/to/aJar.jar!/some/path/xsd/aResource.xsd final int bangIndex = current.toString().indexOf("!"); if (bangIndex == -1) { throw new MojoExecutionException("Illegal JAR URL [" + current.toString() + "]: lacks a '!'"); } final String internalPath = current.toString().substring(bangIndex + 1); fileName = new File(internalPath).getName(); } else { throw new MojoExecutionException("Could not extract FileName from URL [" + current + "]"); } final File targetFile = new File(targetXsdDirectory, fileName); if (targetFile.exists()) { // TODO: Should we throw an exception here instead? getLog().warn("File [" + FileSystemUtilities.getCanonicalPath(targetFile) + "] already exists. Not copying XSD file [" + current.getPath() + "] to it."); } IOUtil.copy(current.openStream(), new FileWriter(targetFile)); } // Refresh the BuildContext getBuildContext().refresh(targetXsdDirectory); } } catch (MojoExecutionException e) { throw e; } catch (NoSchemasException e) { if (failOnNoSchemas) { throw new MojoExecutionException("", e); } } catch (Exception e) { throw new MojoExecutionException(e.getMessage(), e); } // All done. return updateStaleFileTimestamp; } /** * Override this method to acquire a List holding all URLs to the JAXB sources for which this * AbstractJavaGeneratorMojo should generate Java files. Sources are assumed to be in the form given by * the {@code sourceType} value. * * @return A non-null List holding URLs to sources for the XJC generation. * @see #sourceType */ @Override protected abstract List getSources(); /** * Override this method to retrieve a list of Files to all XJB files for which this * AbstractJavaGeneratorMojo should generate Java files. * * @return A non-null List holding binding files. */ protected abstract List getSourceXJBs(); /** * Adds any directories containing the generated XJC classes to the appropriate Project compilation sources; * either {@code TestCompileSourceRoot} or {@code CompileSourceRoot} depending on the exact Mojo implementation * of this AbstractJavaGeneratorMojo. */ protected abstract void addGeneratedSourcesToProjectSourceRoot(); /** * Adds the supplied Resource to the project using the appropriate scope (i.e. resource or testResource) * depending on the exact implementation of this AbstractJavaGeneratorMojo. * * @param resource The resource to add. */ protected abstract void addResource(final Resource resource); // // Private helpers // private String[] getXjcArguments(final String classPath, final String episodeFileNameOrNull) throws MojoExecutionException, NoSchemasException { final ArgumentBuilder builder = new ArgumentBuilder(); // Add all flags on the form '-flagName' builder.withFlag(true, sourceType.getXjcArgument()); builder.withFlag(noPackageLevelAnnotations, "npa"); builder.withFlag(laxSchemaValidation, "nv"); builder.withFlag(verbose, "verbose"); builder.withFlag(quiet, "quiet"); builder.withFlag(enableIntrospection, "enableIntrospection"); builder.withFlag(extension, "extension"); builder.withFlag(readOnly, "readOnly"); builder.withFlag(noGeneratedHeaderComments, "no-header"); builder.withFlag(addGeneratedAnnotation, "mark-generated"); // Add all arguments on the form '-argumentName argumentValue' // (i.e. in 2 separate elements of the returned String[]) builder.withNamedArgument("httpproxy", getProxyString(settings.getActiveProxy())); builder.withNamedArgument("encoding", getEncoding(true)); builder.withNamedArgument("p", packageName); builder.withNamedArgument("target", target); builder.withNamedArgument("d", getOutputDirectory().getAbsolutePath()); builder.withNamedArgument("classpath", classPath); if (generateEpisode) { // We must use the -extension flag for the episode to work. if (!extension) { if (getLog().isInfoEnabled()) { getLog().info("Adding 'extension' flag to XJC arguments, since the 'generateEpisode' argument is " + "given. (XJCs 'episode' argument requires that the 'extension' argument is provided)."); } builder.withFlag(true, "extension"); } final File episodeFile = getEpisodeFile(episodeFileNameOrNull); builder.withNamedArgument("episode", FileSystemUtilities.getCanonicalPath(episodeFile)); } if (catalog != null) { builder.withNamedArgument("catalog", FileSystemUtilities.getCanonicalPath(catalog)); } if (arguments != null) { builder.withPreCompiledArguments(arguments); } for (File current : getSourceXJBs()) { // Shorten the argument? // final String strippedXjbPath = FileSystemUtilities.relativize( // current.getAbsolutePath(), getProject().getBasedir()); // Each XJB must be given as a separate argument. builder.withPreCompiledArguments(Arrays.asList("-b", current.getAbsolutePath())); } final List sourceXSDs = getSources(); if (sourceXSDs.isEmpty()) { // If we have no XSDs, we are not going to be able to run XJC. getLog().warn("No XSD files found. Please check your plugin configuration."); throw new NoSchemasException(); } else { final List unwrappedSourceXSDs = new ArrayList(); for (URL current : sourceXSDs) { // Shorten the argument if possible. if ("file".equalsIgnoreCase(current.getProtocol())) { unwrappedSourceXSDs.add(FileSystemUtilities.relativize( current.getPath(), new File(System.getProperty("user.dir")))); } else { unwrappedSourceXSDs.add(current.toString()); } } builder.withPreCompiledArguments(unwrappedSourceXSDs); } // All done. return logAndReturnToolArguments(builder.build(), "XJC"); } private String getProxyString(final Proxy activeProxy) { // Check sanity if (activeProxy == null) { return null; } // The XJC proxy argument should be on the form // [user[:password]@]proxyHost[:proxyPort] // // builder.withNamedArgument("httpproxy", httpproxy); // final StringBuilder proxyBuilder = new StringBuilder(); if (activeProxy.getUsername() != null) { // Start with the username. proxyBuilder.append(activeProxy.getUsername()); // Append the password if provided. if (activeProxy.getPassword() != null) { proxyBuilder.append(":").append(activeProxy.getPassword()); } proxyBuilder.append("@"); } // Append hostname and port. proxyBuilder.append(activeProxy.getHost()).append(":").append(activeProxy.getPort()); // All done. return proxyBuilder.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy