/**
*
* Copyright 2003-2004 The Apache Software Foundation
*
* 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.apache.geronimo.axis.builder;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarFile;
import javax.wsdl.Definition;
import javax.wsdl.Operation;
import javax.wsdl.Port;
import javax.wsdl.extensions.soap.SOAPAddress;
import javax.xml.namespace.QName;
import javax.xml.rpc.handler.HandlerInfo;
import javax.xml.rpc.holders.BigDecimalHolder;
import javax.xml.rpc.holders.BigIntegerHolder;
import javax.xml.rpc.holders.BooleanHolder;
import javax.xml.rpc.holders.BooleanWrapperHolder;
import javax.xml.rpc.holders.ByteArrayHolder;
import javax.xml.rpc.holders.ByteHolder;
import javax.xml.rpc.holders.ByteWrapperHolder;
import javax.xml.rpc.holders.CalendarHolder;
import javax.xml.rpc.holders.DoubleHolder;
import javax.xml.rpc.holders.DoubleWrapperHolder;
import javax.xml.rpc.holders.FloatHolder;
import javax.xml.rpc.holders.FloatWrapperHolder;
import javax.xml.rpc.holders.IntHolder;
import javax.xml.rpc.holders.IntegerWrapperHolder;
import javax.xml.rpc.holders.LongHolder;
import javax.xml.rpc.holders.LongWrapperHolder;
import javax.xml.rpc.holders.ObjectHolder;
import javax.xml.rpc.holders.QNameHolder;
import javax.xml.rpc.holders.ShortHolder;
import javax.xml.rpc.holders.ShortWrapperHolder;
import javax.xml.rpc.holders.StringHolder;
import org.apache.geronimo.common.DeploymentException;
import org.apache.geronimo.kernel.ClassLoading;
import org.apache.geronimo.schema.SchemaConversionUtils;
import org.apache.geronimo.xbeans.j2ee.ExceptionMappingType;
import org.apache.geronimo.xbeans.j2ee.JavaWsdlMappingDocument;
import org.apache.geronimo.xbeans.j2ee.JavaWsdlMappingType;
import org.apache.geronimo.xbeans.j2ee.PackageMappingType;
import org.apache.geronimo.xbeans.j2ee.ParamValueType;
import org.apache.geronimo.xbeans.j2ee.PortComponentHandlerType;
import org.apache.geronimo.xbeans.j2ee.PortComponentType;
import org.apache.geronimo.xbeans.j2ee.ServiceEndpointInterfaceMappingType;
import org.apache.geronimo.xbeans.j2ee.ServiceEndpointMethodMappingType;
import org.apache.geronimo.xbeans.j2ee.ServiceImplBeanType;
import org.apache.geronimo.xbeans.j2ee.WebserviceDescriptionType;
import org.apache.geronimo.xbeans.j2ee.WebservicesDocument;
import org.apache.geronimo.xbeans.j2ee.WebservicesType;
import org.apache.geronimo.xbeans.j2ee.XsdQNameType;
import org.apache.xmlbeans.XmlException;
/**
* @version $Rev: $ $Date: $
*/
public class WSDescriptorParser {
public static JavaWsdlMappingType readJaxrpcMapping(JarFile moduleFile, URI jaxrpcMappingURI) throws DeploymentException {
String jaxrpcMappingPath = jaxrpcMappingURI.toString();
return readJaxrpcMapping(moduleFile, jaxrpcMappingPath);
}
public static JavaWsdlMappingType readJaxrpcMapping(JarFile moduleFile, String jaxrpcMappingPath) throws DeploymentException {
JavaWsdlMappingType mapping;
InputStream jaxrpcInputStream = null;
try {
jaxrpcInputStream = moduleFile.getInputStream(moduleFile.getEntry(jaxrpcMappingPath));
} catch (IOException e) {
throw new DeploymentException("Could not open stream to jaxrpc mapping document", e);
}
JavaWsdlMappingDocument mappingDocument = null;
try {
mappingDocument = JavaWsdlMappingDocument.Factory.parse(jaxrpcInputStream);
} catch (XmlException e) {
throw new DeploymentException("Could not parse jaxrpc mapping document", e);
} catch (IOException e) {
throw new DeploymentException("Could not read jaxrpc mapping document", e);
}
mapping = mappingDocument.getJavaWsdlMapping();
return mapping;
}
public static Map getExceptionMap(JavaWsdlMappingType mapping) {
Map exceptionMap = new HashMap();
if (mapping != null) {
ExceptionMappingType[] exceptionMappings = mapping.getExceptionMappingArray();
for (int i = 0; i < exceptionMappings.length; i++) {
ExceptionMappingType exceptionMapping = exceptionMappings[i];
QName exceptionMessageQName = exceptionMapping.getWsdlMessage().getQNameValue();
exceptionMap.put(exceptionMessageQName, exceptionMapping);
}
}
return exceptionMap;
}
public static String getPackageFromNamespace(String namespace, JavaWsdlMappingType mapping) throws DeploymentException {
PackageMappingType[] packageMappings = mapping.getPackageMappingArray();
for (int i = 0; i < packageMappings.length; i++) {
PackageMappingType packageMapping = packageMappings[i];
if (namespace.equals(packageMapping.getNamespaceURI().getStringValue().trim())) {
return packageMapping.getPackageType().getStringValue().trim();
}
}
throw new DeploymentException("Namespace " + namespace + " was not mapped in jaxrpc mapping file");
}
private static final Map rpcHolderClasses = new HashMap();
static {
rpcHolderClasses.put(BigDecimal.class, BigDecimalHolder.class);
rpcHolderClasses.put(BigInteger.class, BigIntegerHolder.class);
rpcHolderClasses.put(boolean.class, BooleanHolder.class);
rpcHolderClasses.put(Boolean.class, BooleanWrapperHolder.class);
rpcHolderClasses.put(byte[].class, ByteArrayHolder.class);
rpcHolderClasses.put(byte.class, ByteHolder.class);
rpcHolderClasses.put(Byte.class, ByteWrapperHolder.class);
rpcHolderClasses.put(Calendar.class, CalendarHolder.class);
rpcHolderClasses.put(double.class, DoubleHolder.class);
rpcHolderClasses.put(Double.class, DoubleWrapperHolder.class);
rpcHolderClasses.put(float.class, FloatHolder.class);
rpcHolderClasses.put(Float.class, FloatWrapperHolder.class);
rpcHolderClasses.put(int.class, IntHolder.class);
rpcHolderClasses.put(Integer.class, IntegerWrapperHolder.class);
rpcHolderClasses.put(long.class, LongHolder.class);
rpcHolderClasses.put(Long.class, LongWrapperHolder.class);
rpcHolderClasses.put(Object.class, ObjectHolder.class);
rpcHolderClasses.put(QName.class, QNameHolder.class);
rpcHolderClasses.put(short.class, ShortHolder.class);
rpcHolderClasses.put(Short.class, ShortWrapperHolder.class);
rpcHolderClasses.put(String.class, StringHolder.class);
}
public static Class getHolderType(String paramJavaTypeName, boolean isInOnly, QName typeQName, boolean isComplexType, JavaWsdlMappingType mapping, ClassLoader classLoader) throws DeploymentException {
Class paramJavaType = null;
if (isInOnly) {
//IN parameters just use their own type
try {
paramJavaType = ClassLoading.loadClass(paramJavaTypeName, classLoader);
} catch (ClassNotFoundException e) {
throw new DeploymentException("could not load parameter type", e);
}
return paramJavaType;
} else {
//INOUT and OUT parameters use holders. See jaxrpc spec 4.3.5
String holderName;
if (isComplexType) {
//complex types get mapped:
//package is determined from the namespace to package map + ".holders"
//class name is the complex type QNMAne local part + "Holder", with the initial character uppercased.
String namespace = typeQName.getNamespaceURI();
String packageName = WSDescriptorParser.getPackageFromNamespace(namespace, mapping);
StringBuffer buf = new StringBuffer(packageName.length() + typeQName.getLocalPart().length() + 14);
buf.append(packageName).append(".holders.").append(typeQName.getLocalPart()).append("Holder");
buf.setCharAt(packageName.length() + 9, Character.toUpperCase(typeQName.getLocalPart().charAt(0)));
holderName = buf.toString();
} else {
//see if it is in the primitive type and simple type mapping
try {
paramJavaType = ClassLoading.loadClass(paramJavaTypeName, classLoader);
} catch (ClassNotFoundException e) {
throw new DeploymentException("could not load parameter type", e);
}
Class holder = (Class) rpcHolderClasses.get(paramJavaType);
if (holder != null) {
try {
//TODO use class names in map or make sure we are in the correct classloader to start with.
holder = ClassLoading.loadClass(holder.getName(), classLoader);
} catch (ClassNotFoundException e) {
throw new DeploymentException("could not load holder type in correct classloader", e);
}
return holder;
}
//Otherwise, the holder must be in:
//package same as type's package + ".holders"
//class name same as type name + "Holder"
String paramTypeName = paramJavaType.getName();
StringBuffer buf = new StringBuffer(paramTypeName.length() + 14);
int dot = paramTypeName.lastIndexOf(".");
//foo.Bar >>> foo.holders.BarHolder
buf.append(paramTypeName.substring(0, dot)).append(".holders").append(paramTypeName.substring(dot)).append("Holder");
holderName = buf.toString();
}
try {
Class holder = ClassLoading.loadClass(holderName, classLoader);
return holder;
} catch (ClassNotFoundException e) {
throw new DeploymentException("Could not load holder class", e);
}
}
}
public static ServiceEndpointMethodMappingType getMethodMappingForOperation(String operationName, ServiceEndpointMethodMappingType[] methodMappings) throws DeploymentException {
for (int i = 0; i < methodMappings.length; i++) {
ServiceEndpointMethodMappingType methodMapping = methodMappings[i];
if (operationName.equals(methodMapping.getWsdlOperation().getStringValue())) {
return methodMapping;
}
}
// Build list of available operations for exception
StringBuffer availOps = new StringBuffer(128);
for (int i = 0; i < methodMappings.length; i++) {
if (i != 0) availOps.append(",");
availOps.append(methodMappings[i].getWsdlOperation().getStringValue());
}
throw new DeploymentException("No method found for operation named '" + operationName + "'. Available operations: " + availOps);
}
public static ServiceEndpointInterfaceMappingType getServiceEndpointInterfaceMapping(ServiceEndpointInterfaceMappingType[] endpointMappings, QName portTypeQName) throws DeploymentException {
for (int i = 0; i < endpointMappings.length; i++) {
ServiceEndpointInterfaceMappingType endpointMapping = endpointMappings[i];
QName testPortQName = endpointMapping.getWsdlPortType().getQNameValue();
if (portTypeQName.equals(testPortQName)) {
return endpointMapping;
}
}
throw new DeploymentException("Could not find service endpoint interface for port named " + portTypeQName);
}
public static javax.wsdl.Service getService(QName serviceQName, Definition definition) throws DeploymentException {
javax.wsdl.Service service;
if (serviceQName != null) {
service = definition.getService(serviceQName);
} else {
Map services = definition.getServices();
if (services.size() != 1) {
throw new DeploymentException("no serviceQName supplied, and there are " + services.size() + " services");
}
service = (javax.wsdl.Service) services.values().iterator().next();
}
if (service == null) {
throw new DeploymentException("No service wsdl for supplied service qname " + serviceQName);
}
return service;
}
public static Method getMethodForOperation(Class serviceEndpointInterface, Operation operation) throws DeploymentException {
Method[] methods = serviceEndpointInterface.getMethods();
String opName = operation.getName();
Method found = null;
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
if (method.getName().equals(opName)) {
if (found != null) {
throw new DeploymentException("Overloaded methods are not allowed in lightweight mappings");
}
found = method;
}
}
if (found == null) {
throw new DeploymentException("No method found for operation named " + opName);
}
return found;
}
/**
* Parses a webservice.xml file and returns a map PortInfo instances indexed by the
* corresponding ejb-link or servlet-link element .
*
* @param webservicesType
* @param moduleFile
* @param isEJB
* @param servletLocations
* @return
* @throws org.apache.geronimo.common.DeploymentException
*/
public static Map parseWebServiceDescriptor(WebservicesType webservicesType, JarFile moduleFile, boolean isEJB, Map servletLocations) throws DeploymentException {
Map portMap = new HashMap();
WebserviceDescriptionType[] webserviceDescriptions = webservicesType.getWebserviceDescriptionArray();
for (int i = 0; i < webserviceDescriptions.length; i++) {
WebserviceDescriptionType webserviceDescription = webserviceDescriptions[i];
URI wsdlURI = null;
try {
wsdlURI = new URI(webserviceDescription.getWsdlFile().getStringValue().trim());
} catch (URISyntaxException e) {
throw new DeploymentException("could not construct wsdl uri from " + webserviceDescription.getWsdlFile().getStringValue(), e);
}
URI jaxrpcMappingURI = null;
try {
jaxrpcMappingURI = new URI(webserviceDescription.getJaxrpcMappingFile().getStringValue().trim());
} catch (URISyntaxException e) {
throw new DeploymentException("Could not construct jaxrpc mapping uri from " + webserviceDescription.getJaxrpcMappingFile(), e);
}
SchemaInfoBuilder schemaInfoBuilder = new SchemaInfoBuilder(moduleFile, wsdlURI);
Map wsdlPortMap = schemaInfoBuilder.getPortMap();
JavaWsdlMappingType javaWsdlMapping = readJaxrpcMapping(moduleFile, jaxrpcMappingURI);
HashMap seiMappings = new HashMap();
ServiceEndpointInterfaceMappingType[] mappings = javaWsdlMapping.getServiceEndpointInterfaceMappingArray();
for (int j = 0; j < mappings.length; j++) {
ServiceEndpointInterfaceMappingType seiMapping = mappings[j];
seiMappings.put(seiMapping.getServiceEndpointInterface().getStringValue(), seiMapping);
}
PortComponentType[] portComponents = webserviceDescription.getPortComponentArray();
for (int j = 0; j < portComponents.length; j++) {
PortComponentType portComponent = portComponents[j];
String portComponentName = portComponent.getPortComponentName().getStringValue().trim();
QName portQName = portComponent.getWsdlPort().getQNameValue();
String seiInterfaceName = portComponent.getServiceEndpointInterface().getStringValue().trim();
ServiceImplBeanType serviceImplBeanType = portComponent.getServiceImplBean();
if (isEJB == serviceImplBeanType.isSetServletLink()) {
throw new DeploymentException("Wrong kind of web service described in web service descriptor: expected " + (isEJB ? "EJB" : "POJO(Servlet)"));
}
String linkName;
String servletLocation;
if (serviceImplBeanType.isSetServletLink()) {
linkName = serviceImplBeanType.getServletLink().getStringValue().trim();
servletLocation = (String) servletLocations.get(linkName);
if (servletLocation == null) {
throw new DeploymentException("No servlet mapping for port " + portQName);
}
schemaInfoBuilder.movePortLocation(portQName.getLocalPart(), servletLocation);
} else {
linkName = serviceImplBeanType.getEjbLink().getStringValue().trim();
servletLocation = (String) servletLocations.get(linkName);
servletLocation = schemaInfoBuilder.movePortLocation(portQName.getLocalPart(), servletLocation);
}
PortComponentHandlerType[] handlers = portComponent.getHandlerArray();
Port port = (Port) wsdlPortMap.get(portQName.getLocalPart());
if (port == null) {
throw new DeploymentException("No WSDL Port definition for port-component " + portComponentName);
}
ServiceEndpointInterfaceMappingType seiMapping = (ServiceEndpointInterfaceMappingType) seiMappings.get(seiInterfaceName);
String wsdlLocation = webserviceDescription.getWsdlFile().getStringValue().trim();
URI contextURI = null;
try {
contextURI = new URI(servletLocation);
} catch (URISyntaxException e) {
throw new DeploymentException("Could not construct URI for web service location", e);
}
PortInfo portInfo = new PortInfo(portComponentName, portQName, schemaInfoBuilder, javaWsdlMapping, seiInterfaceName, handlers, port, seiMapping, wsdlLocation, contextURI);
if (portMap.put(linkName, portInfo) != null) {
throw new DeploymentException("Ambiguous description of port associated with j2ee component " + linkName);
}
}
}
return portMap;
}
public static Map parseWebServiceDescriptor(URL wsDDUrl, JarFile moduleFile, boolean isEJB, Map servletLocations) throws DeploymentException {
try {
WebservicesDocument webservicesDocument = WebservicesDocument.Factory.parse(wsDDUrl);
SchemaConversionUtils.validateDD(webservicesDocument);
WebservicesType webservicesType = webservicesDocument.getWebservices();
return parseWebServiceDescriptor(webservicesType, moduleFile, isEJB, servletLocations);
} catch (XmlException e) {
throw new DeploymentException("Could not read descriptor document", e);
} catch (IOException e) {
return null;
}
}
public static List createHandlerInfoList(PortComponentHandlerType[] handlers, ClassLoader classLoader) throws DeploymentException {
List list = new ArrayList();
for (int i = 0; i < handlers.length; i++) {
PortComponentHandlerType handler = handlers[i];
// Get handler class
Class handlerClass = null;
String className = handler.getHandlerClass().getStringValue().trim();
try {
handlerClass = classLoader.loadClass(className);
} catch (ClassNotFoundException e) {
throw new DeploymentException("Unable to load handler class: " + className, e);
}
// config data for the handler
Map config = new HashMap();
ParamValueType[] paramValues = handler.getInitParamArray();
for (int j = 0; j < paramValues.length; j++) {
ParamValueType paramValue = paramValues[j];
String paramName = paramValue.getParamName().getStringValue().trim();
String paramStringValue = paramValue.getParamValue().getStringValue().trim();
config.put(paramName, paramStringValue);
}
// QName array of headers it processes
XsdQNameType[] soapHeaderQNames = handler.getSoapHeaderArray();
QName[] headers = new QName[soapHeaderQNames.length];
for (int j = 0; j < soapHeaderQNames.length; j++) {
XsdQNameType soapHeaderQName = soapHeaderQNames[j];
headers[j] = soapHeaderQName.getQNameValue();
}
list.add(new HandlerInfo(handlerClass, config, headers));
}
return list;
}
}