/* * 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.apache.axis.wsdl.toJava.Utils; 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 = Utils.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: // // Christmas // Xmas // // 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