org.smallmind.web.jaxws.AbstractJaxwsMojo Maven / Gradle / Ivy
/*
* Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 David Berkman
*
* This file is part of the SmallMind Code Project.
*
* The SmallMind Code Project is free software, you can redistribute
* it and/or modify it under either, at your discretion...
*
* 1) The terms of GNU Affero General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* ...or...
*
* 2) The terms of the Apache License, Version 2.0.
*
* The SmallMind Code Project is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License or Apache License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and the Apache License along with the SmallMind Code Project. If not, see
* or .
*
* Additional permission under the GNU Affero GPL version 3 section 7
* ------------------------------------------------------------------
* If you modify this Program, or any covered work, by linking or
* combining it with other code, such other code is not for that reason
* alone subject to any of the requirements of the GNU Affero GPL
* version 3.
*/
package org.smallmind.web.jaxws;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.Os;
import org.codehaus.plexus.util.cli.CommandLineException;
import org.codehaus.plexus.util.cli.CommandLineUtils;
import org.codehaus.plexus.util.cli.Commandline;
import org.codehaus.plexus.util.cli.DefaultConsumer;
import org.codehaus.plexus.util.cli.StreamConsumer;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.graph.DependencyNode;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.DependencyResolutionException;
import org.eclipse.aether.resolution.DependencyResult;
abstract class AbstractJaxwsMojo extends AbstractMojo {
private static final Logger logger = Logger.getLogger(AbstractJaxwsMojo.class.getName());
private static final List METRO_22 = new ArrayList();
private static final List METRO_221 = new ArrayList();
private static final List METRO_23 = new ArrayList();
static {
// METRO_22.add("-encoding");
METRO_22.add("-clientjar");
METRO_22.add("-generateJWS");
METRO_22.add("-implDestDir");
METRO_22.add("-implServiceName");
METRO_22.add("-implPortName");
METRO_221.addAll(METRO_22);
METRO_221.add("-XdisableAuthenticator");
METRO_23.addAll(METRO_221);
METRO_23.add("-x");
}
/**
* The Maven Project Object.
*/
@Component
protected MavenProject project;
/**
* Output messages about what the tool is doing.
*/
@Parameter(defaultValue = "false")
protected boolean verbose;
/**
* Keep generated files.
*/
@Parameter(defaultValue = "true")
protected boolean keep;
/**
* Specify character encoding used by source files.
*/
@Parameter(property = "project.build.sourceEncoding")
protected String encoding;
/*
* Information about this plugin, used to lookup this plugin's configuration
* from the currently executing project.
*
* @since 2.3.1
*/
@Parameter(defaultValue = "${plugin}", readonly = true)
protected PluginDescriptor pluginDescriptor;
/**
* Allow to use the JAXWS Vendor Extensions.
*/
@Parameter(defaultValue = "false")
private boolean extension;
/**
* Specify optional command-line options.
*
* Multiple elements can be specified, and each token must be placed in its own list.
*
*/
@Parameter
private List args;
/**
* Specify optional JVM options.
*
* Multiple elements can be specified, and each token must be placed in its own list.
*
*/
@Parameter
private List vmArgs;
/**
* Path to the executable. Should be either wsgen or wsimport
* but basically any script which will understand passed in arguments
* will work.
*
* @since 2.2.1
*/
@Parameter
private File executable;
/**
* The entry point to Aether, i.e. the component doing all the work.
*
* @since 2.3.1
*/
@Component
private RepositorySystem repoSystem;
/**
* The current repository/network configuration of Maven.
*
* @since 2.3.1
*/
@Parameter(defaultValue = "${repositorySystemSession}", readonly = true)
private RepositorySystemSession repoSession;
/**
* The project's remote repositories to use for the resolution of project
* dependencies.
*
* @since 2.3.1
*/
@Parameter(defaultValue = "${project.remoteProjectRepositories}", readonly = true)
private List projectRepos;
/**
* The project's remote repositories to use for the resolution of plugins
* and their dependencies.
*
* @since 2.3.1
*/
@Parameter(defaultValue = "${project.remotePluginRepositories}", readonly = true)
private List pluginRepos;
protected abstract String getMain ();
/**
* Either ${build.outputDirectory} or ${build.testOutputDirectory}.
*/
protected abstract File getDestDir ();
protected abstract File getSourceDestDir ();
protected void addSourceRoot (String sourceDir) {
if (!project.getCompileSourceRoots().contains(sourceDir)) {
getLog().debug("adding src root: " + sourceDir);
project.addCompileSourceRoot(sourceDir);
}
else {
getLog().debug("existing src root: " + sourceDir);
}
}
protected abstract File getDefaultSrcOut ();
protected abstract boolean getXnocompile ();
protected String getExtraClasspath () {
return null;
}
protected boolean isExtensionOn () {
return extension;
}
protected List getCommonArgs () throws MojoExecutionException {
List commonArgs = new ArrayList();
if (!isDefaultSrc(getSourceDestDir()) || keep) {
commonArgs.add("-keep");
commonArgs.add("-s");
commonArgs.add("'" + getSourceDestDir().getAbsolutePath() + "'");
if (!getSourceDestDir().mkdirs() && !getSourceDestDir().exists()) {
getLog().warn("Cannot create directory: " + getSourceDestDir().getAbsolutePath());
}
addSourceRoot(getSourceDestDir().getAbsolutePath());
}
File destDir = getDestDir();
if (!destDir.mkdirs() && !destDir.exists()) {
getLog().warn("Cannot create directory: " + destDir.getAbsolutePath());
}
commonArgs.add("-d");
commonArgs.add("'" + destDir.getAbsolutePath() + "'");
if (verbose) {
commonArgs.add("-verbose");
}
if (isArgSupported("-encoding")) {
if (encoding != null) {
maybeUnsupportedOption("-encoding", encoding, commonArgs);
}
else {
getLog().warn("Using platform encoding (" + System.getProperty("file.encoding") + "), build is platform dependent!");
}
}
if (isExtensionOn()) {
commonArgs.add("-extension");
}
if (getXnocompile()) {
commonArgs.add("-Xnocompile");
}
// add additional command line options
if (args != null) {
for (String arg : args) {
commonArgs.add(arg);
}
}
return commonArgs;
}
protected boolean isArgSupported (String arg) throws MojoExecutionException {
//try Metro first
Artifact a = pluginDescriptor.getArtifactMap().get("org.glassfish.metro:webservices-tools");
List supportedArgs = null;
String v = null;
try {
if (a != null) {
ArtifactVersion av = a.getSelectedVersion();
v = av.toString();
if (av.getMajorVersion() == 2 && av.getMinorVersion() == 2 && av.getIncrementalVersion() == 0) {
supportedArgs = METRO_22;
}
else if (av.getMajorVersion() == 2 && av.getMinorVersion() == 2 && av.getIncrementalVersion() >= 1) {
supportedArgs = METRO_221;
}
else { //if (av.getMajorVersion() >= 2 && av.getMinorVersion() >= 3) {
supportedArgs = METRO_23;
}
}
else {
//fallback to RI
a = pluginDescriptor.getArtifactMap().get("com.sun.xml.ws:jaxws-tools");
ArtifactVersion av = a.getSelectedVersion();
v = av.toString();
if (av.getMajorVersion() == 2 && av.getMinorVersion() == 2 && av.getIncrementalVersion() == 6) {
supportedArgs = METRO_22;
}
else if (av.getMajorVersion() == 2 && av.getMinorVersion() == 2 && av.getIncrementalVersion() == 7) {
supportedArgs = METRO_221;
}
else { //if (av.getMajorVersion() >= 2 && av.getMinorVersion() >= 2 && av.getIncrementalVersion() >= 8) {
supportedArgs = METRO_23;
}
}
}
catch (OverConstrainedVersionException ex) {
throw new MojoExecutionException(ex.getMessage(), ex);
}
boolean isSupported = supportedArgs.contains(arg);
if (!isSupported) {
getLog().warn("'" + arg + "' is not supported by " + a.getArtifactId() + ":" + v);
}
return isSupported;
}
private boolean isDefaultSrc (File srcout) {
return srcout.equals(getDefaultSrcOut());
}
protected void exec (List args) throws MojoExecutionException {
StreamConsumer sc = new DefaultConsumer();
try {
Commandline cmd = new Commandline();
if (executable != null) {
if (executable.isFile() && executable.canExecute()) {
cmd.setExecutable(executable.getAbsolutePath());
if (getExtraClasspath() != null) {
cmd.createArg().setLine("-cp");
cmd.createArg().setValue(getExtraClasspath());
}
}
else {
throw new MojoExecutionException("Cannot execute: " + executable.getAbsolutePath());
}
}
else {
cmd.setExecutable(new File(new File(System.getProperty("java.home"), "bin"), getJavaExec()).getAbsolutePath());
// add additional JVM options
if (vmArgs != null) {
for (String arg : vmArgs) {
cmd.createArg().setLine(arg);
}
}
String[] classpath = getCP();
cmd.createArg().setValue("-Xbootclasspath/p:" + classpath[0]);
cmd.createArg().setValue("-cp");
cmd.createArg().setValue(classpath[2]);
cmd.createArg().setLine("org.smallmind.web.jaxws.Invoker");
cmd.createArg().setLine(getMain());
String extraCp = getExtraClasspath();
String cp = extraCp != null ? extraCp + File.pathSeparator : "";
cp += classpath[1];
try {
File pathFile = createPathFile(cp);
cmd.createArg().setLine("-pathfile " + pathFile.getAbsolutePath());
}
catch (IOException ioe) {
//creation of temporary file can fail, in such case just put everything on cp
cmd.createArg().setValue("-cp");
cmd.createArg().setValue(cp);
}
}
cmd.setWorkingDirectory(project.getBasedir());
for (String arg : args) {
cmd.createArg().setLine(arg);
}
String fullCommand = cmd.toString();
if (isWindows() && 8191 <= fullCommand.length()) {
getLog().warn("Length of the command is limitted to 8191 characters but it has "
+ fullCommand.length() + " characters.");
getLog().warn(fullCommand);
}
else {
getLog().debug(fullCommand);
}
if (CommandLineUtils.executeCommandLine(cmd, sc, sc) != 0) {
throw new MojoExecutionException("Mojo failed - check output");
}
}
catch (DependencyResolutionException dre) {
throw new MojoExecutionException(dre.getMessage(), dre);
}
catch (CommandLineException t) {
throw new MojoExecutionException(t.getMessage(), t);
}
}
protected void maybeUnsupportedOption (String option, String value, List args) {
if (executable == null) {
args.add(option);
if (value != null) {
args.add(value);
}
}
else {
getLog().warn(option + " may not supported on older JDKs.\n"
+ "Use to bypass this warning if you really want to use it.");
}
}
protected String[] getExtraDependencies () {
return new String[0];
}
protected String[] getExtraArtifactIDs () {
return new String[0];
}
private String[] getCP () throws DependencyResolutionException {
Set endorsedCp = new HashSet();
Map cp = new HashMap();
Plugin p = pluginDescriptor.getPlugin();
boolean toolsFound = false;
for (Dependency d : p.getDependencies()) {
DependencyResult result = DependencyResolver.resolve(d,
new ExclusionFilter(d.getExclusions()),
pluginRepos, repoSystem, repoSession);
sortArtifacts(result, cp, endorsedCp);
if (containsTools(cp.keySet())) {
toolsFound = true;
}
}
for (String dep : getExtraDependencies()) {
DependencyResult result = DependencyResolver.resolve(
pluginDescriptor.getArtifactMap().get(dep),
toolsFound ? new DepFilter(getExtraArtifactIDs()) : null,
pluginRepos, repoSystem, repoSession);
sortArtifacts(result, cp, endorsedCp);
}
if (!containsTools(cp.keySet())) {
DependencyResult result = DependencyResolver.resolve(
pluginDescriptor.getArtifactMap().get("com.sun.xml.ws:jaxws-tools"),
null, pluginRepos, repoSystem, repoSession);
sortArtifacts(result, cp, endorsedCp);
}
StringBuilder sb = getCPasString(cp.values());
StringBuilder esb = getCPasString(endorsedCp);
//add custom invoker
String invokerPath = AbstractJaxwsMojo.class.getProtectionDomain().getCodeSource().getLocation().toExternalForm();
try {
invokerPath = new URI(invokerPath.substring(5)).getPath();
sb.append(invokerPath);
}
catch (URISyntaxException ex) {
throw new RuntimeException(ex);
}
sb.append(File.pathSeparator);
//don't forget tools.jar
File toolsJar = new File(System.getProperty("java.home"), "../lib/tools.jar");
if (!toolsJar.exists()) {
toolsJar = new File(System.getProperty("java.home"), "lib/tools.jar");
}
sb.append(toolsJar.getAbsolutePath());
sb.append(File.pathSeparator);
return new String[] {esb.substring(0, esb.length() - 1), sb.substring(0, sb.length() - 1), invokerPath};
}
private String getJavaExec () {
return isWindows() ? "java.exe" : "java";
}
private File createPathFile (String cp) throws IOException {
File f = File.createTempFile("jax-ws-mvn-plugin-cp", ".txt");
if (f.exists() && f.isFile()) {
if (!f.delete()) {
//this should not happen
getLog().warn("cannot remove obsolete classpath setting file: " + f.getAbsolutePath());
}
}
Properties p = new Properties();
p.put("cp", cp.replace(File.separatorChar, '/'));
getLog().debug("stored classpath: " + cp.replace(File.separatorChar, '/'));
FileOutputStream fos = null;
try {
fos = new FileOutputStream(f);
p.store(fos, null);
}
catch (IOException ex) {
logger.log(Level.SEVERE, null, ex);
}
finally {
if (fos != null) {
try {
fos.close();
}
catch (IOException ex) {
logger.log(Level.SEVERE, null, ex);
}
}
}
return f;
}
private boolean isWindows () {
return Os.isFamily(Os.FAMILY_WINDOWS);
}
private StringBuilder getCPasString (Collection artifacts) {
StringBuilder sb = new StringBuilder();
for (org.eclipse.aether.artifact.Artifact a : artifacts) {
sb.append(a.getFile().getAbsolutePath());
sb.append(File.pathSeparator);
}
return sb;
}
private void sortArtifacts (DependencyResult result, Map cp, Set endorsedCp) {
ClassPathNodeListGenerator nlg = new ClassPathNodeListGenerator();
result.getRoot().accept(nlg);
for (org.eclipse.aether.artifact.Artifact a : nlg.getArtifacts(false)) {
cp.put(a.getGroupId() + ":" + a.getArtifactId(), a);
}
nlg.setEndorsed(true);
endorsedCp.addAll(nlg.getArtifacts(false));
}
private boolean containsTools (Set cp) {
return cp.contains("com.sun.xml.ws:jaxws-tools")
|| cp.contains("org.glassfish.metro:webservices-tools")
|| cp.contains("com.oracle.weblogic:weblogic-server-pom");
}
private static class DepFilter implements DependencyFilter {
private final Set toExclude = new HashSet();
public DepFilter (String[] artifacts) {
if (artifacts != null) {
for (String a : artifacts) {
int i = a.indexOf(':');
toExclude.add(new Dep(a.substring(0, i), a.substring(i + 1)));
}
}
}
@Override
public boolean accept (DependencyNode node, List parents) {
org.eclipse.aether.artifact.Artifact a = node.getDependency().getArtifact();
return !toExclude.contains(new Dep(a.getGroupId(), a.getArtifactId()));
}
private static class Dep {
private final String groupId;
private final String artifactId;
public Dep (String groupId, String artifactId) {
this.groupId = groupId;
this.artifactId = artifactId;
}
@Override
public int hashCode () {
int hash = 5;
hash = 37 * hash + (this.groupId != null ? this.groupId.hashCode() : 0);
return hash;
}
@Override
public boolean equals (Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Dep other = (Dep)obj;
if ((this.groupId == null)
? (other.groupId != null)
: !this.groupId.equals(other.groupId)) {
return false;
}
//startsWith here is intentional
return !((this.artifactId == null)
? (other.artifactId != null)
: !this.artifactId.startsWith(other.artifactId));
}
}
}
}