blob: 719ab7c02fd16c40f22b312e73cd3c4e85709da4 [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.impl;
import org.apache.axis2.AxisFault;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.description.Parameter;
import org.apache.axis2.jaxws.ExceptionFactory;
import org.apache.axis2.jaxws.description.AttachmentDescription;
import org.apache.axis2.jaxws.description.AttachmentType;
import org.apache.axis2.jaxws.description.EndpointDescription;
import org.apache.axis2.jaxws.description.EndpointDescriptionWSDL;
import org.apache.axis2.jaxws.description.builder.DescriptionBuilderComposite;
import static org.apache.axis2.jaxws.description.builder.MDQConstants.CONSTRUCTOR_METHOD;
import org.apache.axis2.jaxws.description.builder.MDQConstants;
import org.apache.axis2.jaxws.description.builder.MethodDescriptionComposite;
import org.apache.axis2.jaxws.description.builder.WebMethodAnnot;
import org.apache.axis2.jaxws.description.xml.handler.HandlerChainsType;
import org.apache.axis2.jaxws.i18n.Messages;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.wsdl.Binding;
import javax.wsdl.BindingOperation;
import javax.wsdl.Operation;
import javax.wsdl.extensions.mime.MIMEContent;
import javax.wsdl.extensions.mime.MIMEMultipartRelated;
import javax.wsdl.extensions.mime.MIMEPart;
import javax.wsdl.extensions.soap.SOAPBody;
import javax.wsdl.extensions.soap.SOAPHeader;
import javax.wsdl.extensions.soap12.SOAP12Body;
import javax.wsdl.extensions.soap12.SOAP12Header;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Unmarshaller;
import javax.xml.namespace.QName;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.soap.SOAPBinding;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
/** Utilities used throughout the Description package. */
public class DescriptionUtils {
private static final Log log = LogFactory.getLog(DescriptionUtils.class);
static boolean isEmpty(String string) {
return (string == null || "".equals(string));
}
static boolean isEmpty(QName qname) {
return qname == null || isEmpty(qname.getLocalPart());
}
/** @return Returns TRUE if we find just one WebMethod Annotation with exclude flag set to false */
static boolean falseExclusionsExist(DescriptionBuilderComposite dbc) {
MethodDescriptionComposite mdc = null;
Iterator<MethodDescriptionComposite> iter = dbc.getMethodDescriptionsList().iterator();
while (iter.hasNext()) {
mdc = iter.next();
WebMethodAnnot wma = mdc.getWebMethodAnnot();
if (wma != null) {
if (wma.exclude() == false)
return true;
}
}
return false;
}
/**
* Gathers all MethodDescriptionCompsite's that contain a WebMethod Annotation with the exclude
* set to FALSE
*
* @return Returns List<MethodDescriptionComposite>
*/
static ArrayList<MethodDescriptionComposite> getMethodsWithFalseExclusions(
DescriptionBuilderComposite dbc) {
ArrayList<MethodDescriptionComposite> mdcList = new ArrayList<MethodDescriptionComposite>();
Iterator<MethodDescriptionComposite> iter = dbc.getMethodDescriptionsList().iterator();
if (DescriptionUtils.falseExclusionsExist(dbc)) {
while (iter.hasNext()) {
MethodDescriptionComposite mdc = iter.next();
if (mdc.getWebMethodAnnot() != null) {
if (mdc.getWebMethodAnnot().exclude() == false) {
mdc.setDeclaringClass(dbc.getClassName());
mdcList.add(mdc);
}
}
}
}
return mdcList;
}
/*
* Check whether a MethodDescriptionComposite contains a WebMethod annotation with
* exlude set to true
*/
static boolean isExcludeTrue(MethodDescriptionComposite mdc) {
if (mdc.getWebMethodAnnot() != null) {
if (mdc.getWebMethodAnnot().exclude() == true) {
return true;
}
}
return false;
}
static String javifyClassName(String className) {
if (className.indexOf("/") != -1) {
return className.replaceAll("/", ".");
}
return className;
}
/**
* Return the name of the class without any package qualifier.
*
* @param theClass
* @return the name of the class sans package qualification.
*/
static String getSimpleJavaClassName(String name) {
String returnName = null;
if (name != null) {
String fqName = name;
// We need the "simple name", so strip off any package information from the name
int endOfPackageIndex = fqName.lastIndexOf('.');
int startOfClassIndex = endOfPackageIndex + 1;
returnName = fqName.substring(startOfClassIndex);
}
return returnName;
}
/**
* Returns the package name from the class. If no package, then returns null
*
* @param theClassName
* @return
*/
static String getJavaPackageName(String theClassName) {
String returnPackage = null;
if (theClassName != null) {
String fqName = theClassName;
// Get the package name, if there is one
int endOfPackageIndex = fqName.lastIndexOf('.');
if (endOfPackageIndex >= 0) {
returnPackage = fqName.substring(0, endOfPackageIndex);
}
}
return returnPackage;
}
/**
* Create a JAX-WS namespace based on the package name
*
* @param packageName
* @param protocol
* @return
*/
static final String NO_PACKAGE_HOST_NAME = "DefaultNamespace";
static String makeNamespaceFromPackageName(String packageName, String protocol) {
if (DescriptionUtils.isEmpty(protocol)) {
protocol = "http";
}
if (DescriptionUtils.isEmpty(packageName)) {
return protocol + "://" + NO_PACKAGE_HOST_NAME;
}
StringTokenizer st = new StringTokenizer(packageName, ".");
String[] words = new String[ st.countTokens() ];
for (int i = 0; i < words.length; ++i)
words[i] = st.nextToken();
StringBuffer sb = new StringBuffer(80);
for (int i = words.length - 1; i >= 0; --i) {
String word = words[i];
// seperate with dot
if (i != words.length - 1)
sb.append('.');
sb.append(word);
}
return protocol + "://" + sb.toString() + "/";
}
/**
* Determines whether a method should have an OperationDescription created for it based on the
* name. This is a convenience method to allow us to exlude methods such as constructors.
*
* @param methodName
* @return
*/
static boolean createOperationDescription(String methodName) {
if (methodName.equals(CONSTRUCTOR_METHOD)) {
return false;
}
return true;
}
/**
* This is a helper method that will open a stream to an @HandlerChain configuration file.
*
* @param configFile - The path to the file
* @param className - The class in which the annotation was declared. This is used in case the
* file path is relative.
* @param classLoader - ClassLoader used to load relative file paths.
* @return
*/
public static InputStream openHandlerConfigStream(String configFile, String className,
ClassLoader
classLoader) {
InputStream configStream = null;
URL configURL;
if (log.isDebugEnabled()) {
log.debug("Attempting to load @HandlerChain configuration file: " + configFile +
" relative to class: " + className);
}
try {
configURL = new URL(configFile);
if (configURL != null) {
if (log.isDebugEnabled()) {
log.debug("Found absolute @HandlerChain configuration file: " + configFile);
}
configStream = configURL.openStream();
}
}
catch (MalformedURLException e) {
// try another method to obtain a stream to the configuration file
}
catch (IOException e) {
// report this since it was a valid URL but the openStream caused a problem
throw ExceptionFactory.makeWebServiceException(Messages.getMessage("hcConfigLoadFail",
configFile, className,
e.toString()));
}
if (configStream == null) {
if (log.isDebugEnabled()) {
log.debug("@HandlerChain.file attribute refers to a relative location: "
+ configFile);
}
className = className.replace(".", "/");
try {
if (log.isDebugEnabled()) {
log.debug("Resolving @HandlerChain configuration file: " + configFile +
" relative to class file: " + className);
}
URI uri = new URI(className);
uri = uri.resolve(configFile);
String resolvedPath = uri.toString();
if (log.isDebugEnabled()) {
log.debug("@HandlerChain.file resolved file path location: " + resolvedPath);
}
configStream = getInputStream(resolvedPath, classLoader);
}
catch (URISyntaxException e) {
throw ExceptionFactory.makeWebServiceException(Messages.getMessage("hcConfigLoadFail",
configFile, className,
e.toString()));
}
}
if (configStream == null) {
//throw ExceptionFactory.makeWebServiceException(Messages.getMessage("handlerChainNS",
// configFile, className));
// No longer throwing an exception here. This method is best-effort, and the caller may
// change the class to which the path is relative and try again. The caller is responsible
// for determining when to "give up"
if (log.isDebugEnabled()) {
log.debug("@HandlerChain configuration fail: " + configFile + " in class: " + className + " failed to load.");
}
} else {
if (log.isDebugEnabled()) {
log.debug("@HandlerChain configuration file: " + configFile + " in class: " +
className + " was successfully loaded.");
}
}
return configStream;
}
private static InputStream getInputStream(String path, ClassLoader classLoader) {
InputStream configStream = classLoader.getResourceAsStream(path);
if (configStream == null) {
// try another classloader
ClassLoader cl = System.class.getClassLoader();
if (cl != null) {
configStream = cl.getResourceAsStream(path);
}
}
if (configStream == null) {
// and another classloader
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl != null) {
configStream = cl.getResourceAsStream(path);
}
}
return configStream;
}
/**
* Determine is this method is an async method
* @param method - The method to examine
* @return
*/
public static boolean isAsync(Method method) {
if (method == null) {
return false;
}
String methodName = method.getName();
Class returnType = method.getReturnType();
if (methodName.endsWith("Async")
&& (returnType.isAssignableFrom(javax.xml.ws.Response.class) || returnType
.isAssignableFrom(java.util.concurrent.Future.class))) {
return true;
} else {
return false;
}
}
public static HandlerChainsType loadHandlerChains(InputStream is, ClassLoader classLoader) {
try {
HandlerChainsParser parser = new HandlerChainsParser();
return parser.loadHandlerChains(is);
} catch (Exception e) {
throw ExceptionFactory
.makeWebServiceException(Messages.getMessage("loadHandlerChainErr", e.getMessage()));
}
}
/**
* This method will loop through a list of extensibility elements looking for one
* of four objects: SOAPBody, SOAP12Body, SOAPHeader, SOAP12Header. If any of these
* objects are found the namespace URI from this object will be returned.
*/
public static String getNamespaceFromSOAPElement(List extElements) {
Iterator extIter = extElements.iterator();
while (extIter.hasNext()) {
Object extObj = extIter.next();
if (extObj instanceof SOAPBody) {
if (log.isDebugEnabled()) {
log.debug("Returning SOAPBody namespace: "
+ ((SOAPBody) extObj).getNamespaceURI());
}
return ((SOAPBody) extObj).getNamespaceURI();
} else if (extObj instanceof SOAP12Body) {
if (log.isDebugEnabled()) {
log.debug("Returning SOAP12Body namespace: "
+ ((SOAP12Body) extObj).getNamespaceURI());
}
return ((SOAP12Body) extObj).getNamespaceURI();
} else if (extObj instanceof SOAPHeader) {
if (log.isDebugEnabled()) {
log.debug("Returning SOAPHeader namespace: "
+ ((SOAPHeader) extObj).getNamespaceURI());
}
return ((SOAPHeader) extObj).getNamespaceURI();
} else if (extObj instanceof SOAP12Header) {
if (log.isDebugEnabled()) {
log.debug("Returning SOAP12Header namespace: "
+ ((SOAP12Header) extObj).getNamespaceURI());
}
return ((SOAP12Header) extObj).getNamespaceURI();
}
else if (extObj instanceof MIMEMultipartRelated) {
if (log.isDebugEnabled()) {
log.debug("Found a MIMEMultipartRelated element. Unwrapping to get SOAP binding.");
}
MIMEMultipartRelated mime = (MIMEMultipartRelated) extObj;
List mimeParts = mime.getMIMEParts();
Iterator itr = mimeParts.iterator();
while (itr.hasNext()) {
MIMEPart mimePart = (MIMEPart) itr.next();
List elements = mimePart.getExtensibilityElements();
String ns = getNamespaceFromSOAPElement(elements);
return ns;
}
}
}
return null;
}
/**
* This method will process a WSDL Binding and build AttachmentDescription objects if the
* WSDL dicatates attachments.
*/
public static void getAttachmentFromBinding(OperationDescriptionImpl opDesc, Binding binding) {
if (binding != null) {
Iterator bindingOpIter = binding.getBindingOperations().iterator();
while (bindingOpIter.hasNext()) {
BindingOperation bindingOp = (BindingOperation) bindingOpIter.next();
// found the BindingOperation that matches the current OperationDescription
if (bindingOp.getName().equals(opDesc.getName().getLocalPart())) {
if (bindingOp.getBindingInput() != null) {
if (log.isDebugEnabled()) {
log.debug("Processing binding opertion input");
}
processBindingForMIME(bindingOp.getBindingInput().getExtensibilityElements(),
opDesc, bindingOp.getOperation(), true);
}
if (bindingOp.getBindingOutput() != null) {
if (log.isDebugEnabled()) {
log.debug("Processing binding output");
}
processBindingForMIME(bindingOp.getBindingOutput().getExtensibilityElements(),
opDesc, bindingOp.getOperation(), false);
}
}
}
}
}
/**
* This method will loop through the extensibility elements for a given BindingInput or
* BindingOutput element and determine if it has any MIMEMultipartRelated content. If it
* does it will build up the appropriate AttachmentDescription objects.
*/
private static void processBindingForMIME(List extensibilityElements,
OperationDescriptionImpl opDesc,
Operation operation,
boolean isRequest) {
Iterator extensibilityIter = extensibilityElements.iterator();
while (extensibilityIter.hasNext()) {
Object obj = extensibilityIter.next();
if (obj instanceof MIMEMultipartRelated) {
if (log.isDebugEnabled()) {
log.debug("Found a mime:multipartRelated extensiblity element.");
}
// Found mime information now process it and determine if we need to
// create an AttachmentDescription
MIMEMultipartRelated mime = (MIMEMultipartRelated) obj;
Iterator partIter = mime.getMIMEParts().iterator();
while (partIter.hasNext()) {
if (log.isDebugEnabled()) {
log.debug("Found a mime:part child element.");
}
MIMEPart mimePart = (MIMEPart) partIter.next();
Iterator mExtIter = mimePart.getExtensibilityElements().iterator();
// Process each mime part to determine if there is mime content
while (mExtIter.hasNext()) {
Object obj2 = mExtIter.next();
// For mime content we need to potentially create an AttachmentDescription
if (obj2 instanceof MIMEContent) {
MIMEContent mimeContent = (MIMEContent) obj2;
String part = mimeContent.getPart();
String type = mimeContent.getType();
// if we have not already processed this part for the operation
if (opDesc.getPartAttachmentDescription(part) == null) {
if (log.isDebugEnabled()) {
log.debug("Adding new AttachmentDescription for part: " + part
+ " on operation: " + opDesc.getOperationName());
}
AttachmentDescription attachmentDesc =
new AttachmentDescriptionImpl(AttachmentType.SWA,
new String[] { type });
opDesc.addPartAttachmentDescription(part, attachmentDesc);
} else {
if (log.isDebugEnabled()) {
log.debug("Already created AttachmentDescription for part: "
+ part + " of type: " + type);
}
}
}
else if (obj2 instanceof SOAPBody || obj2 instanceof SOAP12Body) {
if (log.isDebugEnabled()) {
log.debug("Found a body element with potential nested mime content");
}
// Flag whether there's a potential nested attachment.
if (isRequest) {
opDesc.setHasRequestSwaRefAttachments(true);
}
else {
opDesc.setHasResponseSwaRefAttachments(true);
}
}
}
}
}
}
}
public static void registerHandlerHeaders(AxisService axisService, List<Handler> handlers) {
if (handlers == null || axisService == null) {
return;
}
ArrayList<QName> understoodHeaderQNames = new ArrayList<QName>();
for (Handler handler : handlers) {
if (handler instanceof SOAPHandler) {
SOAPHandler soapHandler = (SOAPHandler) handler;
Set<QName> headers = soapHandler.getHeaders();
if (headers != null) {
for (QName header : headers) {
if (!understoodHeaderQNames.contains(header)) {
understoodHeaderQNames.add(header);
}
}
}
}
}
if (!understoodHeaderQNames.isEmpty()) {
Parameter headerQNParameter =
new Parameter(EndpointDescription.HANDLER_PARAMETER_QNAMES, understoodHeaderQNames);
try {
axisService.addParameter(headerQNParameter);
} catch (AxisFault e) {
log.warn(Messages.getMessage("regHandlerHeadersErr",axisService.getName(),e.getMessage()));
}
}
}
/**
* Given a binding type value based on a JAXWS anntation, return the corresponding WSDL
* binding type. The JAXWS annotation values understood are those returned by
* mapBindingTypeWsdltoAnnotation.
*
* @see #mapBindingTypeWsdlToAnnotation(String, String)
*
* @param annotationBindingType The binding type as represented by a JAXWS annotation value
* @return The binding type as represented by a WSDL binding extension namespace value
*/
public static String mapBindingTypeAnnotationToWsdl(String annotationBindingType) {
String wsdlBindingType = null;
if (SOAPBinding.SOAP11HTTP_BINDING.equals(annotationBindingType)
|| MDQConstants.SOAP11JMS_BINDING.equals(annotationBindingType)) {
wsdlBindingType = EndpointDescriptionWSDL.SOAP11_WSDL_BINDING;
} else if (SOAPBinding.SOAP12HTTP_BINDING.equals(annotationBindingType)
|| MDQConstants.SOAP12JMS_BINDING.equals(annotationBindingType)) {
wsdlBindingType = EndpointDescriptionWSDL.SOAP12_WSDL_BINDING;
} else if (javax.xml.ws.http.HTTPBinding.HTTP_BINDING.equals(annotationBindingType)) {
wsdlBindingType = EndpointDescriptionWSDL.HTTP_WSDL_BINDING;
}
return wsdlBindingType;
}
/**
* Given a binding type value based on WSDL, return the corresponding JAXWS annotation value.
* The WSDL binding type values are based on the namespace of the binding extension element.
* The JAXWS annotation values correspond to the values to the HTTPBinding and SOAPBinding
* annotations. Additionally, proprietary values for JMS bindings are supported. The JAXWS
* binding type annotation values returned could be from SOAPBinding or HTTPBinding.
*
* @param wsdlBindingType The binding type as represnted by the WSDL binding extension namespace
* @param soapTransport The WSDL transport. Used to determine if a JMS binding type should
* be returned
* @return The binding represented by a JAXWS Binding Type Annotation value from either
* SOAPBinding or HTTPBinding.
*/
public static String mapBindingTypeWsdlToAnnotation(String wsdlBindingType, String soapTransport) {
String soapBindingType = null;
if (EndpointDescriptionWSDL.SOAP11_WSDL_BINDING.equals(wsdlBindingType)) {
if (MDQConstants.SOAP11JMS_BINDING.equals(soapTransport)) {
soapBindingType = MDQConstants.SOAP11JMS_BINDING;
} else {
//REVIEW: We are making the assumption that if not JMS, then HTTP
soapBindingType = SOAPBinding.SOAP11HTTP_BINDING;
}
} else if (EndpointDescriptionWSDL.SOAP12_WSDL_BINDING.equals(wsdlBindingType)) {
if (MDQConstants.SOAP12JMS_BINDING.equals(soapTransport)) {
soapBindingType = MDQConstants.SOAP12JMS_BINDING;
} else {
//REVIEW: We are making the assumption that if not JMS, then HTTP
soapBindingType = SOAPBinding.SOAP12HTTP_BINDING;
}
} else if (EndpointDescriptionWSDL.HTTP_WSDL_BINDING.equals(wsdlBindingType)) {
soapBindingType = javax.xml.ws.http.HTTPBinding.HTTP_BINDING;
}
return soapBindingType;
}
}