/*******************************************************************************
* Copyright (c) 2006-2010 eBay Inc. All Rights Reserved.
* 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
*******************************************************************************/
package org.ebayopensource.turmeric.plugins;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.interpolation.EnvarBasedValueSource;
import org.codehaus.plexus.interpolation.InterpolationException;
import org.codehaus.plexus.interpolation.Interpolator;
import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.StringUtils;
import org.ebayopensource.turmeric.plugins.catalog.CatalogConfiguration;
/**
* Goal which processes the ServiceCodeGen.xsd and generates various types of xml, and java code.
*
* @goal xjc-episode
* @phase process-sources
* @requiresDependencyResolution compile
*/
public class XjcEpisodeMojo extends AbstractMojo {
/**
* The default maven project object
*
* @parameter expression="${project}"
* @required
* @readonly
*/
private MavenProject project;
/**
* Location of generated java code
*
* @parameter expression="${jaxb.episode.output.directory}" default-value=
* "${project.build.directory}/generated-sources/jaxb-episode"
* @required
*/
private File sourcesOutputDirectory = new File("${project.build.directory}/generated-sources/jaxb-episode");
/**
* Location of generated resources
*
* @parameter expression="${jaxb.episode.resources.output.directory}" default-value=
* "${project.build.directory}/generated-resources/jaxb-episode"
* @required
*/
private File resourcesOutputDirectory = new File("${project.build.directory}/generated-resources/jaxb-episode");
/**
* Relative path to the generated episode file.
*
* @parameter expression="${jaxb.episode.episode.name}" default-value="META-INF/SOAClient.episode"
* @required
*/
private String episodePath;
/**
* Schema file to generate SOA episode from.
*
* @parameter expression="${schemaEpisode}"
* @required
*/
private File xsdEpisodeFile;
/**
* Catalog to help find content such as <code><xs:import></code> references
* to schemas found in the project dependencies instead.
*
* @parameter
* @optional
*/
private CatalogConfiguration catalog;
/**
* List of packages to exclude (delete) from the generated sources
*
* @parameter expression="${excludedPackages}"
* @optional
*/
private String excludedPackages[];
/**
* Verbose XJC Output
*
* @parameter expression="${verbose}" default-value="false"
* @optional
*/
private boolean verbose = false;
/**
* Schemas to generate sources from.
*
* @parameter expression="${service.codegen.schemas}"
* @required
*/
private File schemas[];
/**
* Timestamp file used for tracking last generation and preventing
* a loop of generation seen in m2eclipse.
*
* @parameter expression="${xjc.timestamp.file}"
* default-value="${project.build.directory}/jaxb-episode-gen-timestamp"
* @required
*/
private File xjcTimestampFile = new File("${project.build.directory}/jaxb-episode-gen-timestamp");
private File catalogFile;
public MavenProject getProject() {
return project;
}
private void ensureDirectoryExists(String id, File dir)
throws MojoExecutionException {
if (!dir.exists()) {
if (!dir.mkdirs()) {
throw new MojoExecutionException("Unable to create " + id
+ ": " + dir);
}
}
}
public void execute() throws MojoExecutionException {
sourcesOutputDirectory = expandFile("sourcesOutputDirectory", sourcesOutputDirectory);
resourcesOutputDirectory = expandFile("resourcesOutputDirectory", resourcesOutputDirectory);
ensureDirectoryExists("sourcesOutputDirectory", sourcesOutputDirectory);
ensureDirectoryExists("resourcesOutputDirectory", resourcesOutputDirectory);
// When Unit testing the plugin, the project object is unset
if (project != null) {
// Attach the generated source directory to the maven project
project.addCompileSourceRoot(sourcesOutputDirectory.getAbsolutePath());
// Attach the generated resources directory to the maven project
Resource resource = new Resource();
resource.setDirectory(resourcesOutputDirectory.getAbsolutePath());
resource.addInclude("**/" + episodePath);
project.addResource(resource);
}
getLog().debug("Catalog = " + catalog);
if(catalog != null) {
File targetDir = new File(project.getBuild().getDirectory());
ensureDirectoryExists("target", targetDir);
catalogFile = new File(targetDir, "jaxb-episode-catalog.xml");
FileWriter writer = null;
PrintWriter out = null;
try {
writer = new FileWriter(catalogFile);
out = new PrintWriter(writer);
catalog.writeXml(out);
out.flush();
} catch(IOException e) {
throw new MojoExecutionException("Unable to write temporary catalog file: " + catalogFile, e);
} finally {
IOUtil.close(out);
IOUtil.close(writer);
}
}
// Expand any paths
episodePath = expandParameter("episodePath", episodePath);
xjcTimestampFile = expandFile("xjcTimestampFile", xjcTimestampFile);
xsdEpisodeFile = expandFile("xsdEpisodeFile", xsdEpisodeFile);
if(schemas != null) {
for (int i = 0; i < schemas.length; i++) {
schemas[i] = expandFile("schemas[" + i + "]", schemas[i]);
}
}
// Test to see if a rebuild is even required.
if (needsGeneration() == false) {
getLog().info("Skipping xjc-episode, No generation needed");
return;
}
// Perform generation
long now = System.currentTimeMillis();
try {
File episodeFile = new File(resourcesOutputDirectory, episodePath);
ensureDirectoryExists("Episode Home Dir",
episodeFile.getParentFile());
XjcEpisode gen = new XjcEpisode();
gen.setVerbose(verbose);
gen.setLog(getLog());
gen.setCatalogFile(catalogFile);
gen.setXsdEpisodeFile(xsdEpisodeFile);
gen.setSourceOutputDir(sourcesOutputDirectory);
gen.setResourceOutputDir(resourcesOutputDirectory);
gen.setEpisodeFile(episodeFile);
gen.readXSD();
gen.xjcCreateEpisode();
gen.filterEpisode();
gen.generateEpisodeSourceFiles();
if (excludedPackages != null) {
File packageDir;
for (String excludedPackage : excludedPackages) {
packageDir = new File(sourcesOutputDirectory,
excludedPackage.replace('.', File.separatorChar));
if (packageDir.exists()) {
getLog().info(
"Removing exclude Source Package: "
+ excludedPackage);
FileUtils.deleteDirectory(packageDir);
}
}
}
for (File xsd : schemas) {
gen.generateSourceFiles(xsd);
}
gen.deleteObjectFactory();
writeLastGenTimestamp(now);
} catch (Exception e) {
throw new MojoExecutionException("Unable to generate code", e);
}
}
private void writeLastGenTimestamp(long timestamp)
throws MojoExecutionException {
try {
FileUtils.fileWrite(xjcTimestampFile.getAbsolutePath(), "UTF-8",
String.valueOf(timestamp));
} catch (IOException e) {
throw new MojoExecutionException(
"Unable to write timestamp generation tracking file: "
+ xjcTimestampFile, e);
}
}
/**
* Check the various files and timestamps to determine if a generation
* is needed.
* @return true if generation is needed
*/
private boolean needsGeneration() {
if(xjcTimestampFile == null) {
File targetDir = new File(project.getBuild().getDirectory());
xjcTimestampFile = new File(targetDir, "jaxb-episode-gen-timestamp");
}
if(!xjcTimestampFile.exists()) {
getLog().debug("Generation Needed: Timestamp file not present: " + xjcTimestampFile);
return true;
}
// Load Timestamp
long lastGenTimestamp = 0;
try {
String rawTimestamp = FileUtils.fileRead(xjcTimestampFile);
if (StringUtils.isBlank(rawTimestamp)) {
getLog().debug(
"Generation Needed: last timestamp is blank: "
+ xjcTimestampFile);
return true;
}
lastGenTimestamp = Long.parseLong(rawTimestamp.trim());
} catch (IOException e) {
getLog().debug(
"Generation Needed: Unable to read last timestamp: "
+ xjcTimestampFile, e);
return true;
}
// Test the various xsd files for changes.
if (modifiedSince(lastGenTimestamp, xsdEpisodeFile)) {
getLog().debug(
"Generation Needed: File modified since last generation: "
+ xsdEpisodeFile);
}
for (File xsd : schemas) {
if (modifiedSince(lastGenTimestamp, xsd)) {
getLog().debug(
"Generation Needed: File modified since last generation: "
+ xsd);
}
}
// No need to generate
getLog().debug("No need to generate this time, nothing modified");
return false;
}
private boolean modifiedSince(long lastGenTimestamp, File file) {
return file.lastModified() > lastGenTimestamp;
}
/**
* Convenience method for using {@link #expandParameter(String)} with
* File objects.
*
* @param rawfile the raw file object
* @return null if rawfile is null, or the expanded File object.
* @throws MojoExecutionException
*/
private File expandFile(String name, File rawfile) throws MojoExecutionException {
if (rawfile == null) {
return null;
}
String rawpath = rawfile.getPath();
return new File(expandParameter(name, rawpath));
}
/**
* Take a raw parameter value, and expand any found properties within it.
*
* @param parameter
* @return the expanded parameter
* @throws MojoExecutionException
* if unable to interpolate
*/
private String expandParameter(String name, String rawparameter)
throws MojoExecutionException {
if (StringUtils.isBlank(rawparameter)) {
return rawparameter;
}
try {
Interpolator interpolator = new RegexBasedInterpolator();
interpolator.addValueSource(new PrefixedObjectValueSource("project",
project));
interpolator.addValueSource(new PrefixedObjectValueSource("mojo",
this));
interpolator.addValueSource(new PropertiesBasedValueSource(System
.getProperties()));
interpolator.addValueSource(new EnvarBasedValueSource());
String result = interpolator.interpolate(rawparameter);
if(getLog().isDebugEnabled()) {
StringBuilder msg = new StringBuilder();
msg.append("Expand Parameter: ").append(name);
msg.append("\n Raw : ").append(rawparameter);
msg.append("\n Expanded: ").append(result);
getLog().debug(msg.toString());
}
return result;
} catch (IOException e) {
throw new MojoExecutionException(
"Unable to use Environment for Parameter Interpolation", e);
} catch (InterpolationException e) {
throw new MojoExecutionException("Unable to use interpolatate: "
+ rawparameter, e);
}
}
public File getXsdEpisodeFile() {
return xsdEpisodeFile;
}
}