org.smallmind.web.jaxws.WsImportMojo Maven / Gradle / Ivy
/*
* Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 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.FileFilter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Formatter;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.settings.Proxy;
import org.apache.maven.settings.Settings;
abstract class WsImportMojo extends AbstractJaxwsMojo {
private static final String STALE_FILE_PREFIX = ".";
private static final String PATTERN = "[^\\s]+\\.wsdl$";
/**
* List of files to use for WSDLs. If not specified, all .wsdl
* files in the wsdlDirectory
will be used.
*/
@Parameter
protected List wsdlFiles;
/**
* Directory containing binding files.
*/
@Parameter(defaultValue = "${basedir}/src/jaxws")
protected File bindingDirectory;
/**
* List of files to use for bindings. If not specified, all .xml
* files in the bindingDirectory
will be used.
*/
@Parameter
protected List bindingFiles;
/**
* Specify the location of authorization file.
*/
@Parameter
protected File xauthFile;
/**
* The package in which the source files will be generated.
*/
@Parameter
private String packageName;
/**
* Catalog file to resolve external entity references support TR9401,
* XCatalog, and OASIS XML Catalog format.
*/
@Parameter
private File catalog;
/**
* Set HTTP/HTTPS proxy. Format is [user[:password]@]proxyHost[:proxyPort]
.
*/
@Parameter
private String httpproxy;
/**
* Directory containing WSDL files.
*/
@Parameter(defaultValue = "${basedir}/src/wsdl")
private File wsdlDirectory;
/**
* List of external WSDL URLs to be compiled.
*/
@Parameter
private List> wsdlUrls;
/**
* @WebService.wsdlLocation and @WebServiceClient.wsdlLocation value.
*
*
* Can end with asterisk in which case relative path of the WSDL will
* be appended to the given wsdlLocation
.
*
*
* Example:
*
* ...
* <configuration>
* <wsdlDirectory>src/mywsdls</wsdlDirectory>
* <wsdlFiles>
* <wsdlFile>a.wsdl</wsdlFile>
* <wsdlFile>b/b.wsdl</wsdlFile>
* <wsdlFile>${basedir}/src/mywsdls/c.wsdl</wsdlFile>
* </wsdlFiles>
* <wsdlLocation>http://example.com/mywebservices/*</wsdlLocation>
* </configuration>
* ...
*
* wsdlLocation for a.wsdl
will be http://example.com/mywebservices/a.wsdl
* wsdlLocation for b/b.wsdl
will be http://example.com/mywebservices/b/b.wsdl
* wsdlLocation for ${basedir}/src/mywsdls/c.wsdl
will be file://absolute/path/to/c.wsdl
*
*
*
* Note: External binding files cannot be used if asterisk notation is in place.
*
*/
@Parameter
private String wsdlLocation;
/**
* Generate code as per the given JAXWS specification version.
* Setting "2.0" will cause JAX-WS to generate artifacts
* that run with JAX-WS 2.0 runtime.
*/
@Parameter
private String target;
/**
* Suppress wsimport output.
*/
@Parameter(defaultValue = "false")
private boolean quiet;
/**
* Local portion of service name for generated JWS implementation.
* Implies genJWS=true
.
*
* Note: It is a QName string, formatted as: "{" + Namespace URI + "}" + local part
*/
@Parameter
private String implServiceName;
/**
* Local portion of port name for generated JWS implementation.
* Implies genJWS=true
.
*
* Note: It is a QName string, formatted as: "{" + Namespace URI + "}" + local part
*/
@Parameter
private String implPortName;
/**
* Generate stubbed JWS implementation file.
*/
@Parameter(defaultValue = "false")
private boolean genJWS;
/**
* Turn off compilation after code generation and let generated sources be
* compiled by maven during compilation phase; keep is turned on with this option.
*/
@Parameter(defaultValue = "true")
private boolean xnocompile;
/**
* Maps headers not bound to the request or response messages to Java method parameters.
*/
@Parameter(defaultValue = "false")
private boolean xadditionalHeaders;
/**
* Turn on debug message.
*/
@Parameter(defaultValue = "false")
private boolean xdebug;
/**
* Binding W3C EndpointReferenceType to Java. By default WsImport follows spec and does not bind
* EndpointReferenceType to Java and uses the spec provided {@link javax.xml.ws.wsaddressing.W3CEndpointReference}
*/
@Parameter(defaultValue = "false")
private boolean xnoAddressingDataBinding;
/**
* Disable the SSL Hostname verification while fetching WSDL(s).
*/
@Parameter(defaultValue = "false")
private boolean xdisableSSLHostnameVerification;
/**
*/
@Parameter(defaultValue = "false")
private boolean xuseBaseResourceAndURLToLoadWSDL;
/**
* Disable Authenticator used by JAX-WS RI, xauthfile
will be ignored if set.
*/
@Parameter(defaultValue = "false")
private boolean xdisableAuthenticator;
/**
* Specify optional XJC-specific parameters that should simply be passed to xjc
* using -B
option of WsImport command.
*
* Multiple elements can be specified, and each token must be placed in its own list.
*
*/
@Parameter
private List xjcArgs;
/**
* The folder containing flag files used to determine if the output is stale.
*/
@Parameter(defaultValue = "${project.build.directory}/jaxws/stale")
private File staleFile;
/**
*/
@Parameter(property = "settings", readonly = true)
private Settings settings;
/**
* @return proxy string as [user[:password]@]proxyHost[:proxyPort] or null
*/
static String getActiveHttpProxy (Settings s) {
String retVal = null;
for (Proxy p : s.getProxies()) {
if (p.isActive() && "http".equals(p.getProtocol())) {
StringBuilder sb = new StringBuilder();
String user = p.getUsername();
String pwd = p.getPassword();
if (user != null) {
sb.append(user);
if (pwd != null) {
sb.append(":");
sb.append(pwd);
}
sb.append("@");
}
sb.append(p.getHost());
sb.append(":");
sb.append(p.getPort());
retVal = sb.toString().trim();
break;
}
}
return retVal;
}
protected abstract File getImplDestDir ();
@Override
public void execute () throws MojoExecutionException {
try {
URL[] wsdls = getWSDLFiles();
if (wsdls.length == 0 && (wsdlUrls == null || wsdlUrls.isEmpty())) {
getLog().info("No WSDLs are found to process, Specify atleast one of the following parameters: wsdlFiles, wsdlDirectory or wsdlUrls.");
return;
}
this.processWsdlViaUrls();
this.processLocalWsdlFiles(wsdls);
}
catch (MojoExecutionException e) {
throw e;
}
catch (IOException e) {
throw new MojoExecutionException(e.getMessage(), e);
}
}
@Override
protected String getMain () {
return "com.sun.tools.ws.wscompile.WsimportTool";
}
@Override
protected boolean getXnocompile () {
return xnocompile;
}
/**
* @throws MojoExecutionException
* @throws IOException
*/
private void processLocalWsdlFiles (URL[] wsdls)
throws MojoExecutionException, IOException {
for (URL u : wsdls) {
String url = u.toExternalForm();
if (isOutputStale(url)) {
getLog().info("Processing: " + url);
String relPath = null;
if ("file".equals(u.getProtocol())) {
relPath = getRelativePath(new File(u.getPath()));
}
ArrayList args = getWsImportArgs(relPath);
args.add("\"" + url + "\"");
getLog().info("jaxws:wsimport args: " + args);
exec(args);
touchStaleFile(url);
}
else {
getLog().info("Ignoring: " + url);
}
//http://java.net/jira/browse/JAX_WS_COMMONS-95
addSourceRoot(getSourceDestDir().getAbsolutePath());
}
}
/**
* process external wsdl
*
* @throws MojoExecutionException
*/
private void processWsdlViaUrls ()
throws MojoExecutionException, IOException {
for (int i = 0; wsdlUrls != null && i < wsdlUrls.size(); i++) {
String wsdlUrl = wsdlUrls.get(i).toString();
if (isOutputStale(wsdlUrl)) {
getLog().info("Processing: " + wsdlUrl);
ArrayList args = getWsImportArgs(null);
args.add("\"" + wsdlUrl + "\"");
getLog().info("jaxws:wsimport args: " + args);
exec(args);
touchStaleFile(wsdlUrl);
}
//http://java.net/jira/browse/JAX_WS_COMMONS-95
addSourceRoot(getSourceDestDir().getAbsolutePath());
}
}
/**
* @return wsimport's command arguments
* @throws MojoExecutionException
*/
private ArrayList getWsImportArgs (String relativePath)
throws MojoExecutionException {
ArrayList args = new ArrayList();
args.addAll(getCommonArgs());
if (httpproxy != null) {
args.add("-httpproxy:" + httpproxy);
}
else if (settings != null) {
String proxyString = getActiveHttpProxy(settings);
if (proxyString != null) {
args.add("-httpproxy:" + proxyString);
}
}
if (packageName != null) {
args.add("-p");
args.add(packageName);
}
if (catalog != null) {
args.add("-catalog");
args.add("'" + catalog.getAbsolutePath() + "'");
}
if (wsdlLocation != null) {
if (relativePath != null) {
args.add("-wsdllocation");
args.add(wsdlLocation.replaceAll("\\*", relativePath));
}
else if (!wsdlLocation.contains("*")) {
args.add("-wsdllocation");
args.add(wsdlLocation);
}
}
if (target != null) {
args.add("-target");
args.add(target);
}
if (quiet) {
args.add("-quiet");
}
if ((genJWS || implServiceName != null || implPortName != null)
&& isArgSupported("-generateJWS")) {
args.add("-generateJWS");
if (implServiceName != null && isArgSupported("-implServiceName")) {
args.add("-implServiceName");
args.add(implServiceName);
}
if (implPortName != null && isArgSupported("-implPortName")) {
args.add("-implPortName");
args.add(implPortName);
}
File implDestDir = getImplDestDir();
if (!implDestDir.mkdirs() && !implDestDir.exists()) {
getLog().warn("Cannot create directory: " + implDestDir.getAbsolutePath());
}
args.add("-implDestDir");
args.add("'" + implDestDir.getAbsolutePath() + "'");
if (!project.getCompileSourceRoots().contains(implDestDir.getAbsolutePath())) {
project.addCompileSourceRoot(implDestDir.getAbsolutePath());
}
}
if (xdebug) {
args.add("-Xdebug");
}
/**
* -Xno-addressing-databinding enable binding of W3C EndpointReferenceType to Java
*/
if (xnoAddressingDataBinding) {
args.add("-Xno-addressing-databinding");
}
if (xadditionalHeaders) {
args.add("-XadditionalHeaders");
}
if (xauthFile != null) {
args.add("-Xauthfile");
args.add(xauthFile.getAbsolutePath());
}
if (xdisableSSLHostnameVerification) {
args.add("-XdisableSSLHostnameVerification");
}
if (xuseBaseResourceAndURLToLoadWSDL) {
args.add("-XuseBaseResourceAndURLToLoadWSDL");
}
if (xdisableAuthenticator && isArgSupported("-XdisableAuthenticator")) {
args.add("-XdisableAuthenticator");
}
// xjcOIptions
if (xjcArgs != null) {
for (String xjcArg : xjcArgs) {
if (xjcArg.startsWith("-"))
args.add("-B" + xjcArg);
else
args.add(xjcArg);
}
}
// Bindings
File[] bindings = getBindingFiles();
if (bindings.length > 0 && wsdlLocation != null && wsdlLocation.contains("*")) {
throw new MojoExecutionException("External binding file(s) can not be bound to more WSDL files (" + wsdlLocation + ")\n"
+ "Please use either inline binding(s) or multiple execution tags.");
}
for (File binding : bindings) {
args.add("-b");
args.add("'" + binding.getAbsolutePath() + "'");
}
getLog().debug("jaxws:wsimport args: " + args);
return args;
}
/**
* Returns a file array of xml files to translate to object models.
*
* @return An array of schema files to be parsed by the schema compiler.
*/
public final File[] getBindingFiles () {
File[] bindings;
if (bindingFiles != null) {
bindings = new File[bindingFiles.size()];
for (int i = 0; i < bindingFiles.size(); ++i) {
String schemaName = bindingFiles.get(i);
File file = new File(schemaName);
if (!file.isAbsolute()) {
file = new File(bindingDirectory, schemaName);
}
bindings[i] = file;
}
}
else {
getLog().debug("The binding Directory is " + bindingDirectory);
bindings = bindingDirectory.listFiles(new XMLFile());
if (bindings == null) {
bindings = new File[0];
}
}
return bindings;
}
/**
* Returns a file array of wsdl files to translate to object models.
*
* @return An array of schema files to be parsed by the schema compiler.
*/
private URL[] getWSDLFiles () throws MojoExecutionException {
List files = new ArrayList();
@SuppressWarnings("unchecked")
Set dependencyArtifacts = project.getDependencyArtifacts();
List urlCpath = new ArrayList(dependencyArtifacts.size());
for (Artifact a : dependencyArtifacts) {
try {
if (a.getFile() != null) {
@SuppressWarnings("deprecation")
URL u = new File(a.getFile().toURI()).toURL();
urlCpath.add(u);
}
else {
getLog().warn("cannot find file for " + a.getGroupId() + ":" + a.getArtifactId());
}
}
catch (MalformedURLException ex) {
Logger.getLogger(WsImportMojo.class.getName()).log(Level.SEVERE, null, ex);
}
}
ClassLoader loader = urlCpath.isEmpty()
? Thread.currentThread().getContextClassLoader()
: new URLClassLoader(urlCpath.toArray(new URL[urlCpath.size()]));
if (wsdlFiles != null) {
for (String wsdlFileName : wsdlFiles) {
File wsdl = new File(wsdlFileName);
URL toAdd = null;
if (!wsdl.isAbsolute()) {
wsdl = new File(wsdlDirectory, wsdlFileName);
}
if (!wsdl.exists()) {
toAdd = loader.getResource(wsdlFileName);
}
else {
try {
toAdd = wsdl.toURI().toURL();
}
catch (MalformedURLException ex) {
Logger.getLogger(WsImportMojo.class.getName()).log(Level.SEVERE, null, ex);
}
}
getLog().debug("The wsdl File is '" + wsdlFileName + "' from '" + toAdd + "'");
if (toAdd != null) {
files.add(toAdd);
}
else {
throw new MojoExecutionException("'" + wsdlFileName + "' not found.");
}
}
}
else {
getLog().debug("The wsdl Directory is " + wsdlDirectory);
if (wsdlDirectory.exists()) {
File[] wsdls = wsdlDirectory.listFiles(new WSDLFile());
for (File wsdl : wsdls) {
try {
files.add(wsdl.toURI().toURL());
}
catch (MalformedURLException ex) {
Logger.getLogger(WsImportMojo.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
else {
URI rel = project.getBasedir().toURI().relativize(wsdlDirectory.toURI());
String dir = rel.getPath();
URL u = loader.getResource(dir);
if (u == null) {
dir = "WEB-INF/wsdl/";
u = loader.getResource(dir);
}
if (u == null) {
dir = "META-INF/wsdl/";
u = loader.getResource(dir);
}
if (!(u == null || !"jar".equalsIgnoreCase(u.getProtocol()))) {
String path = u.getPath();
try {
Pattern p = Pattern.compile(dir.replace(File.separatorChar, '/') + PATTERN, Pattern.CASE_INSENSITIVE);
Enumeration jes = new JarFile(path.substring(5, path.indexOf("!/"))).entries();
while (jes.hasMoreElements()) {
JarEntry je = jes.nextElement();
Matcher m = p.matcher(je.getName());
if (m.matches()) {
String s = "jar:" + path.substring(0, path.indexOf("!/") + 2) + je.getName();
files.add(new URL(s));
}
}
}
catch (IOException ex) {
Logger.getLogger(WsImportMojo.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
return files.toArray(new URL[files.size()]);
}
private String getRelativePath (File f) {
if (wsdlFiles != null) {
for (String s : wsdlFiles) {
String path = f.getPath().replace(File.separatorChar, '/');
if (path.endsWith(s) && path.length() != s.length()) {
return s;
}
}
}
return null;
}
/**
* Returns true if given WSDL resource or any binding file is newer
* than the staleFlag
file.
*
* @return True if wsdl files have been modified since the last build.
*/
private boolean isOutputStale (String resource) {
File[] sourceBindings = getBindingFiles();
File stFile = new File(staleFile, STALE_FILE_PREFIX + getHash(resource));
boolean stale = !stFile.exists();
if (!stale) {
getLog().debug("Stale flag file exists, comparing to wsdls and bindings.");
long staleMod = stFile.lastModified();
try {
//resource can be URL
URL sourceWsdl = new URL(resource);
if (sourceWsdl.openConnection().getLastModified() > staleMod) {
getLog().debug(resource + " is newer than the stale flag file.");
stale = true;
}
}
catch (MalformedURLException mue) {
//or a file
File sourceWsdl = new File(resource);
if (sourceWsdl.lastModified() > staleMod) {
getLog().debug(resource + " is newer than the stale flag file.");
stale = true;
}
}
catch (IOException ioe) {
//possible error while openning connection
getLog().error(ioe);
}
for (File sourceBinding : sourceBindings) {
if (sourceBinding.lastModified() > staleMod) {
getLog().debug(sourceBinding.getName() + " is newer than the stale flag file.");
stale = true;
}
}
}
return stale;
}
private void touchStaleFile (String resource) throws IOException {
File stFile = new File(staleFile, STALE_FILE_PREFIX + getHash(resource));
if (!stFile.exists()) {
File staleDir = stFile.getParentFile();
if (!staleDir.mkdirs() && !staleDir.exists()) {
getLog().warn("Cannot create directory: " + staleDir.getAbsolutePath());
}
if (!stFile.createNewFile()) {
getLog().warn("Cannot create file: " + stFile.getAbsolutePath());
}
getLog().debug("Stale flag file created.[" + stFile.getAbsolutePath() + "]");
}
else {
if (!stFile.setLastModified(System.currentTimeMillis())) {
getLog().warn("Stale file has not been updated!");
}
}
}
private String getHash (String s) {
try {
MessageDigest md = MessageDigest.getInstance("SHA");
Formatter formatter = new Formatter();
for (byte b : md.digest(s.getBytes("UTF-8"))) {
formatter.format("%02x", b);
}
return formatter.toString();
}
catch (UnsupportedEncodingException ex) {
getLog().debug(ex.getMessage(), ex);
}
catch (NoSuchAlgorithmException ex) {
getLog().debug(ex.getMessage(), ex);
}
//fallback to some default
getLog().warn("Could not compute hash for " + s + ". Using fallback method.");
return s.substring(s.lastIndexOf('/')).replaceAll("\\.", "-");
}
/**
* A class used to look up .xml documents from a given directory.
*/
private static final class XMLFile implements FileFilter {
/**
* Returns true if the file ends with an xml extension.
*
* @param file The filed being reviewed by the filter.
* @return true if an xml file.
*/
@Override
public boolean accept (final java.io.File file) {
return file.getName().endsWith(".xml");
}
}
/**
* A class used to look up .wsdl documents from a given directory.
*/
private static final class WSDLFile implements FileFilter {
/**
* Returns true if the file ends with a wsdl extension.
*
* @param file The filed being reviewed by the filter.
* @return true if an wsdl file.
*/
@Override
public boolean accept (final java.io.File file) {
return file.getName().endsWith(".wsdl");
}
}
}