| /* |
| * Copyright 2001-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.axis.message; |
| |
| import org.apache.axis.AxisFault; |
| import org.apache.axis.Constants; |
| import org.apache.axis.Message; |
| import org.apache.axis.MessageContext; |
| import org.apache.axis.description.OperationDesc; |
| import org.apache.axis.description.ParameterDesc; |
| import org.apache.axis.description.ServiceDesc; |
| import org.apache.axis.encoding.DeserializationContext; |
| import org.apache.axis.encoding.SerializationContext; |
| import org.apache.axis.constants.Style; |
| import org.apache.axis.constants.Use; |
| import org.apache.axis.handlers.soap.SOAPService; |
| import org.apache.axis.soap.SOAPConstants; |
| import org.apache.axis.utils.JavaUtils; |
| import org.apache.axis.utils.Messages; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.SAXException; |
| |
| import javax.xml.namespace.QName; |
| import javax.xml.soap.SOAPElement; |
| import java.util.ArrayList; |
| import java.util.Enumeration; |
| import java.util.Vector; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Collection; |
| |
| public class RPCElement extends SOAPBodyElement |
| { |
| //protected Vector params2 = new Vector(); |
| protected boolean needDeser = false; |
| OperationDesc [] operations = null; |
| |
| public RPCElement(String namespace, |
| String localName, |
| String prefix, |
| Attributes attributes, |
| DeserializationContext context, |
| OperationDesc [] operations) throws AxisFault |
| { |
| super(namespace, localName, prefix, attributes, context); |
| |
| // This came from parsing XML, so we need to deserialize it sometime |
| needDeser = true; |
| |
| // Obtain our possible operations |
| if (operations == null) { |
| updateOperationsByName(); |
| } else { |
| this.operations = operations; |
| } |
| } |
| |
| public RPCElement(String namespace, String methodName, Object [] args) |
| { |
| this.setNamespaceURI(namespace); |
| this.name = methodName; |
| |
| for (int i = 0; args != null && i < args.length; i++) { |
| if (args[i] instanceof RPCParam) { |
| addParam((RPCParam)args[i]); |
| } else { |
| String name = null; |
| if (name == null) name = "arg" + i; |
| addParam(new RPCParam(namespace, name, args[i])); |
| } |
| } |
| } |
| |
| public RPCElement(String methodName) |
| { |
| this.name = methodName; |
| } |
| |
| public void updateOperationsByName() throws AxisFault |
| { |
| if (context == null) { |
| return; |
| } |
| |
| MessageContext msgContext = context.getMessageContext(); |
| |
| if (msgContext == null) { |
| return; |
| } |
| |
| // Obtain our possible operations |
| SOAPService service = msgContext.getService(); |
| if (service == null) { |
| return; |
| } |
| |
| ServiceDesc serviceDesc = |
| service.getInitializedServiceDesc(msgContext); |
| |
| String lc = JavaUtils.xmlNameToJava(name); |
| if (serviceDesc == null) { |
| throw AxisFault.makeFault( |
| new ClassNotFoundException( |
| Messages.getMessage("noClassForService00", |
| lc))); |
| } |
| |
| this.operations = serviceDesc.getOperationsByName(lc); |
| } |
| |
| public void updateOperationsByQName() throws AxisFault |
| { |
| if (context == null) { |
| return; |
| } |
| |
| MessageContext msgContext = context.getMessageContext(); |
| |
| if (msgContext == null) { |
| return; |
| } |
| |
| this.operations = msgContext.getPossibleOperationsByQName(getQName()); |
| } |
| |
| public OperationDesc[] getOperations() |
| { |
| return this.operations; |
| } |
| |
| public String getMethodName() |
| { |
| return name; |
| } |
| |
| public void setNeedDeser(boolean needDeser) { |
| this.needDeser = needDeser; |
| } |
| |
| public void deserialize() throws SAXException |
| { |
| needDeser = false; |
| |
| MessageContext msgContext = context.getMessageContext(); |
| |
| // Figure out if we should be looking for out params or in params |
| // (i.e. is this message a response?) |
| Message msg = msgContext.getCurrentMessage(); |
| SOAPConstants soapConstants = msgContext.getSOAPConstants(); |
| |
| boolean isResponse = ((msg != null) && |
| Message.RESPONSE.equals(msg.getMessageType())); |
| |
| // We're going to need this below, so create one. |
| RPCHandler rpcHandler = new RPCHandler(this, isResponse); |
| |
| if (operations != null) { |
| int numParams = (getChildren() == null) ? 0 : getChildren().size(); |
| |
| SAXException savedException = null; |
| |
| // By default, accept missing parameters as nulls, and |
| // allow the message context to override. |
| boolean acceptMissingParams = msgContext.isPropertyTrue( |
| MessageContext.ACCEPTMISSINGPARAMS, |
| true); |
| |
| // We now have an array of all operations by this name. Try to |
| // find the right one. For each matching operation which has an |
| // equal number of "in" parameters, try deserializing. If we |
| // don't succeed for any of the candidates, punt. |
| |
| for (int i = 0; i < operations.length; i++) { |
| OperationDesc operation = operations[i]; |
| |
| // See if any information is coming from a header |
| boolean needHeaderProcessing = |
| needHeaderProcessing(operation, isResponse); |
| |
| // Make a quick check to determine if the operation |
| // could be a match. |
| // 1) The element is the first param, DOCUMENT, (i.e. |
| // don't know the operation name or the number |
| // of params, so try all operations). |
| // or (2) Style is literal |
| // If the Style is LITERAL, the numParams may be inflated |
| // as in the following case: |
| // <getAttractions xmlns="urn:CityBBB"> |
| // <attname>Christmas</attname> |
| // <attname>Xmas</attname> |
| // </getAttractions> |
| // for getAttractions(String[] attName) |
| // numParams will be 2 and and operation.getNumInParams=1 |
| // or (3) Number of expected params is |
| // >= num params in message |
| if (operation.getStyle() == Style.DOCUMENT || |
| operation.getStyle() == Style.WRAPPED || |
| operation.getUse() == Use.LITERAL || |
| (acceptMissingParams ? |
| (operation.getNumInParams() >= numParams) : |
| (operation.getNumInParams() == numParams))) { |
| |
| boolean isEncoded = operation.getUse() == Use.ENCODED; |
| rpcHandler.setOperation(operation); |
| try { |
| // If no operation name and more than one |
| // parameter is expected, don't |
| // wrap the rpcHandler in an EnvelopeHandler. |
| if ( ( msgContext.isClient() && |
| operation.getStyle() == Style.DOCUMENT ) || |
| ( !msgContext.isClient() && |
| operation.getStyle() == Style.DOCUMENT && |
| operation.getNumInParams() > 0 ) ) { context.pushElementHandler(rpcHandler); |
| context.setCurElement(null); |
| } else { |
| context.pushElementHandler( |
| new EnvelopeHandler(rpcHandler)); |
| context.setCurElement(this); |
| } |
| |
| publishToHandler((org.xml.sax.ContentHandler) context); |
| |
| // If parameter values are located in headers, |
| // get the information and publish the header |
| // elements to the rpc handler. |
| if (needHeaderProcessing) { |
| processHeaders(operation, isResponse, |
| context, rpcHandler); |
| } |
| |
| // Check if the RPCParam's value match the signature of the |
| // param in the operation. |
| boolean match = true; |
| List params = getParams2(); |
| for ( int j = 0 ; j < params.size() && match ; j++ ) { |
| RPCParam rpcParam = (RPCParam)params.get(j); |
| Object value = rpcParam.getObjectValue(); |
| |
| // 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(); |
| |
| // if the type is an array but the value is not |
| // an array or Collection, put it into an |
| // ArrayList so that we correctly recognize it |
| // as convertible |
| if (sigType.isArray()) { |
| if (value != null && |
| JavaUtils.isConvertable(value, |
| sigType.getComponentType()) && |
| !(value.getClass().isArray()) && |
| !(value instanceof Collection)) { |
| ArrayList list = new ArrayList(); |
| list.add(value); |
| value = list; |
| rpcParam.setObjectValue(value); |
| } |
| } |
| |
| if(!JavaUtils.isConvertable(value, sigType, isEncoded)) |
| match = false; |
| } |
| } |
| // This is not the right operation, try the next one. |
| if(!match) { |
| children = new ArrayList(); |
| continue; |
| } |
| |
| // Success!! This is the right one... |
| msgContext.setOperation(operation); |
| return; |
| } catch (SAXException e) { |
| // If there was a problem, try the next one. |
| savedException = e; |
| children = new ArrayList(); |
| continue; |
| } catch (AxisFault e) { |
| // Thrown by getHeadersByName... |
| // If there was a problem, try the next one. |
| savedException = new SAXException(e); |
| children = new ArrayList(); |
| continue; |
| } |
| } |
| } |
| |
| // If we're SOAP 1.2, getting to this point means bad arguments. |
| if (!msgContext.isClient() && soapConstants == SOAPConstants.SOAP12_CONSTANTS) { |
| AxisFault fault = new AxisFault(Constants.FAULT_SOAP12_SENDER, "string", null, null); |
| fault.addFaultSubCode(Constants.FAULT_SUBCODE_BADARGS); |
| throw new SAXException(fault); |
| } |
| |
| if (savedException != null) { |
| throw savedException; |
| } else if (!msgContext.isClient()) { |
| QName faultCode = new QName(Constants.FAULT_SERVER_USER); |
| if (soapConstants == SOAPConstants.SOAP12_CONSTANTS) |
| faultCode = Constants.FAULT_SOAP12_SENDER; |
| AxisFault fault = new AxisFault(faultCode, |
| null, Messages.getMessage("noSuchOperation", name), null, null, null); |
| |
| throw new SAXException(fault); |
| } |
| } |
| |
| if (operations != null) { |
| rpcHandler.setOperation(operations[0]); |
| } |
| |
| // Same logic as above. Don't wrap rpcHandler |
| // if there is no operation wrapper in the message |
| if (operations != null && operations.length > 0 && |
| (operations[0].getStyle() == Style.DOCUMENT)) { |
| context.pushElementHandler(rpcHandler); |
| context.setCurElement(null); |
| } else { |
| context.pushElementHandler(new EnvelopeHandler(rpcHandler)); |
| context.setCurElement(this); |
| } |
| |
| publishToHandler((org.xml.sax.ContentHandler)context); |
| } |
| |
| private List getParams2() { |
| return getParams(new ArrayList()); |
| } |
| |
| private List getParams(List list) { |
| for (int i = 0; children != null && i < children.size(); i++) { |
| Object child = children.get(i); |
| if (child instanceof RPCParam) { |
| list.add(child); |
| } |
| } |
| return list; |
| } |
| |
| /** This gets the FIRST param whose name matches. |
| * !!! Should it return more in the case of duplicates? |
| */ |
| public RPCParam getParam(String name) throws SAXException |
| { |
| if (needDeser) { |
| deserialize(); |
| } |
| |
| List params = getParams2(); |
| for (int i = 0; i < params.size(); i++) { |
| RPCParam param = (RPCParam)params.get(i); |
| if (param.getName().equals(name)) |
| return param; |
| } |
| |
| return null; |
| } |
| |
| public Vector getParams() throws SAXException |
| { |
| if (needDeser) { |
| deserialize(); |
| } |
| |
| return (Vector)getParams(new Vector()); |
| } |
| |
| public void addParam(RPCParam param) |
| { |
| param.setRPCCall(this); |
| initializeChildren(); |
| children.add(param); |
| } |
| |
| protected void outputImpl(SerializationContext context) throws Exception |
| { |
| MessageContext msgContext = context.getMessageContext(); |
| boolean hasOperationElement = |
| (msgContext == null || |
| msgContext.getOperationStyle() == Style.RPC || |
| msgContext.getOperationStyle() == Style.WRAPPED); |
| |
| // When I have MIME and a no-param document WSDL, if I don't check |
| // for no params here, the server chokes with "can't find Body". |
| // because it will be looking for the enclosing element always |
| // found in an RPC-style (and wrapped) request |
| boolean noParams = getParams2().size() == 0; |
| |
| if (hasOperationElement || noParams) { |
| // Set default namespace if appropriate (to avoid prefix mappings |
| // in literal style). Do this only if there is no encodingStyle. |
| if (encodingStyle != null && encodingStyle.equals("")) { |
| context.registerPrefixForURI("", getNamespaceURI()); |
| } |
| context.startElement(new QName(getNamespaceURI(), name), attributes); |
| } |
| |
| if(noParams) { |
| if (children != null) { |
| for (Iterator it = children.iterator(); it.hasNext();) { |
| ((NodeImpl)it.next()).output(context); |
| } |
| } |
| } else { |
| List params = getParams2(); |
| for (int i = 0; i < params.size(); i++) { |
| RPCParam param = (RPCParam)params.get(i); |
| if (!hasOperationElement && encodingStyle != null && encodingStyle.equals("")) { |
| context.registerPrefixForURI("", param.getQName().getNamespaceURI()); |
| } |
| param.serialize(context); |
| } |
| } |
| |
| if (hasOperationElement || noParams) { |
| context.endElement(); |
| } |
| } |
| |
| /** |
| * needHeaderProcessing |
| * @param operation OperationDesc |
| * @param isResponse boolean indicates if request or response message |
| * @return true if the operation description indicates parameters/results |
| * are located in the soap header. |
| */ |
| private boolean needHeaderProcessing(OperationDesc operation, |
| boolean isResponse) { |
| |
| // Search parameters/return to see if any indicate |
| // that instance data is contained in the header. |
| ArrayList paramDescs = operation.getParameters(); |
| if (paramDescs != null) { |
| for (int j=0; j<paramDescs.size(); j++) { |
| ParameterDesc paramDesc = |
| (ParameterDesc) paramDescs.get(j); |
| if ((!isResponse && paramDesc.isInHeader()) || |
| (isResponse && paramDesc.isOutHeader())) { |
| return true; |
| } |
| } |
| } |
| if (isResponse && |
| operation.getReturnParamDesc() != null && |
| operation.getReturnParamDesc().isOutHeader()) { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * needHeaderProcessing |
| * @param operation OperationDesc |
| * @param isResponse boolean indicates if request or response message |
| * @param context DeserializationContext |
| * @param handler RPCHandler used to deserialize parameters |
| * are located in the soap header. |
| */ |
| private void processHeaders(OperationDesc operation, |
| boolean isResponse, |
| DeserializationContext context, |
| RPCHandler handler) |
| throws AxisFault, SAXException |
| { |
| // Inform handler that subsequent elements come from |
| // the header |
| try { |
| handler.setHeaderElement(true); |
| // Get the soap envelope |
| SOAPElement envelope = getParentElement(); |
| while (envelope != null && |
| !(envelope instanceof SOAPEnvelope)) { |
| envelope = envelope.getParentElement(); |
| } |
| if (envelope == null) |
| return; |
| |
| // Find parameters that have instance |
| // data in the header. |
| ArrayList paramDescs = operation.getParameters(); |
| if (paramDescs != null) { |
| for (int j=0; j<paramDescs.size(); j++) { |
| ParameterDesc paramDesc = |
| (ParameterDesc) paramDescs.get(j); |
| if ((!isResponse && paramDesc.isInHeader()) || |
| (isResponse && paramDesc.isOutHeader())) { |
| // Get the headers that match the parameter's |
| // QName |
| Enumeration headers = ((SOAPEnvelope) envelope). |
| getHeadersByName( |
| paramDesc.getQName().getNamespaceURI(), |
| paramDesc.getQName().getLocalPart(), |
| true); |
| // Publish each of the found elements to the |
| // handler. The pushElementHandler and |
| // setCurElement calls are necessary to |
| // have the message element recognized as a |
| // child of the RPCElement. |
| while(headers != null && |
| headers.hasMoreElements()) { |
| context.pushElementHandler(handler); |
| context.setCurElement(null); |
| ((MessageElement) headers.nextElement()). |
| publishToHandler( |
| (org.xml.sax.ContentHandler)context); |
| } |
| } |
| } |
| } |
| |
| // Now do the same processing for the return parameter. |
| if (isResponse && |
| operation.getReturnParamDesc() != null && |
| operation.getReturnParamDesc().isOutHeader()) { |
| ParameterDesc paramDesc = operation.getReturnParamDesc(); |
| Enumeration headers = |
| ((SOAPEnvelope) envelope). |
| getHeadersByName( |
| paramDesc.getQName().getNamespaceURI(), |
| paramDesc.getQName().getLocalPart(), |
| true); |
| while(headers != null && |
| headers.hasMoreElements()) { |
| context.pushElementHandler(handler); |
| context.setCurElement(null); |
| |
| ((MessageElement) headers.nextElement()). |
| publishToHandler((org.xml.sax.ContentHandler)context); |
| } |
| } |
| } finally { |
| handler.setHeaderElement(false); |
| } |
| } |
| } |