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

org.jvnet.jaxb2.maven2.RawXJC2Mojo Maven / Gradle / Ivy

/*
 * Copyright [2006] java.net
 * 
 * Licensed 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. 
 */

package org.jvnet.jaxb2.maven2;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DependencyManagement;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.artifact.InvalidDependencyVersionException;
import org.codehaus.plexus.util.FileUtils;
import org.jfrog.maven.annomojo.annotations.MojoGoal;
import org.jfrog.maven.annomojo.annotations.MojoPhase;
import org.jvnet.jaxb2.maven2.util.ArtifactUtils;
import org.jvnet.jaxb2.maven2.util.CollectionUtils;
import org.jvnet.jaxb2.maven2.util.IOUtils;
import org.jvnet.jaxb2.maven2.util.StringUtils;
import org.xml.sax.InputSource;

import com.sun.codemodel.CodeWriter;
import com.sun.codemodel.JCodeModel;
import com.sun.org.apache.xml.internal.resolver.tools.CatalogResolver;
import com.sun.tools.xjc.BadCommandLineException;
import com.sun.tools.xjc.Language;
import com.sun.tools.xjc.ModelLoader;
import com.sun.tools.xjc.Options;
import com.sun.tools.xjc.api.SpecVersion;
import com.sun.tools.xjc.model.Model;
import com.sun.tools.xjc.outline.Outline;

/**
 * Maven JAXB 2.x Mojo.
 * 
 * @author Aleksei Valikov ([email protected])
 */
@MojoGoal("generate")
@MojoPhase("generate-sources")
public class RawXJC2Mojo extends AbstractXJC2Mojo {

	private Collection xjcPluginArtifacts;

	private Collection xjcPluginFiles;

	private Collection xjcPluginURLs;

	public Collection getXjcPluginArtifacts() {
		return xjcPluginArtifacts;
	}

	public Collection getXjcPluginFiles() {
		return xjcPluginFiles;
	}

	public Collection getXjcPluginURLs() {
		return xjcPluginURLs;
	}

	private Collection episodeArtifacts;

	private Collection episodeFiles;

	public Collection getEpisodeArtifacts() {
		return episodeArtifacts;
	}

	public Collection getEpisodeFiles() {
		return episodeFiles;
	}

	private List schemaFiles;

	public List getSchemaFiles() {
		return schemaFiles;
	}

	private List bindingFiles;

	public List getBindingFiles() {
		return bindingFiles;
	}

	private List dependsFiles;

	public List getDependsFiles() {
		return dependsFiles;
	}

	private List producesFiles;

	public List getProducesFiles() {
		return producesFiles;
	}

	/**
	 * Execute the maven2 mojo to invoke the xjc2 compiler based on any
	 * configuration settings.
	 */
	public void execute() throws MojoExecutionException {

		injectDependencyDefaults();
		resolveArtifacts();

		// Install project dependencies into classloader's class path
		// and execute xjc2.
		final ClassLoader currentClassLoader = Thread.currentThread()
				.getContextClassLoader();
		final ClassLoader classLoader = createClassLoader(currentClassLoader);
		Thread.currentThread().setContextClassLoader(classLoader);
		try {
			doExecute();

		} finally {
			// Set back the old classloader
			Thread.currentThread().setContextClassLoader(currentClassLoader);
		}
	}

	/**
	 * *************************************************************************
	 * *
	 */

	protected void injectDependencyDefaults() {
		injectDependencyDefaults(getPlugins());
		injectDependencyDefaults(getEpisodes());
	}

	@SuppressWarnings("unchecked")
	protected void injectDependencyDefaults(Dependency[] dependencies) {
		if (dependencies != null) {
			final Map dependencyMap = new TreeMap();
			for (final Dependency dependency : dependencies) {
				dependencyMap.put(dependency.getManagementKey(), dependency);
			}

			final DependencyManagement dependencyManagement = getProject()
					.getDependencyManagement();

			if (dependencyManagement != null) {
				merge(dependencyMap, dependencyManagement.getDependencies());
			}
			if (getProject().getDependencies() != null) {
				merge(dependencyMap, getProject().getDependencies());
			}
		}
	}

	private void merge(final Map dependencyMap,
			final List managedDependencies) {
		for (final Dependency managedDependency : managedDependencies) {
			final String key = managedDependency.getManagementKey();
			final Dependency dependency = (Dependency) dependencyMap.get(key);
			if (dependency != null) {
				ArtifactUtils.mergeDependencyWithDefaults(dependency,
						managedDependency);
			}
		}
	}

	protected void resolveArtifacts() throws MojoExecutionException {
		try {

			resolveXJCPluginArtifacts();
			resolveEpisodeArtifacts();
		} catch (ArtifactResolutionException arex) {
			throw new MojoExecutionException("Could not resolve the artifact.",
					arex);
		} catch (ArtifactNotFoundException anfex) {
			throw new MojoExecutionException("Artifact not found.", anfex);
		} catch (InvalidDependencyVersionException idvex) {
			throw new MojoExecutionException("Invalid dependency version.",
					idvex);
		}
	}

	protected void resolveXJCPluginArtifacts()
			throws ArtifactResolutionException, ArtifactNotFoundException,
			InvalidDependencyVersionException {

		this.xjcPluginArtifacts = ArtifactUtils.resolveTransitively(
				getArtifactFactory(), getArtifactResolver(),
				getLocalRepository(), getArtifactMetadataSource(),
				getPlugins(), getProject());
		this.xjcPluginFiles = ArtifactUtils.getFiles(this.xjcPluginArtifacts);
		this.xjcPluginURLs = CollectionUtils.apply(this.xjcPluginFiles,
				IOUtils.GET_URL);
	}

	protected void resolveEpisodeArtifacts()
			throws ArtifactResolutionException, ArtifactNotFoundException,
			InvalidDependencyVersionException {
		this.episodeArtifacts = ArtifactUtils.resolve(getArtifactFactory(),
				getArtifactResolver(), getLocalRepository(),
				getArtifactMetadataSource(), getEpisodes(), getProject());
		this.episodeFiles = ArtifactUtils.getFiles(this.episodeArtifacts);
	}

	/**
	 * @param parent
	 *            the returned classLoader will be a descendant of this one.
	 * @return a context class loader with a classPath containing the project
	 *         dependencies.
	 * @throws MojoExecutionException
	 */
	protected ClassLoader createClassLoader(ClassLoader parent)
			throws MojoExecutionException {

		final Collection xjcPluginURLs = getXjcPluginURLs();

		return new ParentFirstClassLoader(xjcPluginURLs
				.toArray(new URL[xjcPluginURLs.size()]), parent);
	}

	protected void doExecute() throws MojoExecutionException {
		setupLogging();
		if (getVerbose())
			getLog().info("Started execution.");
		setupMavenPaths();
		setupDirectories();
		setupFiles();
		if (getVerbose()) {
			logConfiguration();
		}

		final Options options = setupOptions();

		if (!this.getForceRegenerate() && isUpToDate()) {
			getLog()
					.info(
							"Skipped XJC execution.  Generated sources were up-to-date.");
			return;
		}

		final Model model = loadModel(options);
		final Outline outline = generateCode(model);
		writeCode(outline);
		if (getVerbose())
			getLog().info("Finished execution.");
	}

	/**
	 * Initializes logging. If Maven is run in debug mode (that is, debug level
	 * is enabled in the log), turn on the verbose mode in Mojo. Further on, if
	 * vebose mode is on, set the
	 * com.sun.tools.xjc.Options.findServices system property on to
	 * enable debuggin of XJC plugins.
	 * 
	 */
	protected void setupLogging() {

		setVerbose(getVerbose() || getLog().isDebugEnabled());

		if (getVerbose()) {
			System
					.setProperty("com.sun.tools.xjc.Options.findServices",
							"true");
		}

	}

	/**
	 * Augments Maven paths with generated resources.
	 */
	protected void setupMavenPaths() {
		getProject().addCompileSourceRoot(getGenerateDirectory().getPath());

		// Mark XJC_gen prop files as resources for inclusion to final jar.
		final Resource generatedResource = new Resource();
		generatedResource.setDirectory(getGenerateDirectory().getPath());
		generatedResource.addInclude("**/jaxb.properties");
		generatedResource.addInclude("**/bgm.ser");

		getProject().addResource(generatedResource);

		if (getEpisode() && getEpisodeFile() != null) {
			final String episodeFilePath = getEpisodeFile().getAbsolutePath();
			final String generatedDirectoryPath = getGenerateDirectory()
					.getAbsolutePath();

			if (episodeFilePath.startsWith(generatedDirectoryPath
					+ File.separator)) {
				final String path = episodeFilePath
						.substring(generatedDirectoryPath.length() + 1);

				final Resource resource = new Resource();
				resource.setDirectory(generatedDirectoryPath);
				resource.addInclude(path);
				getProject().addResource(resource);
			}
		}
	}

	protected void setupDirectories() {

		final File generateDirectory = getGenerateDirectory();
		if (getRemoveOldOutput() && generateDirectory.exists()) {
			try {
				FileUtils.deleteDirectory(this.getGenerateDirectory());
			} catch (IOException ex) {
				getLog().warn(
						"Failed to remove old generateDirectory ["
								+ generateDirectory + "].", ex);
			}
		}

		// Create the destination path if it does not exist.
		if (generateDirectory != null && !generateDirectory.exists()) {
			generateDirectory.mkdirs();
		}

		final File episodeFile = getEpisodeFile();
		if (getEpisode() && episodeFile != null) {
			final File parentFile = episodeFile.getParentFile();
			parentFile.mkdirs();
		}
	}

	protected void setupFiles() throws MojoExecutionException {
		setupSchemaFiles();
		setupBindingFiles();
		setupDependsFiles();
		setupProducesFiles();
	}

	protected void setupSchemaFiles() throws MojoExecutionException {
		try {
			this.schemaFiles = IOUtils.scanDirectoryForFiles(
					getSchemaDirectory(), getSchemaIncludes(),
					getSchemaExcludes(), !getDisableDefaultExcludes());
		} catch (IOException ioex) {
			throw new MojoExecutionException("Could not setup schema files.",
					ioex);
		}
	}

	protected void setupBindingFiles() throws MojoExecutionException {
		try {
			this.bindingFiles = IOUtils.scanDirectoryForFiles(
					getBindingDirectory(), getBindingIncludes(),
					getBindingExcludes(), !getDisableDefaultExcludes());
		} catch (IOException ioex) {
			throw new MojoExecutionException("Could not setup schema files.",
					ioex);
		}
	}

	protected void setupDependsFiles() {
		final List dependsFiles = new ArrayList();

		dependsFiles.addAll(getSchemaFiles());
		dependsFiles.addAll(getBindingFiles());
		final File catalog = getCatalog();
		if (catalog != null) {
			dependsFiles.add(catalog);
		}
		dependsFiles.add(getProject().getFile());
		if (getOtherDepends() != null) {
			dependsFiles.addAll(Arrays.asList(getOtherDepends()));
		}
		this.dependsFiles = dependsFiles;
	}

	protected void setupProducesFiles() throws MojoExecutionException {
		try {
			this.producesFiles = IOUtils.scanDirectoryForFiles(
					getGenerateDirectory(), new String[] { "**/*.*",
							"**/*.java", "**/bgm.ser", "**/jaxb.properties" },
					new String[0], !getDisableDefaultExcludes());
		} catch (IOException ioex) {
			throw new MojoExecutionException("Could not setup produced files.",
					ioex);
		}
	}

	/**
	 * Log the configuration settings. Shown when exception thrown or when
	 * verbose is true.
	 */
	protected void logConfiguration() throws MojoExecutionException {
		super.logConfiguration();
		getLog().info("schemaFiles (calculated):" + getSchemaFiles());
		getLog().info("bindingFiles (calculated):" + getBindingFiles());
		getLog().info(
				"xjcPluginArtifacts (resolved):" + getXjcPluginArtifacts());
		getLog().info("xjcPluginFiles (resolved):" + getXjcPluginFiles());
		getLog().info("xjcPluginURLs (resolved):" + getXjcPluginURLs());
		getLog().info("episodeArtifacts (resolved):" + getEpisodeArtifacts());
		getLog().info("episodeFiles (resolved):" + getEpisodeArtifacts());
	}

	/**
	 * *************************************************************************
	 * *
	 */

	/**
	 * Creates and initializes an instance of XJC options.
	 * 
	 */
	protected Options setupOptions() throws MojoExecutionException {
		final Options options = new Options();

		options.verbose = this.getVerbose();
		options.classpaths.addAll(getXjcPluginURLs());
		options.debugMode = this.getDebug();
		final SpecVersion specVersion = SpecVersion.parse(getSpecVersion());
		options.target = specVersion == null ? SpecVersion.LATEST : specVersion;

		final Language schemaLanguage = getLanguage();
		if (schemaLanguage != null) {
			options.setSchemaLanguage(schemaLanguage);
		}

		for (InputSource grammar : getGrammars()) {
			options.addGrammar(grammar);
		}

		for (InputSource bindFile : getBindFiles()) {
			options.addBindFile(bindFile);
		}

		final CatalogResolver catalogResolver = getCatalogResolverInstance();
		if (catalogResolver != null) {
			options.entityResolver = catalogResolver;
		}

		// Setup Catalog files (XML Entity Resolver).
		final File catalog = this.getCatalog();
		if (catalog != null) {
			try {
				options.addCatalog(catalog);
			} catch (IOException ioex) {
				throw new MojoExecutionException(
						"Error while setting the catalog to ["
								+ catalog.getAbsolutePath() + "].", ioex);
			}
		}

		// Setup Other Options

		options.defaultPackage = getGeneratePackage();
		options.targetDir = getGenerateDirectory();

		options.strictCheck = this.getStrict();
		options.readOnly = this.getReadOnly();

		if (getExtension()) {
			options.compatibilityMode = Options.EXTENSION;
		}

		final String[] arguments = getArguments();
		try {
			options.parseArguments(arguments);
		}

		catch (BadCommandLineException bclex) {
			throw new MojoExecutionException("Error parsing the command line ["
					+ arguments + "]", bclex);
		}

		return options;
	}

	/**
	 * Returns the internal schema language as enum, or null for
	 * autodetect.
	 * 
	 * @return Internal schema language.
	 */
	protected Language getLanguage() throws MojoExecutionException {
		// Setup Schema Language.
		final String schemaLanguage = getSchemaLanguage();
		if (StringUtils.isEmpty(schemaLanguage)) {

			if (getVerbose()) {
				getLog()
						.info(
								"The schemaLanguage setting was not defined, assuming 'AUTODETECT'.");
			}
			return null;
		} else if ("AUTODETECT".equalsIgnoreCase(schemaLanguage))
			return null; // nothing, it is AUTDETECT by default.
		else if ("XMLSCHEMA".equalsIgnoreCase(schemaLanguage))
			return Language.XMLSCHEMA;
		else if ("DTD".equalsIgnoreCase(schemaLanguage))
			return Language.DTD;
		else if ("RELAXNG".equalsIgnoreCase(schemaLanguage))
			return Language.RELAXNG;
		else if ("RELAXNG_COMPACT".equalsIgnoreCase(schemaLanguage))
			return Language.RELAXNG_COMPACT;
		else if ("WSDL".equalsIgnoreCase(schemaLanguage))
			return Language.WSDL;
		else {
			throw new MojoExecutionException("Unknown schemaLanguage ["
					+ schemaLanguage + "]");
		}
	}

	protected List getGrammars() throws MojoExecutionException {
		final List schemaFiles = getSchemaFiles();
		final List grammars = new ArrayList(
				schemaFiles.size());
		for (final File schemaFile : schemaFiles) {
			grammars.add(IOUtils.getInputSource(schemaFile));
		}
		return grammars;
	}

	protected List getBindFiles() throws MojoExecutionException {
		final List bindingFiles = getBindingFiles();
		final List bindFiles = new ArrayList(
				bindingFiles.size());
		for (final File bindingFile : bindingFiles) {
			bindFiles.add(IOUtils.getInputSource(bindingFile));
		}
		return bindFiles;

	}

	/**
	 * Creates an instance of catalog resolver.
	 * 
	 * @return
	 * @throws MojoExecutionException
	 */
	protected CatalogResolver getCatalogResolverInstance()
			throws MojoExecutionException {
		if (getCatalogResolver() == null) {
			return null;
		} else {

			try {
				final String catalogResolverClassName = getCatalogResolver()
						.trim();
				final Class draftCatalogResolverClass = Thread
						.currentThread().getContextClassLoader().loadClass(
								catalogResolverClassName);
				if (!CatalogResolver.class
						.isAssignableFrom(draftCatalogResolverClass)) {
					throw new MojoExecutionException(
							"Specified catalog resolver class ["
									+ catalogResolver
									+ "] could not be casted to ["
									+ CatalogResolver.class + "].");
				} else {
					@SuppressWarnings("unchecked")
					final Class catalogResolverClass = (Class) draftCatalogResolverClass;
					final CatalogResolver catalogResolverInstance = catalogResolverClass
							.newInstance();
					return catalogResolverInstance;
				}
			} catch (ClassNotFoundException cnfex) {
				throw new MojoExecutionException(
						"Could not find specified catalog resolver class ["
								+ catalogResolver + "].", cnfex);
			} catch (InstantiationException iex) {
				throw new MojoExecutionException(
						"Could not instantiate catalog resolver class ["
								+ catalogResolver + "].", iex);
			} catch (IllegalAccessException iaex) {
				throw new MojoExecutionException(
						"Could not instantiate catalog resolver class ["
								+ catalogResolver + "].", iaex);
			}

		}
	}

	/**
	 * @return true to indicate results are up-to-date, that is, when the latest
	 *         from input files is earlier than the younger from the output
	 *         files (meaning no re-execution required).
	 */
	protected boolean isUpToDate() throws MojoExecutionException {
		final List dependsFiles = getDependsFiles();
		final List producesFiles = getProducesFiles();

		if (getVerbose())
			getLog()
					.info("Checking up-to-date depends [" + dependsFiles + "].");

		if (getVerbose())
			getLog().info(
					"Checking up-to-date produces [" + producesFiles + "].");

		final Long dependsTimestamp = CollectionUtils.bestValue(dependsFiles,
				IOUtils.LAST_MODIFIED, CollectionUtils. gt());
		final Long producesTimestamp = CollectionUtils.bestValue(producesFiles,
				IOUtils.LAST_MODIFIED, CollectionUtils. lt());

		if (getVerbose())
			getLog().info(
					"Depends timestamp [" + dependsTimestamp
							+ "], produces timestamp [" + producesTimestamp
							+ "].");

		return producesTimestamp != null
				&& CollectionUtils. lt().compare(dependsTimestamp,
						producesTimestamp) > 0;
	}

	protected Model loadModel(Options options) throws MojoExecutionException {
		if (getVerbose()) {
			getLog().info("Parsing input schema(s)...");
		}
		final Model model = ModelLoader.load(options, new JCodeModel(),
				new LoggingErrorReceiver("Error while parsing schema(s).",
						getLog(), getVerbose()));

		if (model == null)
			throw new MojoExecutionException(
					"Unable to parse input schema(s). Error messages should have been provided.");
		return model;
	}

	protected Outline generateCode(final Model model)
			throws MojoExecutionException {
		if (getVerbose()) {
			getLog().info("Compiling input schema(s)...");
		}

		final Outline outline = model.generateCode(model.options,
				new LoggingErrorReceiver("Error while generating code.",
						getLog(), getVerbose()));
		if (outline == null) {
			throw new MojoExecutionException(
					"Failed to compile input schema(s)!  Error messages should have been provided.");
		} else {
			return outline;
		}
	}

	protected void writeCode(Outline outline) throws MojoExecutionException {

		if (getWriteCode()) {
			if (getVerbose())
				getLog().info(
						"Writing output to ["
								+ outline.getModel().options.targetDir
										.getAbsolutePath() + "].");

			try {
				final CodeWriter codeWriter = new LoggingCodeWriter(outline
						.getModel().options.createCodeWriter(), getLog(),
						getVerbose());
				outline.getModel().codeModel.build(codeWriter);
			} catch (IOException e) {
				throw new MojoExecutionException("Unable to write files: "
						+ e.getMessage(), e);
			}
		} else {
			getLog().info("Code will not be written.");
		}
	}

	/**
	 * Returns array of command line arguments for XJC. These arguments are
	 * based on the configured arguments (see {@link #getArgs()}) but also
	 * include episode arguments.
	 * 
	 * @return String array of XJC command line options.
	 * @throws MojoExecutionException
	 */

	protected String[] getArguments() throws MojoExecutionException {

		final List arguments = new ArrayList(getArgs());
		if (getEpisode() && getEpisodeFile() != null) {
			arguments.add("-episode");
			arguments.add(getEpisodeFile().getAbsolutePath());
		}

		for (final File episodeFile : getEpisodeFiles()) {
			arguments.add(episodeFile.getAbsolutePath());
		}
		return arguments.toArray(new String[arguments.size()]);
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy