| RPCInvocation.java |
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2001-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Axis" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.jboss.axis.providers.java;
import org.jboss.axis.AxisFault;
import org.jboss.axis.Constants;
import org.jboss.axis.Message;
import org.jboss.axis.MessageContext;
import org.jboss.axis.MessagePart;
import org.jboss.axis.description.OperationDesc;
import org.jboss.axis.description.ParameterDesc;
import org.jboss.axis.description.ServiceDesc;
import org.jboss.axis.enums.Style;
import org.jboss.axis.enums.Use;
import org.jboss.axis.handlers.soap.SOAPService;
import org.jboss.axis.message.RPCElement;
import org.jboss.axis.message.RPCHeaderParam;
import org.jboss.axis.message.RPCParam;
import org.jboss.axis.message.RPCParamElementImpl;
import org.jboss.axis.message.SOAPBodyElementAxisImpl;
import org.jboss.axis.message.SOAPElementAxisImpl;
import org.jboss.axis.message.SOAPEnvelopeAxisImpl;
import org.jboss.axis.soap.SOAPConstants;
import org.jboss.axis.utils.JavaUtils;
import org.jboss.axis.utils.Messages;
import org.jboss.logging.Logger;
import org.w3c.dom.Element;
import javax.activation.DataHandler;
import javax.xml.namespace.QName;
import javax.xml.rpc.JAXRPCException;
import javax.xml.rpc.holders.Holder;
import javax.xml.soap.AttachmentPart;
import javax.xml.soap.SOAPHeaderElement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
/**
* Implement message processing by walking over RPCElements of the
* envelope body, invoking the appropriate methods on the service object.
*
* @author Doug Davis (dug@us.ibm.com)
*/
public class RPCInvocation
{
private static Logger log = Logger.getLogger(RPCInvocation.class.getName());
private RPCProvider rpcProvider;
private MessageContext messageContext;
private SOAPEnvelopeAxisImpl requestEnvelope;
private SOAPEnvelopeAxisImpl responseEnvelope;
private Object targetObject;
private OperationDesc operation;
private Object[] argValues;
private Object returnValue;
private RPCElement body;
private List outParams;
// A flag that indicates thaat the response envelope has already been prepared
private boolean responsePrepared;
public RPCInvocation(RPCProvider rpcProvider, MessageContext messageContext, SOAPEnvelopeAxisImpl reqEnv, SOAPEnvelopeAxisImpl resEnv, Object targetObject)
{
this.rpcProvider = rpcProvider;
this.messageContext = messageContext;
this.requestEnvelope = reqEnv;
this.responseEnvelope = resEnv;
this.targetObject = targetObject;
}
/**
* Copy constructor
*/
public RPCInvocation(RPCInvocation invocation)
{
this.rpcProvider = invocation.rpcProvider;
this.messageContext = invocation.messageContext;
this.requestEnvelope = invocation.requestEnvelope;
this.responseEnvelope = invocation.responseEnvelope;
this.targetObject = invocation.targetObject;
this.responsePrepared = invocation.responsePrepared;
}
public RPCProvider getProvider()
{
return rpcProvider;
}
/**
* Prepare the invocation object from the given prameters
*/
public void prepareFromRequestEnvelope()
{
log.debug("Enter: prepareFromRequestEnvelope\n" + requestEnvelope);
SOAPService service = messageContext.getService();
ServiceDesc serviceDesc = service.getServiceDescription();
operation = messageContext.getOperation();
try
{
Vector bodies = requestEnvelope.getBodyElements();
if (log.isDebugEnabled())
{
log.debug(Messages.getMessage("bodyElems00", "" + bodies.size()));
log.debug(Messages.getMessage("bodyIs00", "" + bodies.get(0)));
}
// Find the first "root" body element, which is the RPC call.
for (int bNum = 0; body == null && bNum < bodies.size(); bNum++)
{
// If this is a regular old SOAPBodyElement, and it's a root,
// we're probably a non-wrapped doc/lit service. In this case,
// we deserialize the element, and create an RPCElement "wrapper"
// around it which points to the correct method.
// FIXME : There should be a cleaner way to do this...
if (!(bodies.get(bNum) instanceof RPCElement))
{
SOAPBodyElementAxisImpl bodyEl = (SOAPBodyElementAxisImpl)bodies.get(bNum);
// igors: better check if bodyEl.getID() != null
// to make sure this loop does not step on SOAP-ENC objects
// that follow the parameters! FIXME?
if (bodyEl.isRoot() && operation != null && bodyEl.getID() == null)
{
ParameterDesc param = operation.getParameter(bNum);
// at least do not step on non-existent parameters!
if (param != null)
{
Object val = bodyEl.getValueAsType(param.getTypeQName());
body = new RPCElement("",
operation.getName(),
new Object[]{val});
}
}
}
else
{
body = (RPCElement)bodies.get(bNum);
}
}
// special case code for a document style operation with no
// arguments (which is a strange thing to have, but whatever)
if (body == null)
{
// throw an error if this isn't a document style service
if (!(serviceDesc.getStyle().equals(Style.DOCUMENT)))
{
throw new JAXRPCException(Messages.getMessage("noBody00"));
}
// look for a method in the service that has no arguments,
// use the first one we find.
ArrayList ops = serviceDesc.getOperations();
for (Iterator iterator = ops.iterator(); iterator.hasNext();)
{
OperationDesc desc = (OperationDesc)iterator.next();
if (desc.getNumInParams() == 0)
{
// found one with no parameters, use it
messageContext.setOperation(desc);
// create an empty element
body = new RPCElement(desc.getName());
// stop looking
break;
}
}
// If we still didn't find anything, report no body error.
if (body == null)
{
throw new JAXRPCException(Messages.getMessage("noBody00"));
}
}
String methodName = body.getMethodName();
Vector args = body.getParams();
int numArgs = args.size();
// This may have changed, so get it again...
// FIXME (there should be a cleaner way to do this)
operation = messageContext.getOperation();
if (operation == null)
{
QName qname = new QName(body.getNamespaceURI(),
body.getName());
operation = getOperation(serviceDesc, qname);
}
if (operation == null)
{
// BP R2725
// As specified by the SOAP processing model, (a) a "VersionMismatch" faultcode must be generated if the
// namespace of the "Envelope" element is incorrect, (b) a "MustUnderstand" fault must be generated if the
// instance does not understand a SOAP header block with a value of "1" for the soap:mustUnderstand attribute.
// In all other cases where a message is inconsistent with its WSDL description, a fault with a "Client"
// faultcode should be generated.
// [TDI] 17-Aug-2004 com/sun/ts/tests/jaxrpc/wsi/j2w/rpc/literal/R2725
Vector headers = requestEnvelope.getHeaders();
for (int i = 0; i < headers.size(); i++)
{
SOAPHeaderElement header = (SOAPHeaderElement)headers.elementAt(i);
if (header.getMustUnderstand() == true)
{
AxisFault fault = new AxisFault(Messages.getMessage("noSuchOperation", methodName));
fault.setFaultCode(Constants.FAULT_MUSTUNDERSTAND);
// This hack wrapps the checked AxisException in a RuntimeException
// so we don't loose the fault code, etc
// [TDI] 17-Aug-2004
throw new JAXRPCException(fault);
}
}
throw new JAXRPCException(Messages.getMessage("noSuchOperation", methodName));
}
// Create the array we'll use to hold the actual parameter
// values. We know how big to make it from the metadata.
argValues = new Object[operation.getNumParams()];
// A place to keep track of the out params (INOUTs and OUTs)
outParams = new ArrayList();
// Check if we have and endpoint of type doStuff(org.w3c.dom.Element)
// In that case we pass the entire SOAPBodyElement to the endpoint
boolean isGenericDocumentEndpoint = false;
Style style = serviceDesc.getStyle();
Use use = serviceDesc.getUse();
if (style.equals(Style.DOCUMENT) && use.equals(Use.LITERAL) && operation.getInParams().size() == 1)
{
ParameterDesc firstInParam = (ParameterDesc)operation.getInParams().get(0);
if (Element.class.isAssignableFrom(firstInParam.getJavaType()))
{
isGenericDocumentEndpoint = true;
argValues[0] = body;
}
}
// Put the values contained in the RPCParams into an array
// suitable for passing to java.lang.reflect.Method.invoke()
// Make sure we respect parameter ordering if we know about it
// from metadata, and handle whatever conversions are necessary
// (values -> Holders, etc)
if (isGenericDocumentEndpoint == false)
{
for (int i = 0; i < numArgs; i++)
{
RPCParam rpcParam = (RPCParam)args.get(i);
Object value = rpcParam.getValue();
// first check the type on the paramter
ParameterDesc paramDesc = rpcParam.getParamDesc();
// if we found some type info try to make sure the value type is
// correct. For instance, if we deserialized a xsd:dateTime in
// to a Calendar and the service takes a Date, we need to convert
if (paramDesc != null && paramDesc.getJavaType() != null)
{
// Get the type in the signature (java type or its holder)
Class sigType = paramDesc.getJavaType();
// Get the content of an attachement part
if (value instanceof AttachmentPart)
{
if (sigType.equals(DataHandler.class))
value = ((AttachmentPart)value).getDataHandler();
else
value = ((AttachmentPart)value).getContent();
}
// Convert the value into the expected type in the signature
value = JavaUtils.convert(value, sigType);
rpcParam.setValue(value);
if (paramDesc.getMode() == ParameterDesc.INOUT)
{
outParams.add(rpcParam);
}
}
// Put the value (possibly converted) in the argument array
// make sure to use the parameter order if we have it
if (paramDesc == null || paramDesc.getOrder() == -1)
{
argValues[i] = value;
}
else
{
argValues[paramDesc.getOrder()] = value;
}
if (log.isDebugEnabled())
{
log.debug(" " + Messages.getMessage("value00", "" + argValues[i]));
}
}
}
// See if any subclasses want a crack at faulting on a bad operation
// FIXME : Does this make sense here???
String allowedMethods = (String)service.getOption("allowedMethods");
rpcProvider.checkMethodName(messageContext, allowedMethods, operation.getName());
// Now create any out holders we need to pass in
int count = numArgs;
for (int i = 0; i < argValues.length; i++)
{
// We are interested only in OUT/INOUT
ParameterDesc param = operation.getParameter(i);
if (param.getMode() == ParameterDesc.IN)
continue;
Class holderClass = param.getJavaType();
if (holderClass != null &&
Holder.class.isAssignableFrom(holderClass))
{
int index = count;
if (param.getMode() == ParameterDesc.OUT)
{
// OUT params don't have param order, just stick them at the end.
count++;
}
else if (param.getMode() == ParameterDesc.INOUT)
{
// Use the parameter order if specified or just stick them to the end.
if (param.getOrder() != -1)
{
index = param.getOrder();
}
else
{
count++;
}
// If it's already filled, don't muck with it
if (argValues[index] != null)
{
continue;
}
}
argValues[index] = holderClass.newInstance();
// Store an RPCParam in the outs collection so we
// have an easy and consistent way to write these
// back to the client below
RPCParam p = new RPCParam(param.getQName(),
argValues[index]);
p.setParamDesc(param);
outParams.add(p);
}
else
{
String message = Messages.getMessage("badOutParameter00", "" + param.getQName(), operation.getName());
log.error(message);
throw new JAXRPCException(message);
}
}
log.debug("Exit: prepareFromRequestEnvelope");
}
catch (JAXRPCException e)
{
log.error(e.toString(), e);
throw e;
}
catch (Exception e)
{
log.error(e.toString(), e);
throw new JAXRPCException(e);
}
}
public OperationDesc getOperation(ServiceDesc serviceDesc, QName qname)
{
return serviceDesc.getOperationByElementQName(qname);
}
/**
* Prepare the response envelope from the given parameters
*/
public void prepareResponseEnvelope(Object resObject)
{
returnValue = resObject;
log.debug("Enter: prepareResponseEnvelope: [resObject=" + resObject + "]");
// There is no way we can get a new resObj from the response SOAPEnvelope.
// Sucker is the response handler that modifies the response value.
// The RPCInvocation will ignore another call to prepareResponseEnvelope
// because this could potentially overwrite what the handlers did.
if (responsePrepared)
{
log.debug("Ignoring request to prepare response envelope");
log.debug("Exit: prepareResponseEnvelope\n" + responseEnvelope);
return;
}
SOAPService service = messageContext.getService();
ServiceDesc serviceDesc = service.getServiceDescription();
ArrayList immutableElements = new ArrayList();
try
{
// Set the encoding style - in contradiction to BP-1.0/R1005
// s1as interop tests expect this to be set in the <env:Body>
// [TDI 11-Sep-2004]
String encodingStyle = messageContext.getEncodingStyle();
String bodyEncStyle = responseEnvelope.getBody().getEncodingStyle();
if (bodyEncStyle.equals(encodingStyle) == false)
{
if (bodyEncStyle.equals(""))
responseEnvelope.getBody().setEncodingStyle(encodingStyle);
else
log.warn("Mixed encoding styles are not supported: " + bodyEncStyle + "!=" + encodingStyle);
}
RPCElement resBody = null;
// Check if we have and endpoint of type org.w3c.dom.Element doStuff(...)
// In that case the resObject is the SOAPBodyElement
boolean isGenericDocumentReturn = false;
Style style = serviceDesc.getStyle();
Use use = serviceDesc.getUse();
if (style.equals(Style.DOCUMENT) && use.equals(Use.LITERAL) && resObject != null && Element.class.isAssignableFrom(resObject.getClass()))
{
Element resElement = (Element)resObject;
resBody = new RPCElement(resElement.getLocalName());
resBody.setPrefix(resElement.getPrefix());
resBody.setNamespaceURI(resElement.getNamespaceURI());
immutableElements.add(resBody);
isGenericDocumentReturn = true;
}
/* Now put the result in the result SOAPEnvelope */
/*************************************************/
if (isGenericDocumentReturn == false)
{
String methodName = operation.getMethod().getName();
resBody = new RPCElement(methodName + "Response");
resBody.setPrefix(body.getPrefix());
resBody.setNamespaceURI(body.getNamespaceURI());
immutableElements.add(resBody);
}
// Return first
if (operation.getMethod().getReturnType() != Void.TYPE)
{
QName returnQName = operation.getReturnQName();
if (returnQName == null)
{
String methodName = operation.getMethod().getName();
returnQName = new QName(body.getNamespaceURI(), methodName + "Response");
}
ParameterDesc retParamDesc = operation.getReturnParamDesc();
QName retTypeQName = retParamDesc.getTypeQName();
if (resObject != null && retTypeQName != null)
{
Class retType = messageContext.getTypeMapping().getClassForQName(retTypeQName);
if (retType != null && JavaUtils.isConvertable(resObject, retType))
resObject = JavaUtils.convert(resObject, retType);
}
RPCParam param = new RPCParam(returnQName, resObject);
param.setParamDesc(retParamDesc);
param.setRPCCall(resBody);
// Added to include the user type in the respone message
// Otherwise the client does not use the BeanDeserializer
// com/sun/ts/tests/webservices/wsdlImport/file/nested1#invokeNestedImportWsdl
if (operation.getUse().equals(Use.LITERAL))
param.setXSITypeGeneration(Boolean.TRUE);
// Added to build the soap tree such that it contains SOAPElements for the parameters
// Revisit to do this properly for the different encoding styles, arrays, value types, etc.
// TDI 06-June-2006
RPCParamElementImpl paramElement = new RPCParamElementImpl(param);
resBody.addChildElement(paramElement);
immutableElements.add(paramElement);
if (!operation.isReturnHeader())
{
// For SOAP 1.2 rpc style, add a result
if (messageContext.getSOAPConstants() == SOAPConstants.SOAP12_CONSTANTS &&
(serviceDesc.getStyle().equals(Style.RPC)))
{
RPCParam resultParam = new RPCParam(Constants.QNAME_RPC_RESULT, returnQName);
resultParam.setXSITypeGeneration(Boolean.FALSE);
resBody.addParam(resultParam);
}
resBody.addParam(param);
}
else
{
responseEnvelope.addHeader(new RPCHeaderParam(param));
}
}
// Then any other out params
if (!outParams.isEmpty())
{
for (Iterator i = outParams.iterator(); i.hasNext();)
{
// We know this has a holder, so just unwrap the value
RPCParam param = (RPCParam)i.next();
Object value = param.getValue();
if (value instanceof Holder)
{
Holder holder = (Holder)value;
value = JavaUtils.getHolderValue(holder);
}
ParameterDesc paramDesc = param.getParamDesc();
param.setValue(value);
if (paramDesc != null && paramDesc.isOutHeader())
{
responseEnvelope.addHeader(new RPCHeaderParam(param));
}
else
{
resBody.addParam(param);
}
}
}
responseEnvelope.addBodyElement(resBody);
responsePrepared = true;
// Make the added elements immutable
Iterator it = immutableElements.iterator();
while (it.hasNext())
{
SOAPElementAxisImpl soapElement = (SOAPElementAxisImpl)it.next();
soapElement.setImmutable(true);
}
// Put the response envelope in the SOAPPart
messageContext.setPastPivot(true);
Message respMessage = (Message)messageContext.getMessage();
((MessagePart)respMessage.getSOAPPart()).setSOAPEnvelope(responseEnvelope);
log.debug("Exit: prepareResponseEnvelope\n" + responseEnvelope);
}
catch (JAXRPCException e)
{
throw e;
}
catch (Exception e)
{
throw new JAXRPCException(e);
}
}
public Object[] getArgValues()
{
return argValues;
}
public RPCElement getBody()
{
return body;
}
public MessageContext getMessageContext()
{
return messageContext;
}
public OperationDesc getOperation()
{
return operation;
}
public List getOutParams()
{
return outParams;
}
public SOAPEnvelopeAxisImpl getRequestEnvelope()
{
return requestEnvelope;
}
public SOAPEnvelopeAxisImpl getResponseEnvelope()
{
return responseEnvelope;
}
public Object getTargetObject()
{
return targetObject;
}
public String toString()
{
StringBuffer ret = new StringBuffer("\nRPCInvocation:\n");
ret.append("TargetObject=" + targetObject + "\n");
ret.append("Operation=" + operation);
ret.append("ArgValues=" + (argValues != null ? Arrays.asList(argValues) : null) + "\n");
ret.append("ReturnValue=" + returnValue + "\n");
return ret.toString();
}
}
| RPCInvocation.java |