blob: d4b112b7fc4502e464b3acf31caa6c101f428018 [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.marshaller.impl.alt;
import org.apache.axis2.AxisFault;
import org.apache.axis2.description.AxisOperation;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.description.Parameter;
import org.apache.axis2.java.security.AccessController;
import org.apache.axis2.jaxws.ExceptionFactory;
import org.apache.axis2.jaxws.core.MessageContext;
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.FaultDescription;
import org.apache.axis2.jaxws.description.OperationDescription;
import org.apache.axis2.jaxws.description.ParameterDescription;
import org.apache.axis2.jaxws.description.ServiceDescription;
import org.apache.axis2.jaxws.i18n.Messages;
import org.apache.axis2.jaxws.message.Block;
import org.apache.axis2.jaxws.message.Message;
import org.apache.axis2.jaxws.message.Protocol;
import org.apache.axis2.jaxws.message.XMLFault;
import org.apache.axis2.jaxws.message.XMLFaultReason;
import org.apache.axis2.jaxws.message.databinding.JAXBBlockContext;
import org.apache.axis2.jaxws.message.databinding.JAXBUtils;
import org.apache.axis2.jaxws.message.factory.JAXBBlockFactory;
import org.apache.axis2.jaxws.message.util.XMLFaultUtils;
import org.apache.axis2.jaxws.registry.FactoryRegistry;
import org.apache.axis2.jaxws.runtime.description.marshal.AnnotationDesc;
import org.apache.axis2.jaxws.runtime.description.marshal.FaultBeanDesc;
import org.apache.axis2.jaxws.runtime.description.marshal.MarshalServiceRuntimeDescription;
import org.apache.axis2.jaxws.runtime.description.marshal.MarshalServiceRuntimeDescriptionFactory;
import org.apache.axis2.jaxws.utility.ClassUtils;
import org.apache.axis2.jaxws.utility.ConvertUtils;
import org.apache.axis2.jaxws.utility.SAAJFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.activation.DataHandler;
import javax.jws.WebParam.Mode;
import javax.jws.WebService;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPConstants;
import javax.xml.soap.SOAPFault;
import javax.xml.stream.XMLStreamException;
import javax.xml.ws.AsyncHandler;
import javax.xml.ws.Holder;
import javax.xml.ws.ProtocolException;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.soap.SOAPFaultException;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.TreeSet;
/** Static Utilty Classes used by the MethodMarshaller implementations in the alt package. */
public class MethodMarshallerUtils {
private static Log log = LogFactory.getLog(MethodMarshallerUtils.class);
private static JAXBBlockFactory factory =
(JAXBBlockFactory)FactoryRegistry.getFactory(JAXBBlockFactory.class);
/** Intentionally Private. This is a static utility class */
private MethodMarshallerUtils() {
}
/**
* Returns the list of PDElements that need to be marshalled onto the wire
*
* @param marshalDesc
* @param params ParameterDescription for this operation
* @param sigArguments arguments
* @param isInput indicates if input or output params(input args on client,
* output args on server)
* @param isDocLitWrapped
* @param isRPC
* @return PDElements
*/
static List<PDElement> getPDElements(MarshalServiceRuntimeDescription marshalDesc,
ParameterDescription[] params,
Object[] sigArguments,
boolean isInput,
boolean isDocLitWrapped,
boolean isRPC) {
List<PDElement> pdeList = new ArrayList<PDElement>();
int index = 0;
for (int i = 0; i < params.length; i++) {
ParameterDescription pd = params[i];
if (pd.getMode() == Mode.IN && isInput ||
pd.getMode() == Mode.INOUT ||
pd.getMode() == Mode.OUT && !isInput) {
// Get the matching signature argument
Object value = sigArguments[i];
// Don't consider async handlers, they are are not represented on the wire,
// thus they don't have a PDElement
if (isAsyncHandler(value)) {
continue;
}
// Convert from Holder into value
if (isHolder(value)) {
value = ((Holder)value).value;
}
// Get the formal type representing the value
Class formalType = pd.getParameterActualType();
// The namespace and local name are obtained differently depending on
// the style/use and header
QName qName = null;
if (pd.isHeader()) {
// Headers (even rpc) are marshalled with the name defined by the
// element= attribute on the wsd:part
qName = new QName(pd.getTargetNamespace(), pd.getParameterName());
} else if (isDocLitWrapped) {
// For doc/lit wrapped, the localName comes from the PartName
qName = new QName(pd.getTargetNamespace(), pd.getPartName());
} else if (isRPC) {
// Per WSI-BP, the namespace uri is unqualified
qName = new QName(pd.getPartName());
} else {
qName = new QName(pd.getTargetNamespace(), pd.getParameterName());
}
// Create an Element rendering
Element element = null;
AttachmentDescription attachmentDesc = pd.getAttachmentDescription();
if (attachmentDesc != null) {
PDElement pde = createPDElementForAttachment(pd, qName, value, formalType);
pdeList.add(pde);
} else {
if (!marshalDesc.getAnnotationDesc(formalType).hasXmlRootElement()) {
/* when a schema defines a SimpleType with xsd list jaxws tooling
* generates artifacts with array rather than a java.util.List
* However the ObjectFactory definition uses a List and thus
* marshalling fails. Lets convert the Arrays to List and recreate
* the JAXBElements for the same.
*/
if (pd.isListType()) {
List<Object> list = new ArrayList<Object>();
if (formalType.isArray()) {
for (int count = 0; count < Array.getLength(value); count++) {
Object obj = Array.get(value, count);
list.add(obj);
}
}
element = new Element(list, qName, List.class);
} else {
element = new Element(value, qName, formalType);
}
}
else{
element = new Element(value, qName);
}
// The object is now ready for marshalling
PDElement pde = new PDElement(pd, element, null);
pdeList.add(pde);
}
}
}
return pdeList;
}
/**
* @param pd
* @param qName
* @param value
* @param formalType
* @return
*/
private static PDElement createPDElementForAttachment(ParameterDescription pd,
QName qName,
Object value,
Class formalType) {
PDElement pde;
if (log.isDebugEnabled()) {
log.debug("Creating a PDElement for an attachment value: " +
((value == null)? "null":value.getClass().getName()));
log.debug("ParameterDescription = " + pd.toString());
}
AttachmentDescription attachmentDesc = pd.getAttachmentDescription();
AttachmentType attachmentType = attachmentDesc.getAttachmentType();
if (attachmentType == AttachmentType.SWA) {
// Create an Attachment object with the signature value
Attachment attachment = new Attachment(value,
formalType,
attachmentDesc,
pd.getPartName());
pde = new PDElement(pd,
null, // For SWA Attachments, there is no element reference to the attachment
null,
attachment);
} else {
throw ExceptionFactory.makeWebServiceException(Messages.getMessage("pdElementErr"));
}
return pde;
}
/**
* Return the list of PDElements that is unmarshalled from the wire
*
* @param params ParameterDescription for this operation
* @param message Message
* @param packages set of packages needed to unmarshal objects for this operation
* @param isInput indicates if input or output params (input on server, output on client)
* @param hasReturnInBody if isInput=false, then this parameter indicates whether a
* return value is expected in the body.
* @param unmarshalByJavaType in most scenarios this is null.
* Only use this in the scenarios that require unmarshalling by java type
* @return ParamValues
*/
static List<PDElement> getPDElements(ParameterDescription[] params,
Message message,
TreeSet<String> packages,
boolean isInput,
boolean hasReturnInBody,
Class[] unmarshalByJavaType) throws XMLStreamException {
List<PDElement> pdeList = new ArrayList<PDElement>();
// Count
int totalBodyBlocks = 0;
for (int i = 0; i < params.length; i++) {
ParameterDescription pd = params[i];
if (pd.getMode() == Mode.IN && isInput ||
pd.getMode() == Mode.INOUT ||
pd.getMode() == Mode.OUT && !isInput) {
if (!pd.isHeader() && !isSWAAttachment(pd)) {
totalBodyBlocks++;
}
}
}
if (!isInput && hasReturnInBody) {
totalBodyBlocks++;
}
int index = (!isInput && hasReturnInBody) ? 1 : 0;
// TODO What if return is an swa attachment, then this should start
// at 1 not 0.
int swaIndex = 0;
for (int i = 0; i < params.length; i++) {
ParameterDescription pd = params[i];
if (pd.getMode() == Mode.IN && isInput ||
pd.getMode() == Mode.INOUT ||
pd.getMode() == Mode.OUT && !isInput) {
// Don't consider async handlers, they are are not represented on the wire,
// thus they don't have a PDElement
// TODO
//if (isAsyncHandler(param)) {
// continue;
//}
Block block = null;
JAXBBlockContext context = new JAXBBlockContext(packages);
AttachmentDescription attachmentDesc = pd.getAttachmentDescription();
if (attachmentDesc == null) {
// Normal Processing: Not an Attachment
// Trigger unmarshal by java type if necessary
if (unmarshalByJavaType != null && unmarshalByJavaType[i] != null) {
context.setProcessType(unmarshalByJavaType[i]);
context.setIsxmlList(pd.isListType());
}
// Unmarshal the object into a JAXB object or JAXBElement
if (pd.isHeader()) {
// Get the Block from the header
// NOTE The parameter name is always used to get the header
// element...even if the style is RPC.
String localName = pd.getParameterName();
block = message.getHeaderBlock(pd.getTargetNamespace(),
localName,
context,
factory);
} else {
if (totalBodyBlocks > 1) {
// You must use this method if there are more than one body block
// This method may cause OM expansion
block = message.getBodyBlock(index, context, factory);
} else {
// Use this method if you know there is only one body block.
// This method prevents OM expansion.
block = message.getBodyBlock(context, factory);
}
index++;
}
Element element = new Element(block.getBusinessObject(true),
block.getQName());
PDElement pde =
new PDElement(pd, element, unmarshalByJavaType == null ? null
: unmarshalByJavaType[i]);
pdeList.add(pde);
} else {
// Attachment Processing
if (attachmentDesc.getAttachmentType() == AttachmentType.SWA) {
String partName = pd.getPartName();
String cid = null;
if (log.isDebugEnabled()) {
log.debug("Getting the attachment dataHandler for partName=" + partName);
}
if (partName != null && partName.length() > 0) {
// Compliant WS-I behavior
cid = message.getAttachmentID(partName);
}
if (cid == null) {
if (log.isDebugEnabled()) {
log.debug("Attachment dataHandler was not found. Fallback to use attachment " + swaIndex);
}
// Toleration mode for non-compliant attachment names
cid = message.getAttachmentID(swaIndex);
}
DataHandler dh = message.getDataHandler(cid);
Attachment attachment = new Attachment(dh, cid);
PDElement pde = new PDElement(pd, null, null, attachment);
pdeList.add(pde);
swaIndex++;
} else {
throw ExceptionFactory.makeWebServiceException(Messages.getMessage("pdElementErr"));
}
}
}
}
return pdeList;
}
/**
* Creates the request signature arguments (server) from a list
* of element eabled object (PDEements)
* @param pds ParameterDescriptions for this Operation
* @param pvList Element enabled object
* @return Signature Args
* @throws InstantiationException
* @throws IllegalAccessException
* @throws ClassNotFoundException
*/
static Object[] createRequestSignatureArgs(ParameterDescription[] pds,
List<PDElement> pdeList)
throws InstantiationException, IOException,
IllegalAccessException,
ClassNotFoundException {
Object[] args = new Object[pds.length];
int pdeIndex = 0;
for (int i = 0; i < args.length; i++) {
// Get the paramValue
PDElement pde = (pdeIndex < pdeList.size()) ? pdeList.get(pdeIndex) : null;
ParameterDescription pd = pds[i];
if (pde == null ||
pde.getParam() != pd) {
// We have a ParameterDesc but there is not an equivalent PDElement.
// Provide the default
if (pd.isHolderType()) {
args[i] = createHolder(pd.getParameterType(), null);
} else {
args[i] = null;
}
} else {
// We have a matching paramValue. Get the type object that represents the type
Object value = null;
if (pde.getAttachment() != null) {
value = pde.getAttachment().getDataHandler();
} else {
value = pde.getElement().getTypeValue();
}
pdeIndex++;
// Now that we have the type, there may be a mismatch
// between the type (as defined by JAXB) and the formal
// parameter (as defined by JAXWS). Frequently this occurs
// with respect to T[] versus List<T>.
// Use the convert utility to silently do any conversions
if (ConvertUtils.isConvertable(value, pd.getParameterActualType())) {
value = ConvertUtils.convert(value, pd.getParameterActualType());
} else {
String objectClass = (value == null) ? "null" : value.getClass().getName();
throw ExceptionFactory.makeWebServiceException(
Messages.getMessage("convertProblem", objectClass,
pd.getParameterActualType().getName()));
}
// The signature may want a holder representation
if (pd.isHolderType()) {
args[i] = createHolder(pd.getParameterType(), value);
} else {
args[i] = value;
}
}
}
return args;
}
/**
* Update the signature arguments on the client with the unmarshalled element enabled objects
* (pvList)
*
* @param pds ParameterDescriptions
* @param pdeList Element Enabled objects
* @param signatureArgs Signature Arguments (the out/inout holders are updated)
* @throws InstantiationException
* @throws IllegalAccessException
* @throws ClassNotFoundException
*/
static void updateResponseSignatureArgs(ParameterDescription[] pds, List<PDElement> pdeList,
Object[] signatureArgs)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
int pdeIndex = 0;
// Each ParameterDescriptor has a correspondinging signatureArg from the
// the initial client call. The pvList contains the response values from the message.
// Walk the ParameterDescriptor/SignatureArg list and populate the holders with
// the match PDElement
for (int i = 0; i < pds.length; i++) {
// Get the param value
PDElement pde = (pdeIndex < pdeList.size()) ? pdeList.get(pdeIndex) : null;
ParameterDescription pd = pds[i];
if (pde != null && pde.getParam() == pd) {
// We have a matching paramValue. Get the value that represents the type
Object value = null;
if (pde.getAttachment() == null) {
value = pde.getElement().getTypeValue();
} else {
value = pde.getAttachment().getDataHandler();
}
pdeIndex++;
// Now that we have the type, there may be a mismatch
// between the type (as defined by JAXB) and the formal
// parameter (as defined by JAXWS). Frequently this occurs
// with respect to T[] versus List<T>.
// Use the convert utility to silently do any conversions
if (ConvertUtils.isConvertable(value, pd.getParameterActualType())) {
value = ConvertUtils.convert(value, pd.getParameterActualType());
} else {
String objectClass = (value == null) ? "null" : value.getClass().getName();
throw ExceptionFactory.makeWebServiceException(
Messages.getMessage("convertProblem", objectClass,
pd.getParameterActualType().getName()));
}
// TODO Assert that this ParameterDescriptor must represent
// an OUT or INOUT and must have a non-null holder object to
// store the value
if (isHolder(signatureArgs[i])) {
((Holder)signatureArgs[i]).value = value;
}
}
}
}
/**
* Marshal the element enabled objects (pvList) to the Message
*
* @param pdeList element enabled objects
* @param message Message
* @param packages Packages needed to do a JAXB Marshal
* @throws MessageException
*/
static void toMessage(List<PDElement> pdeList,
Message message,
TreeSet<String> packages) throws WebServiceException {
int totalBodyBlocks = 0;
for (int i = 0; i < pdeList.size(); i++) {
PDElement pde = pdeList.get(i);
if (!pde.getParam().isHeader() &&
pde.getElement() != null) { // Element is null for SWARef attachment
totalBodyBlocks++;
}
}
int index = message.getNumBodyBlocks();
for (int i = 0; i < pdeList.size(); i++) {
PDElement pde = pdeList.get(i);
// Create JAXBContext
JAXBBlockContext context = new JAXBBlockContext(packages);
Attachment attachment = pde.getAttachment();
if (attachment == null) {
// Normal Flow: Not an attachment
// Marshal by type only if necessary
if (pde.getByJavaTypeClass() != null) {
context.setProcessType(pde.getByJavaTypeClass());
if(pde.getParam()!=null){
context.setIsxmlList(pde.getParam().isListType());
}
}
// Create a JAXBBlock out of the value.
// (Note that the PDElement.getValue always returns an object
// that has an element rendering...ie. it is either a JAXBElement o
// has @XmlRootElement defined
Block block =
factory.createFrom(pde.getElement().getElementValue(),
context,
pde.getElement().getQName());
if (pde.getParam().isHeader()) {
// Header block
QName qname = block.getQName();
message.setHeaderBlock(qname.getNamespaceURI(), qname.getLocalPart(), block);
} else {
// Body block
if (totalBodyBlocks < 1) {
// If there is only one block, use the following "more performant" method
message.setBodyBlock(block);
} else {
message.setBodyBlock(index, block);
}
index++;
}
} else {
// The parameter is an attachment
AttachmentType type = pde.getParam().
getAttachmentDescription().getAttachmentType();
if (type == AttachmentType.SWA) {
// All we need to do is set the data handler on the message.
// For SWA attachments, the message does not reference the attachment.
message.addDataHandler(attachment.getDataHandler(),
attachment.getContentID());
message.setDoingSWA(true);
} else {
throw ExceptionFactory.makeWebServiceException(Messages.getMessage("pdElementErr"));
}
}
}
}
/**
* Marshals the return object to the message (used on server to marshal return object)
*
* @param returnElement element
* @param returnType
* @param marshalDesc
* @param message
* @param marshalByJavaTypeClass..we must do this for RPC...discouraged otherwise
* @param isHeader
* @throws MessageException
*/
static void toMessage(Element returnElement,
Class returnType,
boolean isList,
MarshalServiceRuntimeDescription marshalDesc,
Message message,
Class marshalByJavaTypeClass,
boolean isHeader)
throws WebServiceException {
// Create the JAXBBlockContext
// RPC uses type marshalling, so recored the rpcType
JAXBBlockContext context = new JAXBBlockContext(marshalDesc.getPackages());
if (marshalByJavaTypeClass != null) {
context.setProcessType(marshalByJavaTypeClass);
context.setIsxmlList(isList);
}
// Create a JAXBBlock out of the value.
Block block = factory.createFrom(returnElement.getElementValue(),
context,
returnElement.getQName());
if (isHeader) {
message.setHeaderBlock(returnElement.getQName().getNamespaceURI(),
returnElement.getQName().getLocalPart(), block);
} else {
message.setBodyBlock(block);
}
}
/**
* Unmarshal the return object from the message
*
* @param packages
* @param message
* @param unmarshalByJavaTypeClass Used only to indicate unmarshaling by type...only necessary
* in some scenarios
* @param isHeader
* @param headerNS (only needed if isHeader)
* @param headerLocalPart (only needed if isHeader)
* @param hasOutputBodyParams (true if the method has out or inout params other
* than the return value)
* @return Element
* @throws WebService
* @throws XMLStreamException
*/
static Element getReturnElement(TreeSet<String> packages,
Message message,
Class unmarshalByJavaTypeClass, // normally null
boolean isList,
boolean isHeader,
String headerNS,
String headerLocalPart,
boolean hasOutputBodyParams)
throws WebServiceException, XMLStreamException {
// The return object is the first block in the body
JAXBBlockContext context = new JAXBBlockContext(packages);
if (unmarshalByJavaTypeClass != null && !isHeader) {
context.setProcessType(unmarshalByJavaTypeClass);
context.setIsxmlList(isList);
}
Block block = null;
boolean isBody = false;
if (isHeader) {
block = message.getHeaderBlock(headerNS, headerLocalPart, context, factory);
} else {
if (hasOutputBodyParams) {
block = message.getBodyBlock(0, context, factory);
isBody = true;
} else {
// If there is only 1 block, we can use the get body block method
// that streams the whole block content.
block = message.getBodyBlock(context, factory);
//We look for body block only when the return type associated with operation is not void.
//If a null body block is returned in response on a operation that is not void, its a user error.
isBody = true;
}
}
//We look for body block only when the return type associated with operation is not void.
//If a null body block is returned in response on a operation that has non void return type, its a user error.
if(isBody && block == null){
if(log.isDebugEnabled()){
log.debug("Empty Body Block Found in response Message for wsdl Operation defintion that expects an Output");
log.debug("Return type associated with SEI operation is not void, Body Block cannot be null");
}
throw ExceptionFactory.makeWebServiceException(Messages.getMessage("MethodMarshallerUtilErr1"));
}
// Get the business object. We want to return the object that represents the type.
Element returnElement = new Element(block.getBusinessObject(true), block.getQName());
return returnElement;
}
/**
* Marshaling a fault is essentially the same for rpc/lit and doc/lit. This method is used by
* all of the MethodMarshallers
*
* @param throwable Throwable to marshal
* @param operationDesc OperationDescription
* @param packages Packages needed to marshal the object
* @param message Message
*/
static void marshalFaultResponse(Throwable throwable,
MarshalServiceRuntimeDescription marshalDesc,
OperationDescription operationDesc,
Message message) {
// Get the root cause of the throwable object
Throwable t = ClassUtils.getRootCause(throwable);
if (log.isDebugEnabled()) {
log.debug("Marshal Throwable =" + throwable.getClass().getName());
log.debug(" rootCause =" + t.getClass().getName());
log.debug(" exception=" + t.toString());
log.debug(" stack=" + stackToString(t));
}
XMLFault xmlfault = null;
try {
// There are 5 different categories of exceptions.
// Each category has a little different marshaling code.
// A) Service Exception that matches the JAX-WS
// specification (chapter 2.5 of the spec)
// B) Service Exception that matches the JAX-WS "legacy"
// exception (chapter 3.7 of the spec)
// C) SOAPFaultException
// D) WebServiceException
// E) Other runtime exceptions (i.e. NullPointerException)
// Get the FaultDescriptor matching this Exception.
// If FaultDescriptor is found, this is a JAX-B Service Exception.
// If not found, this is a System Exception
FaultDescription fd =
operationDesc.resolveFaultByExceptionName(t.getClass().getCanonicalName());
if (fd != null) {
if (log.isErrorEnabled()) {
log.debug("Marshal as a Service Exception");
}
// Create the JAXB Context
JAXBBlockContext context = new JAXBBlockContext(marshalDesc.getPackages());
// The exception is a Service Exception.
// It may be (A) JAX-WS compliant exception or
// (B) JAX-WS legacy exception
// The faultBeanObject is a JAXB object that represents the data of the exception.
// It is marshalled in the detail section of the soap fault.
// The faultBeanObject is obtained direction from the exception (A) or via
// the legacy exception rules (B).
Object faultBeanObject = null;
FaultBeanDesc faultBeanDesc = marshalDesc.getFaultBeanDesc(fd);
String faultInfo = fd.getFaultInfo();
if (faultInfo == null || faultInfo.length() == 0) {
// Legacy Exception case
faultBeanObject = LegacyExceptionUtil.createFaultBean(t, fd, marshalDesc);
} else {
// Normal case
// Get the fault bean object.
Method getFaultInfo = t.getClass().getMethod("getFaultInfo", null);
faultBeanObject = getFaultInfo.invoke(t, null);
}
if (log.isErrorEnabled()) {
log.debug("The faultBean type is" + faultBeanObject.getClass().getName());
}
// Use "by java type" marshalling if necessary
if (faultBeanObject == t ||
(context.getConstructionType() != JAXBUtils.CONSTRUCTION_TYPE
.BY_CONTEXT_PATH &&
isNotJAXBRootElement(faultBeanObject.getClass(), marshalDesc))) {
context.setProcessType(faultBeanObject.getClass());
}
QName faultBeanQName = new QName(faultBeanDesc.getFaultBeanNamespace(),
faultBeanDesc.getFaultBeanLocalName());
// Make sure the faultBeanObject can be marshalled as an element
if (!marshalDesc.getAnnotationDesc(faultBeanObject.getClass()).
hasXmlRootElement())
{
faultBeanObject = new JAXBElement(faultBeanQName, faultBeanObject.getClass(),
faultBeanObject);
}
// Create a detailblock representing the faultBeanObject
Block[] detailBlocks = new Block[1];
detailBlocks[0] = factory.createFrom(faultBeanObject, context, faultBeanQName);
if (log.isDebugEnabled()) {
log.debug("Create the xmlFault for the Service Exception");
}
// Get the fault text using algorithm defined in JAX-WS 10.2.2.3
String text = t.getMessage();
if (text == null || text.length() == 0) {
text = t.toString();
}
// Now make a XMLFault containing the detailblock
xmlfault = new XMLFault(null, new XMLFaultReason(text), detailBlocks);
} else {
xmlfault = createXMLFaultFromSystemException(t);
}
} catch (Throwable e) {
// If an exception occurs while demarshalling an exception,
// then rinse and repeat with a system exception
if (log.isDebugEnabled()) {
log.debug("An exception (" + e + ") occurred while marshalling exception (" + t +
")");
}
WebServiceException wse = ExceptionFactory.makeWebServiceException(e);
xmlfault = createXMLFaultFromSystemException(wse);
}
// Add the fault to the message
message.setXMLFault(xmlfault);
}
/**
* This method is used by WebService Impl and Provider to create an XMLFault (for marshalling)
* from an exception that is a non-service exception
*
* @param t Throwable that represents a Service Exception
* @return XMLFault
*/
public static XMLFault createXMLFaultFromSystemException(Throwable t) {
try {
XMLFault xmlfault = null;
if (t instanceof SOAPFaultException) {
if (log.isErrorEnabled()) {
log.debug("Marshal SOAPFaultException");
}
// Category C: SOAPFaultException
// Construct the xmlFault from the SOAPFaultException's Fault
SOAPFaultException sfe = (SOAPFaultException)t;
SOAPFault soapFault = sfe.getFault();
if (soapFault == null) {
// No fault ? I will treat this like category E
xmlfault =
new XMLFault(null, // Use the default XMLFaultCode
new XMLFaultReason(
t.toString())); // Assumes text lang of current Locale
} else {
xmlfault = XMLFaultUtils.createXMLFault(soapFault);
}
} else if (t instanceof WebServiceException) {
if (log.isErrorEnabled()) {
log.debug("Marshal as a WebServiceException");
}
// Category D: WebServiceException
// The reason is constructed with the getMessage of the exception.
// There is no detail
WebServiceException wse = (WebServiceException)t;
// Get the fault text using algorithm defined in JAX-WS 10.2.2.3
String text = wse.getMessage();
if (text == null || text.length() == 0) {
text = wse.toString();
}
xmlfault = new XMLFault(null, // Use the default XMLFaultCode
new XMLFaultReason(
text)); // Assumes text lang of current Locale
} else {
if (log.isErrorEnabled()) {
log.debug("Marshal as a unchecked System Exception");
}
// Category E: Other System Exception
// The reason is constructed with the toString of the exception.
// This places the class name of the exception in the reason
// There is no detail.
// Get the fault text using algorithm defined in JAX-WS 10.2.2.3
String text = t.getMessage();
if (text == null || text.length() == 0) {
text = t.toString();
}
xmlfault = new XMLFault(null, // Use the default XMLFaultCode
new XMLFaultReason(
text)); // Assumes text lang of current Locale
}
return xmlfault;
} catch (Throwable e) {
try {
// If an exception occurs while demarshalling an exception,
// then rinse and repeat with a webservice exception
if (log.isDebugEnabled()) {
log.debug("An exception (" + e + ") occurred while marshalling exception (" +
t + ")");
}
// Get the fault text using algorithm defined in JAX-WS 10.2.2.3
String text = e.getMessage();
if (text == null || text.length() == 0) {
text = e.toString();
}
WebServiceException wse = ExceptionFactory.makeWebServiceException(e);
return new XMLFault(null, // Use the default XMLFaultCode
new XMLFaultReason(
text)); // Assumes text lang of current Locale
} catch (Exception e2) {
// Exception while creating Exception for Exception
throw ExceptionFactory.makeWebServiceException(e2);
}
}
}
/**
* Unmarshal the service/system exception from a Message. This is used by all of the
* marshallers
*
* @param operationDesc
* @param marshalDesc
* @param message
* @return Throwable
* @throws WebServiceException
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
* @throws XMLStreamException
* @throws InvocationTargetException
* @throws NoSuchMethodException
*/
static Throwable demarshalFaultResponse(OperationDescription operationDesc,
MarshalServiceRuntimeDescription marshalDesc,
Message message)
throws WebServiceException, ClassNotFoundException, IllegalAccessException,
InstantiationException, XMLStreamException, InvocationTargetException,
NoSuchMethodException {
Throwable exception = null;
// Get the fault from the message and get the detail blocks (probably one)
XMLFault xmlfault = message.getXMLFault();
Block[] detailBlocks = xmlfault.getDetailBlocks();
// If there is only one block, get the element name of that block.
QName elementQName = null;
if (detailBlocks != null && detailBlocks.length == 1) {
elementQName = detailBlocks[0].getQName();
}
// Use the element name to find the matching FaultDescriptor
FaultDescription faultDesc = null;
if (elementQName != null) {
for (int i = 0; i < operationDesc.getFaultDescriptions().length && faultDesc == null;
i++) {
FaultDescription fd = operationDesc.getFaultDescriptions()[i];
FaultBeanDesc faultBeanDesc = marshalDesc.getFaultBeanDesc(fd);
if (faultBeanDesc != null) {
QName tryQName = new QName(faultBeanDesc.getFaultBeanNamespace(),
faultBeanDesc.getFaultBeanLocalName());
if (log.isErrorEnabled()) {
log.debug(" FaultDescription qname is (" + tryQName +
") and detail element qname is (" + elementQName + ")");
}
if (elementQName.equals(tryQName)) {
faultDesc = fd;
}
}
}
}
if (faultDesc == null && elementQName != null) {
// If not found, retry the search using just the local name
for (int i = 0; i < operationDesc.getFaultDescriptions().length && faultDesc == null;
i++) {
FaultDescription fd = operationDesc.getFaultDescriptions()[i];
FaultBeanDesc faultBeanDesc = marshalDesc.getFaultBeanDesc(fd);
if (faultBeanDesc != null) {
String tryName = faultBeanDesc.getFaultBeanLocalName();
if (elementQName.getLocalPart().equals(tryName)) {
faultDesc = fd;
}
}
}
}
if (faultDesc == null) {
// This is a system exception if the method does not throw a checked exception or if
// the detail block is missing or contains multiple items.
exception = createSystemException(xmlfault, message);
} else {
if (log.isErrorEnabled()) {
log.debug("Ready to demarshal service exception. The detail entry name is " +
elementQName);
}
FaultBeanDesc faultBeanDesc = marshalDesc.getFaultBeanDesc(faultDesc);
boolean isLegacy =
(faultDesc.getFaultInfo() == null || faultDesc.getFaultInfo().length() == 0);
// Get the JAXB object from the block
JAXBBlockContext blockContext = new JAXBBlockContext(marshalDesc.getPackages());
// Note that faultBean may not be a bean, it could be a primitive
Class faultBeanFormalClass;
try {
faultBeanFormalClass = loadClass(faultBeanDesc.getFaultBeanClassName());
} catch (ClassNotFoundException e){
faultBeanFormalClass = loadClass(faultBeanDesc.getFaultBeanClassName(), operationDesc.getEndpointInterfaceDescription().getEndpointDescription().getAxisService().getClassLoader());
}
// Use "by java type" marshalling if necessary
if (blockContext.getConstructionType() !=
JAXBUtils.CONSTRUCTION_TYPE.BY_CONTEXT_PATH &&
isNotJAXBRootElement(faultBeanFormalClass, marshalDesc)) {
blockContext.setProcessType(faultBeanFormalClass);
}
// Get the jaxb block and business object
Block jaxbBlock = factory.createFrom(detailBlocks[0], blockContext);
Object faultBeanObject = jaxbBlock.getBusinessObject(true);
// At this point, faultBeanObject is an object that can be rendered as an
// element. We want the object that represents the type.
if (faultBeanObject instanceof JAXBElement) {
faultBeanObject = ((JAXBElement)faultBeanObject).getValue();
}
if (log.isErrorEnabled()) {
log.debug("Unmarshalled the detail element into a JAXB object");
}
// Construct the JAX-WS generated exception that holds the faultBeanObject
Class exceptionClass;
try {
exceptionClass = loadClass(faultDesc.getExceptionClassName());
} catch (ClassNotFoundException e){
exceptionClass = loadClass(faultDesc.getExceptionClassName(), operationDesc.getEndpointInterfaceDescription().getEndpointDescription().getAxisService().getClassLoader());
}
if (log.isErrorEnabled()) {
log.debug("Found FaultDescription. The exception name is " +
exceptionClass.getName());
}
exception = createServiceException(xmlfault.getReason().getText(),
exceptionClass,
faultBeanObject,
faultBeanFormalClass,
marshalDesc,
isLegacy);
}
return exception;
}
/**
* @param pds
* @return Number of inout or out parameters
*/
static int numOutputBodyParams(ParameterDescription[] pds) {
int count = 0;
for (int i=0; i<pds.length; i++) {
// TODO Need to change this to also detect not attachment
if (!pds[i].isHeader()) {
if (pds[i].getMode() == Mode.INOUT ||
pds[i].getMode() == Mode.OUT) {
count++;
}
}
}
return count;
}
/**
* @param value
* @return if async handler
*/
static boolean isAsyncHandler(Object value) {
return (value instanceof AsyncHandler);
}
/**
* @param value
* @return true if value is holder
*/
static boolean isHolder(Object value) {
return value != null && Holder.class.isAssignableFrom(value.getClass());
}
/**
* Crate a Holder
*
* @param <T>
* @param paramType
* @param value
* @return
* @throws IllegalAccessException
* @throws InstantiationException
* @throws ClassNotFoundException
*/
static <T> Holder<T> createHolder(Class paramType, T value)
throws IllegalAccessException, InstantiationException, ClassNotFoundException {
if (Holder.class.isAssignableFrom(paramType)) {
Holder holder = (Holder) paramType.newInstance();
holder.value = value;
return holder;
}
return null;
}
/**
* Load the class
*
* @param className
* @return loaded class
* @throws ClassNotFoundException
*/
static Class loadClass(String className) throws ClassNotFoundException {
// Don't make this public, its a security exposure
Class cls = ClassUtils.getPrimitiveClass(className);
if (cls == null) {
cls = forName(className, true, getContextClassLoader());
}
return cls;
}
/**
* Load the class
*
* @param className
* @return loaded class
* @throws ClassNotFoundException
*/
static Class loadClass(String className, ClassLoader cl) throws ClassNotFoundException {
// Don't make this public, its a security exposure
Class cls = ClassUtils.getPrimitiveClass(className);
if (cls == null) {
cls = forName(className, true, cl);
}
return cls;
}
/**
* Return the class for this name
*
* @return Class
*/
private static Class forName(final String className, final boolean initialize,
final ClassLoader classLoader) throws ClassNotFoundException {
// NOTE: This method must remain private because it uses AccessController
Class cl = null;
try {
cl = (Class)AccessController.doPrivileged(
new PrivilegedExceptionAction() {
public Object run() throws ClassNotFoundException {
// Class.forName does not support primitives
Class cls = ClassUtils.getPrimitiveClass(className);
if (cls == null) {
cls = Class.forName(className, initialize, classLoader);
}
return cls;
}
}
);
} catch (PrivilegedActionException e) {
if (log.isDebugEnabled()) {
log.debug("Exception thrown from AccessController: " + e);
}
throw (ClassNotFoundException) e.getException();
}
return cl;
}
/** @return ClassLoader */
private static ClassLoader getContextClassLoader() {
// NOTE: This method must remain private because it uses AccessController
ClassLoader cl = null;
try {
cl = (ClassLoader)AccessController.doPrivileged(
new PrivilegedExceptionAction() {
public Object run() throws ClassNotFoundException {
return Thread.currentThread().getContextClassLoader();
}
}
);
} catch (PrivilegedActionException e) {
if (log.isDebugEnabled()) {
log.debug("Exception thrown from AccessController: " + e);
}
throw ExceptionFactory.makeWebServiceException(e.getException());
}
return cl;
}
/**
* Create a JAX-WS Service Exception (Generated Exception)
*
* @param message
* @param exceptionclass
* @param bean
* @param beanFormalType
* @return
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws InstantiationException
* @throws NoSuchMethodException
* @parma marshalDesc is used to get cached information about the exception class and bean
*/
private static Exception createServiceException(String message,
Class exceptionclass,
Object bean,
Class beanFormalType,
MarshalServiceRuntimeDescription marshalDesc,
boolean isLegacyException) throws
InvocationTargetException, IllegalAccessException, InstantiationException,
NoSuchMethodException {
if (log.isDebugEnabled()) {
log.debug("Constructing JAX-WS Exception:" + exceptionclass);
}
Exception exception = null;
if (isLegacyException) {
// Legacy Exception
exception = LegacyExceptionUtil.createFaultException(exceptionclass,
bean,
marshalDesc);
} else {
// Normal case, use the contstructor to create the exception
Constructor constructor =
exceptionclass.getConstructor(new Class[] { String.class, beanFormalType });
exception = (Exception)constructor.newInstance(new Object[] { message, bean });
}
return exception;
}
/**
* Create a system exception
*
* @param message
* @return
*/
public static ProtocolException createSystemException(XMLFault xmlFault, Message message) {
ProtocolException e = null;
Protocol protocol = message.getProtocol();
String text = xmlFault.getReason().getText();
if (protocol == Protocol.soap11 || protocol == Protocol.soap12) {
// Throw a SOAPFaultException
if (log.isDebugEnabled()) {
log.debug("Constructing SOAPFaultException for " + text);
}
String protocolNS = (protocol == Protocol.soap11) ?
SOAPConstants.URI_NS_SOAP_1_1_ENVELOPE :
SOAPConstants.URI_NS_SOAP_1_2_ENVELOPE;
try {
// The following set of instructions is used to avoid
// some unimplemented methods in the Axis2 SAAJ implementation
javax.xml.soap.MessageFactory mf = SAAJFactory.createMessageFactory(protocolNS);
SOAPBody body = mf.createMessage().getSOAPBody();
SOAPFault soapFault = XMLFaultUtils.createSAAJFault(xmlFault, body);
e = new SOAPFaultException(soapFault);
} catch (Exception ex) {
// Exception occurred during exception processing.
// TODO Probably should do something better here
if (log.isDebugEnabled()) {
log.debug("Exception occurred during fault processing:", ex);
}
e = ExceptionFactory.makeProtocolException(text, null);
}
} else if (protocol == Protocol.rest) {
if (log.isDebugEnabled()) {
log.debug("Constructing ProtocolException for " + text);
}
// TODO Is there an explicit exception for REST
e = ExceptionFactory.makeProtocolException(text, null);
} else if (protocol == Protocol.unknown) {
// REVIEW What should happen if there is no protocol
if (log.isDebugEnabled()) {
log.debug("Constructing ProtocolException for " + text);
}
e = ExceptionFactory.makeProtocolException(text, null);
}
return e;
}
/**
* @param ed
* @return
*/
static MarshalServiceRuntimeDescription getMarshalDesc(EndpointDescription ed) {
ServiceDescription sd = ed.getServiceDescription();
return MarshalServiceRuntimeDescriptionFactory.get(sd);
}
/**
* This probably should be available from the ParameterDescription
*
* @param cls
* @param marshalDesc
* @return true if primitive, wrapper, java.lang.String. Calendar (or GregorianCalendar),
* BigInteger etc or anything other java type that is mapped by the basic schema types
*/
static boolean isNotJAXBRootElement(Class cls, MarshalServiceRuntimeDescription marshalDesc) {
if (cls == String.class ||
cls.isPrimitive() ||
cls == Calendar.class ||
cls == byte[].class ||
cls == GregorianCalendar.class ||
cls == Date.class ||
cls == BigInteger.class ||
cls == BigDecimal.class) {
return true;
}
AnnotationDesc aDesc = marshalDesc.getAnnotationDesc(cls);
if (aDesc != null) {
// XmlRootElementName returns null if @XmlRootElement is not specified
return (aDesc.getXmlRootElementName() == null);
}
return true;
}
/**
* Get a string containing the stack of the specified exception
* @param e
* @return
*/
public static String stackToString(Throwable e) {
java.io.StringWriter sw= new java.io.StringWriter();
java.io.BufferedWriter bw = new java.io.BufferedWriter(sw);
java.io.PrintWriter pw= new java.io.PrintWriter(bw);
e.printStackTrace(pw);
pw.close();
return sw.getBuffer().toString();
}
static boolean isSWAAttachment(ParameterDescription pd) {
return pd.getAttachmentDescription() != null &&
pd.getAttachmentDescription().getAttachmentType() == AttachmentType.SWA;
}
/**
* Register the unmarshalling information so that it can
* be used to speed up subsequent marshalling events.
* @param mc
* @param packages
* @param packagesKey
*/
static void registerUnmarshalInfo(MessageContext mc,
TreeSet<String> packages,
String packagesKey) throws AxisFault {
// The information is registered on the AxisOperation.
if (mc == null ||
mc.getAxisMessageContext() == null ||
mc.getAxisMessageContext().getAxisService() == null ||
mc.getAxisMessageContext().getAxisOperation() == null) {
return;
}
// This needs to be stored on the AxisOperation as unmarshalling
// info will be specific to a method and its parameters
AxisOperation axisOp = mc.getAxisMessageContext().getAxisOperation();
// There are two things that need to be saved.
// 1) The UnmarshalInfo object containing the packages
// (which will be used by the CustomBuilder)
// 2) A MessageContextListener which (when triggered) registers
// the JAXBCustomBuilder
Parameter param = axisOp.getParameter(UnmarshalInfo.KEY);
if (param == null) {
UnmarshalInfo info = new UnmarshalInfo(packages, packagesKey);
axisOp.addParameter(UnmarshalInfo.KEY, info);
param = axisOp.getParameter(UnmarshalInfo.KEY);
param.setTransient(true);
// Add a listener that will set the JAXBCustomBuilder
UnmarshalMessageContextListener.
create(mc.getAxisMessageContext().getServiceContext());
}
}
}