blob: c24d9406905dda84a46ff2596473a10efddf98f8 [file] [log] [blame]
/*
* 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.
*/
package org.apache.axis2.jaxws.description.builder;
import org.apache.axis2.AxisFault;
import org.apache.axis2.Constants;
import org.apache.axis2.dataretrieval.SchemaSupplier;
import org.apache.axis2.dataretrieval.WSDLSupplier;
import org.apache.axis2.deployment.util.Utils;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.description.Parameter;
import org.apache.axis2.engine.AxisConfiguration;
import org.apache.axis2.java.security.AccessController;
import org.apache.axis2.jaxws.catalog.JAXWSCatalogManager;
import org.apache.axis2.jaxws.catalog.impl.OASISCatalogManager;
import org.apache.axis2.jaxws.description.EndpointDescription;
import org.apache.axis2.jaxws.util.CatalogURIResolver;
import org.apache.axis2.transport.http.HTTPConstants;
import org.apache.axis2.util.SchemaUtil;
import org.apache.axis2.wsdl.WSDLConstants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaCollection;
import org.xml.sax.InputSource;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.wsdl.Definition;
import javax.wsdl.WSDLException;
import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLReader;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.soap.SOAPBinding;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
/**
* This class will implement an interface that is defined by the
* MDQ code. It will be registered within the MDQ framework, and the
* MDQ code will call this when it finds an application that was
* deployed without WSDL. This class will use the WsGen tool to
* generate a WSDL Definition based on the Java source for the application.
*/
public class JAXWSRIWSDLGenerator implements SchemaSupplier, WSDLSupplier {
private static final Log log = LogFactory.getLog(JAXWSRIWSDLGenerator.class);
private String classPath;
private AxisService axisService;
private boolean init = false;
private HashMap<String, XmlSchema> docMap;
private HashMap<String, Definition> wsdlDefMap;
public JAXWSRIWSDLGenerator(AxisService axisService) {
this.axisService = axisService;
}
/**
* This method will drive the call to WsGen to generate a WSDL file for
* applications deployed without WSDL. We will then read this file in from
* disk and create a Definition. After we are done with the file we will
* remove it from disk.
*/
public void generateWsdl(String className, String bindingType) throws WebServiceException {
generateWsdl(className, bindingType, null);
}
/**
* This method will drive the call to WsGen to generate a WSDL file for
* applications deployed without WSDL. We will then read this file in from
* disk and create a Definition. After we are done with the file we will
* remove it from disk. This method accepts a CatalogManager as a parameter
* for the eventual use in by an XMLSchemaCollection.
*/
public void generateWsdl(String className, String bindingType, JAXWSCatalogManager catalogManager) throws
WebServiceException {
AxisConfiguration axisConfiguration = axisService.getAxisConfiguration();
File tempFile = (File) axisConfiguration.getParameterValue(
Constants.Configuration.ARTIFACTS_TEMP_DIR);
if (tempFile == null) {
tempFile = new File(getProperty_doPriv("java.io.tmpdir"), "_axis2");
}
Parameter servletConfigParam = axisConfiguration
.getParameter(HTTPConstants.HTTP_SERVLETCONFIG);
String webBase = null;
if (servletConfigParam != null) {
Object obj = servletConfigParam.getValue();
ServletContext servletContext;
if (obj instanceof ServletConfig) {
ServletConfig servletConfig = (ServletConfig) obj;
servletContext = servletConfig.getServletContext();
webBase = servletContext.getRealPath("/WEB-INF");
}
}
if(classPath == null) {
this.classPath = getDefaultClasspath(webBase);
}
if (log.isDebugEnabled()) {
log.debug("For implementation class " + className +
" WsGen classpath: " +
classPath);
}
String localOutputDirectory = tempFile.getAbsolutePath() + className;
if (log.isDebugEnabled()) {
log.debug("Output directory for generated WSDL file: " + localOutputDirectory);
}
boolean errorOnRead = false;
try {
if (log.isDebugEnabled()) {
log.debug("Generating new WSDL Definition");
}
createOutputDirectory(localOutputDirectory);
Class clazz;
try {
// Try the one in JDK16
clazz = Class.forName("com.sun.tools.internal.ws.spi.WSToolsObjectFactory");
} catch (Throwable t){
// Look for the RI
clazz = Class.forName("com.sun.tools.ws.spi.WSToolsObjectFactory");
}
Method m1 = clazz.getMethod("newInstance", new Class[]{});
Object factory = m1.invoke(new Object[]{});
String[] arguments = getWsGenArguments(className, bindingType, localOutputDirectory);
OutputStream os = new ByteArrayOutputStream();
Method m2 = clazz.getMethod("wsgen", new Class[]{OutputStream.class, String[].class});
m2.invoke(factory, os, arguments);
os.close();
wsdlDefMap = readInWSDL(localOutputDirectory);
if (wsdlDefMap.isEmpty()) {
throw new Exception("A WSDL Definition could not be generated for " +
"the implementation class: " + className);
}
docMap = readInSchema(localOutputDirectory, catalogManager);
}
catch (Throwable t) {
String msg =
"Error occurred generating WSDL file for Web service implementation class " +
"{" + className + "}: {" + t + "}";
log.error(msg, t);
throw new WebServiceException(msg, t);
}
}
/**
* This will set up the arguments that will be used by the WsGen tool.
*/
private String[] getWsGenArguments(String className, String bindingType, String localOutputDirectory) throws
WebServiceException {
String[] arguments = null;
if (bindingType == null || bindingType.equals("") || bindingType.equals(
SOAPBinding.SOAP11HTTP_BINDING) || bindingType.equals(
SOAPBinding.SOAP11HTTP_MTOM_BINDING)) {
if (log.isDebugEnabled()) {
log.debug("Generating WSDL with SOAP 1.1 binding type");
}
arguments = new String[]{"-cp", classPath, className, "-keep", "-wsdl:soap1.1", "-d",
localOutputDirectory};
} else if (bindingType.equals(SOAPBinding.SOAP12HTTP_BINDING) || bindingType.equals(
SOAPBinding.SOAP12HTTP_MTOM_BINDING)) {
if (log.isDebugEnabled()) {
log.debug("Generating WSDL with SOAP 1.2 binding type");
}
arguments = new String[]{"-cp", classPath, className, "-keep", "-extension",
"-wsdl:Xsoap1.2", "-d", localOutputDirectory};
} else {
throw new WebServiceException("The binding " + bindingType + " specified by the " +
"class " + className + " cannot be used to generate a WSDL. Please choose " +
"a supported binding type.");
}
return arguments;
}
/**
* This method will be used to create a Definition based on the
* WSDL file generated by WsGen.
*/
private HashMap<String, Definition> readInWSDL(String localOutputDirectory) throws Exception {
List<File> wsdlFiles = getWSDLFiles(localOutputDirectory);
HashMap<String, Definition> wsdlDefMap = new HashMap<String, Definition>();
for (File wsdlFile : wsdlFiles) {
if (wsdlFile != null) {
try {
WSDLFactory wsdlFactory = WSDLFactory.newInstance();
WSDLReader wsdlReader = wsdlFactory.newWSDLReader();
InputStream is = wsdlFile.toURL().openStream();
Definition definition = wsdlReader.readWSDL(localOutputDirectory,
new InputSource(is));
try {
definition.setDocumentBaseURI(wsdlFile.toURI().toString());
if (log.isDebugEnabled()) {
log.debug("Set base document URI for generated WSDL: " +
wsdlFile.toURI().toString());
}
}
catch (Throwable t) {
if (log.isDebugEnabled()) {
log.debug("Could not set base document URI for generated " +
"WSDL: " + wsdlFile.getAbsolutePath() + " : " +
t.toString());
}
}
wsdlDefMap.put(wsdlFile.getName().toLowerCase(), definition);
}
catch (WSDLException e) {
String msg = "Error occurred while attempting to create Definition from " +
"generated WSDL file {" + wsdlFile.getName() + "}: {" + e + "}";
log.error(msg);
throw new Exception(msg);
}
catch (IOException e) {
String msg = "Error occurred while attempting to create Definition from " +
"generated WSDL file {" + wsdlFile.getName() + "}: {" + e + "}";
log.error(msg);
throw new Exception(msg);
}
}
}
return wsdlDefMap;
}
/**
* This method will be used to locate the WSDL file that was
* generated by WsGen. There should be only one file with the
* ".wsdl" extension in this directory.
*/
private List<File> getWSDLFiles(String localOutputDirectory) {
File classDirectory = new File(localOutputDirectory);
ArrayList<File> wsdlFiles = new ArrayList<File>();
if (classDirectory.isDirectory()) {
File[] files = classDirectory.listFiles();
for (File file : files) {
String fileName = file.getName();
if (fileName.endsWith(".wsdl")) {
if (log.isDebugEnabled()) {
log.debug("Located generated WSDL file: " + fileName);
}
wsdlFiles.add(file);
}
}
}
return wsdlFiles;
}
/**
* This file will create the directory we will use as the output
* directory argument in our call to WsGen.
*/
private void createOutputDirectory(String localOutputDirectory) {
File directory = new File(localOutputDirectory);
if (!directory.isDirectory()) {
directory.mkdirs();
}
}
/**
* This method will read in all of the schema files that were generated
* for a given application.
*/
private HashMap<String, XmlSchema> readInSchema(String localOutputDirectory,
JAXWSCatalogManager catalogManager) throws Exception {
try {
XmlSchemaCollection schemaCollection = new XmlSchemaCollection();
if (catalogManager != null)
schemaCollection.setSchemaResolver(new CatalogURIResolver(catalogManager));
schemaCollection.setBaseUri(new File(localOutputDirectory).getAbsolutePath());
HashMap<String, XmlSchema> docMap = new HashMap<String, XmlSchema>();
List<File> schemaFiles = getSchemaFiles(localOutputDirectory);
for (File schemaFile : schemaFiles) {
XmlSchema doc = schemaCollection.read(new InputSource(schemaFile.toURL().toString()), null);
if (log.isDebugEnabled()) {
log.debug("Read in schema file: " + schemaFile.getName());
}
docMap.put(schemaFile.getName(), doc);
}
return docMap;
}
catch (Exception e) {
String msg =
"Error occurred while attempting to read generated schema file {" + e + "}";
log.error(msg);
throw new Exception(msg);
}
}
/**
* This method will return a list of file objects that represent all the
* schema files in the current directory.
*/
private List<File> getSchemaFiles(String localOutputDirectory) {
ArrayList<File> schemaFiles = new ArrayList<File>();
File classDirectory = new File(localOutputDirectory);
if (classDirectory.isDirectory()) {
File[] files = classDirectory.listFiles();
for (File file : files) {
String fileName = file.getName();
if (fileName.endsWith(".xsd")) {
if (log.isDebugEnabled()) {
log.debug("Located generated schema file: " + fileName);
}
schemaFiles.add(file);
}
}
}
return schemaFiles;
}
public Definition getWSDL(AxisService service) throws AxisFault {
Parameter wsdlParameter = service.getParameter(WSDLConstants.WSDL_4_J_DEFINITION);
if (wsdlParameter != null) {
Object value = wsdlParameter.getValue();
if (value != null) {
return (Definition) value;
}
}
initialize(service);
return wsdlDefMap.values().iterator().next();
}
private synchronized void initialize(AxisService service) {
String className = (String) axisService.getParameter(Constants.SERVICE_CLASS).getValue();
if (!init) {
generateWsdl(className, SOAPBinding.SOAP11HTTP_BINDING, getCatalogManager(service));
init = true;
}
}
public XmlSchema getSchema(AxisService service, String xsd) throws AxisFault {
Parameter wsdlParameter = service.getParameter(WSDLConstants.WSDL_4_J_DEFINITION);
if (wsdlParameter != null) {
ArrayList list = service.getSchema();
if (list.size() > 0) {
if (xsd == null || xsd.length() == 0) {
return (XmlSchema) list.get(0);
}
for (Iterator iterator = list.iterator(); iterator.hasNext();) {
XmlSchema schema = (XmlSchema) iterator.next();
XmlSchema[] schemas = SchemaUtil.getAllSchemas(schema);
for (int i = 0; i < schemas.length; i++) {
String uri = schemas[i].getSourceURI();
if (uri != null && uri.endsWith(xsd)) {
return schemas[i];
}
}
}
return (XmlSchema) list.get(0);
}
}
initialize(service);
XmlSchema schema = docMap.get(xsd);
if (schema == null) {
docMap.values().iterator().next();
}
return schema;
}
/**
* Expand a directory path or list of directory paths (File.pathSeparator
* delimited) into a list of file paths of all the jar files in those
* directories.
*
* @param dirPaths The string containing the directory path or list of
* directory paths.
* @return The file paths of the jar files in the directories. This is an
* empty string if no files were found, and is terminated by an
* additional pathSeparator in all other cases.
*/
public static String expandDirs(String dirPaths) {
StringTokenizer st = new StringTokenizer(dirPaths, File.pathSeparator);
StringBuffer buffer = new StringBuffer();
while (st.hasMoreTokens()) {
String d = st.nextToken();
File dir = new File(d);
if (dir.isDirectory()) {
File[] files = dir.listFiles(new JavaArchiveFilter());
for (int i = 0; i < files.length; i++) {
buffer.append(files[i]).append(File.pathSeparator);
}
}
}
return buffer.toString();
}
/**
* Check if this inputstream is a jar/zip
*
* @param is
* @return true if inputstream is a jar
*/
public static boolean isJar(InputStream is) {
try {
JarInputStream jis = new JarInputStream(is);
if (jis.getNextEntry() != null) {
return true;
}
} catch (IOException ioe) {
}
return false;
}
/**
* Get the CatalogManager associated with an AxisService
* @return the CatalogManager in use for this AxisService
*/
public static JAXWSCatalogManager getCatalogManager(AxisService service) {
Parameter param = service.getParameter(EndpointDescription.AXIS_SERVICE_PARAMETER);
if (param != null) {
EndpointDescription ed = (EndpointDescription)param.getValue();
return ed.getServiceDescription().getCatalogManager();
} else
return new OASISCatalogManager();
}
/**
* Get the default classpath from various thingies in the message context
*
* @param msgContext
* @return default classpath
*/
public String getDefaultClasspath(String webBase) {
HashSet classpath = new HashSet();
ClassLoader cl = Thread.currentThread().getContextClassLoader();
fillClassPath(cl, classpath);
// Just to be safe (the above doesn't seem to return the webapp
// classpath in all cases), manually do this:
if (webBase != null) {
addPath(classpath, webBase + File.separatorChar + "classes");
try {
String libBase = webBase + File.separatorChar + "lib";
File libDir = new File(libBase);
String[] jarFiles = libDir.list();
for (int i = 0; i < jarFiles.length; i++) {
String jarFile = jarFiles[i];
if (jarFile.endsWith(".jar")) {
addPath(classpath, libBase +
File.separatorChar +
jarFile);
}
}
} catch (Exception e) {
// Oh well. No big deal.
}
}
URL serviceArchive = axisService.getFileName();
if(serviceArchive != null) {
try {
classpath.add(Utils.toFile(serviceArchive).getCanonicalPath());
} catch (UnsupportedEncodingException e) {
log.error(e.getMessage(), e);
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
// axis.ext.dirs can be used in any appserver
getClassPathFromDirectoryProperty(classpath, "axis.ext.dirs");
// classpath used by Jasper
getClassPathFromProperty(classpath, "org.apache.catalina.jsp_classpath");
// websphere stuff.
getClassPathFromProperty(classpath, "ws.ext.dirs");
getClassPathFromProperty(classpath, "com.ibm.websphere.servlet.application.classpath");
// java class path
getClassPathFromProperty(classpath, "java.class.path");
// Load jars from java external directory
getClassPathFromDirectoryProperty(classpath, "java.ext.dirs");
// boot classpath isn't found in above search
getClassPathFromProperty(classpath, "sun.boot.class.path");
StringBuffer path = new StringBuffer();
for (Iterator iterator = classpath.iterator(); iterator.hasNext();) {
String s = (String) iterator.next();
path.append(s);
path.append(File.pathSeparatorChar);
}
log.debug(path);
return path.toString();
}
private static void addPath(HashSet classpath, String s) {
String path = s.replace(((File.separatorChar == '/') ? '\\' : '/'), File.separatorChar).trim();
File file = new File(path);
if (fileExists(file)) {
path = file.getAbsolutePath();
classpath.add(path);
}
}
/**
* Add all files in the specified directory to the classpath
*
* @param classpath
* @param property
*/
private static void getClassPathFromDirectoryProperty(HashSet classpath, String property) {
String dirs = getProperty_doPriv(property);
String path = null;
try {
path = expandDirs(dirs);
} catch (Exception e) {
// Oh well. No big deal.
}
if (path != null) {
addPath(classpath, path);
}
}
private static String getProperty_doPriv(final String property) {
return (String)
AccessController.doPrivileged(
new PrivilegedAction() {
public Object run() {
try {
return System.getProperty(property);
} catch (Throwable t) {
return null;
}
}
});
}
/**
* Add a classpath stored in a property.
*
* @param classpath
* @param property
*/
private static void getClassPathFromProperty(HashSet classpath, String property) {
String path = getProperty_doPriv(property);
if (path != null) {
addPath(classpath, path);
}
}
/**
* Walk the classloader hierarchy and add to the classpath
*
* @param cl
* @param classpath
*/
private static void fillClassPath(ClassLoader cl, HashSet classpath) {
while (cl != null) {
if (cl instanceof URLClassLoader) {
URL[] urls = ((URLClassLoader) cl).getURLs();
for (int i = 0; (urls != null) && i < urls.length; i++) {
String path = urls[i].getPath();
//If it is a drive letter, adjust accordingly.
if (path.length() >= 3 && path.charAt(0) == '/' && path.charAt(2) == ':')
path = path.substring(1);
addPath(classpath, URLDecoder.decode(path));
// if its a jar extract Class-Path entries from manifest
File file = new File(urls[i].getFile());
if (file.isFile()) {
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
if (isJar(fis)) {
JarFile jar = new JarFile(file);
Manifest manifest = jar.getManifest();
if (manifest != null) {
Attributes attributes = manifest.getMainAttributes();
if (attributes != null) {
String s = attributes.getValue(Attributes.Name.CLASS_PATH);
String base = file.getParent();
if (s != null) {
StringTokenizer st = new StringTokenizer(s, " ");
while (st.hasMoreTokens()) {
String t = st.nextToken();
addPath(classpath, base + File.separatorChar + t);
}
}
}
}
}
} catch (IOException ioe) {
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException ioe2) {
}
}
}
}
}
}
cl = cl.getParent();
}
}
/**
* Filter for zip/jar
*/
private static class JavaArchiveFilter implements FileFilter {
public boolean accept(File file) {
String name = file.getName().toLowerCase();
return (name.endsWith(".jar") || name.endsWith(".zip"));
}
}
static private Boolean fileExists (final File file) {
Boolean exists = (Boolean) AccessController.doPrivileged(
new PrivilegedAction() {
public Object run() {
return new Boolean(file.exists());
}
}
);
return exists;
}
}