gdaniels 02/03/22 08:08:32 Modified: java TODO.txt java/src/org/apache/axis/client Call.java java/src/org/apache/axis/deployment/wsdd WSDDOperation.java WSDDService.java java/src/org/apache/axis/description OperationDesc.java ServiceDesc.java java/src/org/apache/axis/handlers/soap SOAPService.java java/src/org/apache/axis/message RPCElement.java RPCHandler.java RPCParam.java java/src/org/apache/axis/providers/java RPCProvider.java java/test/RPCDispatch Service.java TestSerializedRPC.java java/test/encoding TestArrayListConversions.java Added: java/src/org/apache/axis/deployment/wsdd WSDDParameter.java java/src/org/apache/axis/description ParameterDesc.java Removed: java/src/org/apache/axis/description Parameter.java Log: Metadata improvements, moving towards a more integrated/streamlined model. * Replace Call's internal vectors of param names/types with an OperationDesc * Flesh out OperationDesc/ParameterDesc/ServiceDesc to provide more of what we need * We now try to match parameters by name FIRST, and only then fall back to position if we can't get a match. * Add a test of sending reversed parameters in TestSerializedRPC to make sure this works. * Introspection is now (almost) centralized in ServiceDesc. Next step is to get the RPCProvider fully using the OperationDescs instead of introspecting itself. * Implement <wsdlFile> element for WSDD services, which allows the deployer to specify a static WSDL file which should be returned instead of dynamically creating one. * Clean up TestArrayListConversions - since all introspection now takes place once the first time it's needed, this means all typemappings must be set up before that point. Move typemapping set up into the test constructor. I'm inclined to make adding typemappings after a service has been called a fault, but this is open for discussion. * Update TODO list a bit - clean out finished items * Add WSDDParameter to enable WSDD specification of parameters: <service name="foo" ...> <operation name="testMe"> <parameter qname="foo:Bar" xmlns:foo="myNS" type="xsd:string"/> ... </operation> </service> This will all be cleaner and better integrated in the next round, but I need to get to work and this version gets us part way there and passes all the tests. Next steps are to get rid of Skeletons, and have the deploywriter emit WSDD metadata for the operations on generated services. Revision Changes Path 1.25 +8 -23 xml-axis/java/TODO.txt Index: TODO.txt =================================================================== RCS file: /home/cvs/xml-axis/java/TODO.txt,v retrieving revision 1.24 retrieving revision 1.25 diff -u -r1.24 -r1.25 --- TODO.txt 15 Mar 2002 13:40:08 -0000 1.24 +++ TODO.txt 22 Mar 2002 16:08:31 -0000 1.25 @@ -8,25 +8,15 @@ * <Glen> Write TO DO list -MUST DO FOR BETA1 ------------------ -X <Doug> Doug's issue 1: Attachments are broken -X <Russell> Published interfaces must be vetted. -X <Glen> getParameterName() should return a QName -X <Russell> Make sure no English-language strings are left in the code. - MESSAGE PARSING / ENCODING -------------------------- -X <Glen/Rich> Support different encodingStyles ! <> Support literalxml encodingStyle -X <Rich> Multi-dimensional array support CLIENT API ---------- JAX-RPC COMPATIBILITY --------------------- -X <Rich> Implement serializer/deserializer framework compliance PERFORMANCE ----------- @@ -38,7 +28,6 @@ SOAP SPEC COMPLIANCE -------------------- -X <Glen> Implement support for the actor attribute SAMPLES ------- @@ -47,7 +36,6 @@ WSDL2Java --------- * <> (ongoing) JAX-RPC compliance. In particular: -X <Russell> Generate an error for notification and solicit-response type operations. ! <> Mapping of XML names. We do some, not all, of what JAX-RPC specifies. ! <> Faults - What do we handle now? What needs to be enhanced? JAX-RPC needs improving, first, we think. ! <> Derived type support. @@ -83,15 +71,8 @@ * <Rich/Tom?> Need to handle collections of objects (max occurs > 1). Rich did the work for encoding. Does something still need to be done for literal? -X <Rich> Need to register array types and enum types in the stub and deploy.wsdd. - Registering array types if the remote (non-AXIS) services sets the - type attribute to the array type name instead of soapenc:array. - See samples.echo.testClient - Java2WSDL --------- -X <> Plug new framework into autogen mechanism - ! <Russell?/Rich?> Java2WSDL "void op(boolean b1, Boolean b2)" maps to <wsdl:message name="op1Request"> <wsdl:part name="in0" type="xsd:boolean"/> @@ -111,17 +92,21 @@ ! <> Support wildcards in stop object classes (i.e. "javax.*") +METHOD DISPATCH / SERVICE METADATA +---------------------------------- +! <Glen> Use QName lookup when processing doc/lit style stuff with + RPCProvider + + GENERAL / UNCATEGORIZED ----------------------- ! <> Reorganize resources.properties into packages -X <Glen> Remove search-for-first-MessageContext-arg code -X <Glen> Remove ServiceClient ! <> Before each release, make sure there aren't any English-language strings in the code. Folks aren't very good about putting these strings into resources.properties! -X <Glen> Refactor AxisServiceConfig so it doesn't require object - instantiation (static metadata) ! <Glen> Handle static methods without needing to create object instances +! <> Make Version accept org.apache.axis.utils.Options-style args + FUTURE ENHANCEMENTS (not necessary for current release) ------------------------------------------------------- 1.103 +46 -49 xml-axis/java/src/org/apache/axis/client/Call.java Index: Call.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/client/Call.java,v retrieving revision 1.102 retrieving revision 1.103 diff -u -r1.102 -r1.103 --- Call.java 21 Mar 2002 16:07:43 -0000 1.102 +++ Call.java 22 Mar 2002 16:08:31 -0000 1.103 @@ -85,6 +85,7 @@ import org.apache.axis.handlers.soap.SOAPService; import org.apache.axis.description.OperationDesc; import org.apache.axis.description.ServiceDesc; +import org.apache.axis.description.ParameterDesc; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -148,9 +149,6 @@ private Service service = null ; private QName portTypeName = null ; private QName operationName = null ; - private Vector paramNames = null ; - private Vector paramTypes = null ; - private Vector paramModes = null ; private QName returnType = null ; private MessageContext msgContext = null ; @@ -168,6 +166,7 @@ private String encodingStyle = Constants.URI_CURRENT_SOAP_ENC; private Integer timeout = null; + private OperationDesc operation = null; // Our Transport, if any private Transport transport = null ; @@ -652,22 +651,7 @@ */ public void addParameter(String paramName, QName paramType, ParameterMode parameterMode) { - if (parmAndRetReq) { - - if ( paramNames == null ) { - paramNames = new Vector(); - paramTypes = new Vector(); - paramModes = new Vector(); - } - - paramNames.add( new QName("", paramName) ); - paramTypes.add( paramType ); - paramModes.add( parameterMode ); - - } - else { - throw new JAXRPCException(); - } + addParameter(new QName("", paramName), paramType, parameterMode); } /** * Adds the specified parameter to the list of parameters for the @@ -682,16 +666,22 @@ ParameterMode parameterMode) { if (parmAndRetReq) { - if ( paramNames == null ) { - paramNames = new Vector(); - paramTypes = new Vector(); - paramModes = new Vector(); + if (operation == null) { + operation = new OperationDesc(); } - paramNames.add( paramName ); - paramTypes.add( paramType ); - paramModes.add( parameterMode ); + ParameterDesc param = new ParameterDesc(); + param.setQName( paramName ); + param.setTypeQName( paramType ); + byte mode = ParameterDesc.IN; + if (parameterMode == ParameterMode.PARAM_MODE_INOUT) { + mode = ParameterDesc.INOUT; + } else if (parameterMode == ParameterMode.PARAM_MODE_OUT) { + mode = ParameterDesc.OUT; + } + param.setMode(mode); + operation.addParameter(param); } else { throw new JAXRPCException(); @@ -721,12 +711,13 @@ */ public QName getParameterTypeByQName(QName paramQName) { int i; - if ( paramNames == null ) return( null ); + if ( operation == null ) return( null ); + + ParameterDesc param = operation.getParamByQName(paramQName); + if (param != null) { + return param.getTypeQName(); + } - for (i = 0 ; i< paramNames.size() ; i++ ) - if ( ((QName)paramNames.get(i)).equals(paramQName) ) { - return (QName) paramTypes.get(i); - } return( null ); } @@ -738,6 +729,7 @@ public void setReturnType(QName type) { if (parmAndRetReq) { returnType = type ; + if (operation != null) operation.setReturnType(type); } else { throw new JAXRPCException(); @@ -782,9 +774,7 @@ */ public void removeAllParameters() { if (parmAndRetReq) { - paramNames = null ; - paramTypes = null ; - paramModes = null ; + operation = null; // FIXME -- ??? } else { throw new JAXRPCException(); @@ -980,7 +970,8 @@ if ( message != null ) parts = message.getOrderedParts(null); this.setReturnType( null ); //Make sure we're restarting from fresh. - if(null != paramTypes) // attachments may have no parameters. +// if (null != paramTypes) // attachments may have no parameters. + if (operation != null && operation.getNumParams() > 0) // attachments may have no parameters. this.setReturnType( XMLType.AXIS_VOID ); if ( parts != null ) { for( int i = 0 ;i < parts.size() ; i++ ) { @@ -1281,16 +1272,12 @@ // If we never set-up any names... then just return what was passed in ////////////////////////////////////////////////////////////////////// - if ( paramNames == null ) return( params ); + if ( operation == null ) return( params ); // Count the number of IN and INOUT params, this needs to match the // number of params passed in - if not throw an error ///////////////////////////////////////////////////////////////////// - for ( i = 0 ; i < paramNames.size() ; i++ ) { - if (paramModes.get(i) == ParameterMode.PARAM_MODE_OUT) - continue ; - numParams++ ; - } + numParams = operation.getNumInParams(); if ( numParams != params.length ) throw new JAXRPCException( @@ -1301,10 +1288,14 @@ ////////////////////////////////////////////////// Vector result = new Vector(); int j = 0 ; - for ( i = 0 ; i < paramNames.size() ; i++ ) { - if (paramModes.get(i) == ParameterMode.PARAM_MODE_OUT) + ArrayList parameters = operation.getParameters(); + + for ( i = 0 ; i < parameters.size() ; i++ ) { + ParameterDesc param = (ParameterDesc)parameters.get(i); + if (param.getMode() == ParameterDesc.OUT) continue ; - QName paramQName = (QName)paramNames.get(i); + + QName paramQName = param.getQName(); RPCParam p = new RPCParam(paramQName.getNamespaceURI(), paramQName.getLocalPart(), params[j++] ); @@ -1530,7 +1521,7 @@ * wasn't called (paramTypes == null), then toss a fault. */ if (returnType != null && args != null && args.length != 0 - && paramTypes == null) { + && operation == null) { throw new AxisFault(JavaUtils.getMessage("mustSpecifyParms")); } @@ -1585,7 +1576,7 @@ * parameter types, check for this case right now and toss a fault * if things don't look right. */ - if (paramTypes != null && returnType == null) { + if (operation != null && returnType == null) { throw new AxisFault(JavaUtils.getMessage("mustSpecifyReturnType")); } @@ -1733,10 +1724,16 @@ msgContext.setPassword(password); } msgContext.setMaintainSession(maintainSession); - OperationDesc operationDesc = new OperationDesc(); - msgContext.setOperation(operationDesc); - operationDesc.setStyle(operationStyle); + OperationDesc oper = operation; + + // If we haven't set up an OperationDesc for this Call, just make a + // temporary one. + if (oper == null) + oper = new OperationDesc(); + msgContext.setOperation(oper); + + oper.setStyle(operationStyle); msgContext.setOperationStyle(operationStyle); if (useSOAPAction) { 1.14 +18 -2 xml-axis/java/src/org/apache/axis/deployment/wsdd/WSDDOperation.java Index: WSDDOperation.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/deployment/wsdd/WSDDOperation.java,v retrieving revision 1.13 retrieving revision 1.14 diff -u -r1.13 -r1.14 --- WSDDOperation.java 5 Mar 2002 14:02:12 -0000 1.13 +++ WSDDOperation.java 22 Mar 2002 16:08:31 -0000 1.14 @@ -62,18 +62,20 @@ import org.apache.axis.utils.JavaUtils; import org.apache.axis.description.OperationDesc; import org.apache.axis.description.ServiceDesc; +import org.apache.axis.description.ParameterDesc; import org.apache.axis.handlers.soap.SOAPService; import org.xml.sax.helpers.AttributesImpl; import javax.xml.rpc.namespace.QName; import java.io.IOException; import java.util.HashMap; +import java.util.ArrayList; +import java.util.Iterator; /** * */ -public class WSDDOperation - extends WSDDElement +public class WSDDOperation extends WSDDElement { /** Holds all our actual data */ OperationDesc desc = new OperationDesc(); @@ -93,6 +95,13 @@ String retQNameStr = e.getAttribute("returnQName"); if (retQNameStr != null && !retQNameStr.equals("")) desc.setReturnQName(XMLUtils.getQNameFromString(retQNameStr, e)); + + Element [] parameters = getChildElements(e, "parameter"); + for (int i = 0; i < parameters.length; i++) { + Element paramEl = parameters[i]; + WSDDParameter parameter = new WSDDParameter(paramEl, desc); + desc.addParameter(parameter.getParameter()); + } if (parent.getStyle() == ServiceDesc.STYLE_DOCUMENT) { Element [] mappingElements = getChildElements(e, "elementMapping"); @@ -135,6 +144,13 @@ context.qName2String(desc.getElementQName())); context.startElement(WSDDConstants.ELEMENTMAP_QNAME, attrs); context.endElement(); + } + + ArrayList params = desc.getParameters(); + for (Iterator i = params.iterator(); i.hasNext();) { + ParameterDesc parameterDesc = (ParameterDesc) i.next(); + WSDDParameter p = new WSDDParameter(parameterDesc); + p.writeToContext(context); } context.endElement(); 1.45 +20 -3 xml-axis/java/src/org/apache/axis/deployment/wsdd/WSDDService.java Index: WSDDService.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/deployment/wsdd/WSDDService.java,v retrieving revision 1.44 retrieving revision 1.45 diff -u -r1.44 -r1.45 --- WSDDService.java 5 Mar 2002 14:02:12 -0000 1.44 +++ WSDDService.java 22 Mar 2002 16:08:31 -0000 1.45 @@ -86,6 +86,9 @@ extends WSDDTargetedChain implements WSDDTypeMappingContainer { + public static final QName WSDL_QNAME = new QName(WSDDConstants.WSDD_NS, + "wsdlFile"); + public TypeMappingRegistry tmr = null; private Vector faultFlows = new Vector(); @@ -129,7 +132,8 @@ String modeStr = e.getAttribute("style"); if (modeStr != null && !modeStr.equals("")) { - desc.setStyle(MessageContext.getStyleFromString(modeStr)); + style = MessageContext.getStyleFromString(modeStr); + desc.setStyle(style); } Element [] operationElements = getChildElements(e, "operation"); @@ -160,6 +164,12 @@ namespaces.add(ns); } + Element wsdlElem = getChildElement(e, "wsdlFile"); + if (wsdlElem != null) { + String fileName = XMLUtils.getChildCharacterData(wsdlElem); + desc.setWSDLFile(fileName); + } + String typeStr = e.getAttribute("provider"); if (typeStr != null && !typeStr.equals("")) providerQName = XMLUtils.getQNameFromString(typeStr, e); @@ -423,11 +433,18 @@ attrs.addAttribute("", "provider", "provider", "CDATA", context.qName2String(providerQName)); } - if (style == ServiceDesc.STYLE_DOCUMENT) { - attrs.addAttribute("", "style", "style", "CDATA", "document"); + if (style != ServiceDesc.STYLE_RPC) { + attrs.addAttribute("", "style", "style", + "CDATA", MessageContext.getStyleFromInt(style)); } context.startElement(WSDDConstants.SERVICE_QNAME, attrs); + + if (desc.getWSDLFile() != null) { + context.startElement(WSDL_QNAME, null); + context.writeSafeString(desc.getWSDLFile()); + context.endElement(); + } for (int i = 0; i < operations.size(); i++) { WSDDOperation operation = (WSDDOperation) operations.elementAt(i); 1.10 +93 -85 xml-axis/java/src/org/apache/axis/deployment/wsdd/WSDDParameter.java 1.3 +70 -3 xml-axis/java/src/org/apache/axis/description/OperationDesc.java Index: OperationDesc.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/description/OperationDesc.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- OperationDesc.java 5 Mar 2002 14:02:13 -0000 1.2 +++ OperationDesc.java 22 Mar 2002 16:08:31 -0000 1.3 @@ -56,6 +56,7 @@ import javax.xml.rpc.namespace.QName; import java.util.ArrayList; +import java.util.Iterator; /** * An OperationDesc is an abstract description of an operation on a service. @@ -89,6 +90,9 @@ /** This operation's style. If null, we default to our parent's */ private Integer style; + /** The number of "in" params (i.e. IN or INOUT) for this operation */ + private int numInParams = 0; + /** * Default constructor. */ @@ -162,14 +166,77 @@ return style.intValue(); } - public void addParameter(Parameter param) + public void addParameter(ParameterDesc param) { + // Should we enforce adding INs then INOUTs then OUTs? + parameters.add(param); + if ((param.getMode() == ParameterDesc.IN) || + (param.getMode() == ParameterDesc.INOUT)) { + param.setOrder(numInParams++); + } + } + + public ParameterDesc getParameter(int i) + { + return (ParameterDesc)parameters.get(i); + } + + public ArrayList getParameters() { + return parameters; } - public Parameter getParameter(int i) + public int getNumInParams() { + return numInParams; + } + + public int getNumParams() { + return parameters.size(); + } + + public ParameterDesc getParamByQName(QName qname) { - return (Parameter)parameters.get(i); + for (Iterator i = parameters.iterator(); i.hasNext();) { + ParameterDesc param = (ParameterDesc) i.next(); + if (param.getQName().equals(qname)) + return param; + } + + return null; + } + + public ParameterDesc getInputParamByQName(QName qname) + { + ParameterDesc param = null; + + param = getParamByQName(qname); + + if ((param == null) || (param.getMode() != ParameterDesc.IN)) { + param = null; + } + + return param; + } + + public ParameterDesc getOutputParamByQName(QName qname) + { + ParameterDesc param = null; + + param = getParamByQName(qname); + + if (param != null && param.getMode() == ParameterDesc.IN) { + param = null; + } + + if ((param == null) || (param.getMode() == ParameterDesc.IN)) { + if (returnQName == null || qname.equals(returnQName)) { + param = new ParameterDesc(); + param.setQName(qname); + param.setTypeQName(returnType); + } + } + + return param; } } 1.3 +135 -11 xml-axis/java/src/org/apache/axis/description/ServiceDesc.java Index: ServiceDesc.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/description/ServiceDesc.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- ServiceDesc.java 5 Mar 2002 14:02:13 -0000 1.2 +++ ServiceDesc.java 22 Mar 2002 16:08:31 -0000 1.3 @@ -54,15 +54,22 @@ */ package org.apache.axis.description; +import org.apache.axis.utils.cache.JavaClass; +import org.apache.axis.utils.JavaUtils; +import org.apache.axis.encoding.TypeMapping; + import javax.xml.rpc.namespace.QName; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; +import java.lang.reflect.Method; /** * A ServiceDesc is an abstract description of a service. * - * !!! WORK IN PROGRESS + * ServiceDescs contain OperationDescs, which are descriptions of operations. + * The information about a service's operations comes from one of two places: + * 1) deployment, or 2) introspection. * * @author Glen Daniels ([EMAIL PROTECTED]) */ @@ -72,20 +79,90 @@ public static final int STYLE_WRAPPED = 2; public static final int STYLE_MESSAGE = 3; + /** This becomes true once we've added some operations */ + private boolean hasOperationData = false; + + /** + * Fill in what we can of the service description by introspecting a + * Java class. Only do this if we haven't already been filled in. + */ + public void loadServiceDescByIntrospection(JavaClass jc, TypeMapping tm) + { + if (hasOperationData) + return; + + ArrayList allowedMethods = null; + Method [] methods = jc.getJavaClass().getDeclaredMethods(); + + for (int i = 0; i < methods.length; i++) { + String methodName = methods[i].getName(); + + // Skip it if it's not allowed + // FIXME : Should NEVER allow java.lang methods to be + // called directly, right? + if ((allowedMethods != null) && + !allowedMethods.contains(methodName)) + continue; + + // Make an OperationDesc for each method + Method method = methods[i]; + OperationDesc operation = new OperationDesc(); + operation.setName(methodName); + Class [] paramTypes = method.getParameterTypes(); + String [] paramNames = + JavaUtils.getParameterNamesFromDebugInfo(method); + for (int k = 0; k < paramTypes.length; k++) { + Class type = paramTypes[k]; + ParameterDesc paramDesc = new ParameterDesc(); + if (paramNames != null) { + paramDesc.setName(paramNames[k+1]); + } else { + paramDesc.setName("in" + k); + } + Class heldClass = JavaUtils.getHolderValueType(type); + if (heldClass != null) { + paramDesc.setMode(ParameterDesc.INOUT); + paramDesc.setTypeQName(tm.getTypeQName(heldClass)); + } else { + paramDesc.setMode(ParameterDesc.IN); + paramDesc.setTypeQName(tm.getTypeQName(type)); + } + operation.addParameter(paramDesc); + } + addOperationDesc(operation); + } + + hasOperationData = true; + } + /** Style */ private int style = STYLE_RPC; - /** Implementation class */ + /** Implementation class name */ private String className = null; + /** Implementation class */ + private Class implClass = null; + /** Our operations - a list of OperationDescs */ private ArrayList operations = new ArrayList(); /** A collection of namespaces which will map to this service */ private ArrayList namespaceMappings = null; + /** + * Where does our WSDL document live? If this is non-null, the "?WSDL" + * generation will automatically return this file instead of dynamically + * creating a WSDL. BE CAREFUL because this means that Handlers will + * not be able to add to the WSDL for extensions/headers.... + */ + private String wsdlFileName = null; + + /** Place to store user-extensible service-related properties */ + private HashMap properties = null; + /** Lookup caches */ - private HashMap method2OperationMap = null; + private HashMap name2OperationsMap = null; private HashMap qname2OperationMap = null; public int getStyle() { @@ -110,28 +187,75 @@ return ((style == STYLE_RPC) || (style == STYLE_WRAPPED)); } + public String getWSDLFile() { + return wsdlFileName; + } + + public void setWSDLFile(String wsdlFileName) { + this.wsdlFileName = wsdlFileName; + } + public void addOperationDesc(OperationDesc operation) { operations.add(operation); operation.setParent(this); + if (name2OperationsMap == null) { + name2OperationsMap = new HashMap(); + } + + String name = operation.getName(); + + ArrayList overloads = (ArrayList)name2OperationsMap.get(name); + if (overloads == null) { + overloads = new ArrayList(); + name2OperationsMap.put(name, overloads); + } + + overloads.add(operation); + + // If we're adding these, we won't introspect (either because we + // trust the deployer/user to add everything instead, or because + // we're actually in the middle of introspecting right now) + hasOperationData = true; } + public OperationDesc [] getOperationsByName(String methodName) + { + if (name2OperationsMap == null) + return null; + + ArrayList result = (ArrayList)name2OperationsMap.get(methodName); + if (result == null) + return null; + + OperationDesc [] array = new OperationDesc [result.size()]; + return (OperationDesc[])result.toArray(array); + } + + /** + * Return an operation matching the given method name. Note that if we + * have multiple overloads for this method, we will return the first one. + */ public OperationDesc getOperationDescByName(String methodName) { - if (method2OperationMap == null) { - method2OperationMap = new HashMap(); - for (Iterator i = operations.iterator(); i.hasNext();) { - OperationDesc desc = (OperationDesc) i.next(); - method2OperationMap.put(desc.getName(), desc); - } - } - return (OperationDesc)method2OperationMap.get(methodName); + if (name2OperationsMap == null) + return null; + + ArrayList overloads = (ArrayList)name2OperationsMap.get(methodName); + if (overloads == null) + return null; + + return (OperationDesc)overloads.get(0); } + /** + * Map an XML QName to an operation. + */ public OperationDesc getOperationByElementQName(QName qname) { // If we're a wrapped service (i.e. RPC or WRAPPED style), we expect // this qname to match one of our operation names directly. + // FIXME : Should this really ignore namespaces? if (isWrapped()) { return getOperationDescByName(qname.getLocalPart()); } 1.1 xml-axis/java/src/org/apache/axis/description/ParameterDesc.java Index: ParameterDesc.java =================================================================== /* * The Apache Software License, Version 1.1 * * * Copyright (c) 2001 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 [EMAIL PROTECTED] * * 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.apache.axis.description; import org.apache.axis.wsdl.toJava.TypeEntry; import javax.xml.rpc.namespace.QName; import java.util.Vector; /** * A Parameter descriptor, collecting the interesting info about an * operation parameter. * * (mostly taken from org.apache.axis.wsdl.toJava.Parameter right now) * * @author Glen Daniels ([EMAIL PROTECTED]) */ public class ParameterDesc { // constant values for the parameter mode. public static final byte IN = 1; public static final byte OUT = 2; public static final byte INOUT = 3; /** The Parameter's XML QName */ private QName name; /** A TypeEntry corresponding to this parameter */ public TypeEntry typeEntry; /** The Parameter mode (in, out, inout) */ public byte mode = IN; /** The XML type of this parameter */ private QName typeQName; /** The order of this parameter (-1 indicates unordered) */ private int order = -1; public String toString() { return "(" + typeEntry + ", " + getName() + ", " + (mode == IN ? "IN)" : mode == INOUT ? "INOUT)" : "OUT)"); } // toString /** * Get a mode constant from a string. Defaults to IN, and returns * OUT or INOUT if the string matches (ignoring case). */ public static byte modeFromString(String modeStr) { byte ret = IN; if (modeStr.equalsIgnoreCase("out")) { ret = OUT; } else if (modeStr.equalsIgnoreCase("inout")) { ret = INOUT; } return ret; } public static String getModeAsString(byte mode) { if (mode == INOUT) { return "inout"; } else if (mode == OUT) { return "out"; } else if (mode == IN) { return "in"; } // FIXME - needs message throw new IllegalArgumentException(); } public QName getQName() { return name; } public String getName() { return name.getLocalPart(); } public void setName(String name) { this.name = new QName("", name); } public void setQName(QName name) { this.name = name; } public QName getTypeQName() { return typeQName; } public void setTypeQName(QName typeQName) { this.typeQName = typeQName; } public byte getMode() { return mode; } public void setMode(byte mode) { this.mode = mode; } public int getOrder() { return order; } public void setOrder(int order) { this.order = order; } } // class Parameter 1.52 +26 -0 xml-axis/java/src/org/apache/axis/handlers/soap/SOAPService.java Index: SOAPService.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/handlers/soap/SOAPService.java,v retrieving revision 1.51 retrieving revision 1.52 diff -u -r1.51 -r1.52 --- SOAPService.java 18 Mar 2002 04:03:36 -0000 1.51 +++ SOAPService.java 22 Mar 2002 16:08:32 -0000 1.52 @@ -74,6 +74,7 @@ import org.apache.axis.message.SOAPHeader; import org.apache.axis.utils.JavaUtils; import org.apache.axis.utils.LockableHashtable; +import org.apache.axis.utils.XMLUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -88,6 +89,8 @@ import java.util.HashMap; import java.util.Hashtable; import java.beans.IntrospectionException; +import java.io.File; +import java.io.FileInputStream; /** A <code>SOAPService</code> is a Handler which encapsulates a SOAP * invocation. It has an request chain, an response chain, and a pivot-point, @@ -278,6 +281,29 @@ public void setPropertyParent(Hashtable parent) { ((LockableHashtable)options).setParent(parent); + } + + /** + * Generate WSDL. If we have a specific file configured in the + * ServiceDesc, just return that. Otherwise run through all the Handlers + * (including the provider) and call generateWSDL() on them via our + * parent's implementation. + */ + public void generateWSDL(MessageContext msgContext) throws AxisFault { + if (serviceDescription == null || + serviceDescription.getWSDLFile() == null) { + super.generateWSDL(msgContext); + return; + } + + // Got a WSDL file in the service description, so try and read it + try { + Document doc = XMLUtils.newDocument( + new FileInputStream(serviceDescription.getWSDLFile())); + msgContext.setProperty("WSDL", doc); + } catch (Exception e) { + throw AxisFault.makeFault(e); + } } /********************************************************************* * Administration and management APIs 1.43 +100 -58 xml-axis/java/src/org/apache/axis/message/RPCElement.java Index: RPCElement.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/message/RPCElement.java,v retrieving revision 1.42 retrieving revision 1.43 diff -u -r1.42 -r1.43 --- RPCElement.java 8 Mar 2002 20:04:45 -0000 1.42 +++ RPCElement.java 22 Mar 2002 16:08:32 -0000 1.43 @@ -10,7 +10,7 @@ * are met: * * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + * 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 @@ -18,7 +18,7 @@ * distribution. * * 3. The end-user documentation included with the redistribution, - * if any, must include the following acknowledgment: + * 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, @@ -26,7 +26,7 @@ * * 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 + * software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache", @@ -57,9 +57,13 @@ import org.apache.axis.encoding.DeserializationContext; import org.apache.axis.encoding.SerializationContext; +import org.apache.axis.encoding.TypeMapping; import org.apache.axis.Constants; import org.apache.axis.MessageContext; import org.apache.axis.Handler; +import org.apache.axis.Message; +import org.apache.axis.wsdl.toJava.Utils; +import org.apache.axis.providers.java.RPCProvider; import org.apache.axis.description.OperationDesc; import org.apache.axis.description.ServiceDesc; import org.apache.axis.handlers.soap.SOAPService; @@ -79,6 +83,7 @@ protected Vector params = new Vector(); protected boolean needDeser = false; protected boolean elementIsFirstParam = false; + protected OperationDesc myOperation = null; public RPCElement(String namespace, String localName, @@ -90,12 +95,13 @@ super(namespace, localName, prefix, attributes, context); encodingStyle = Constants.URI_CURRENT_SOAP_ENC; - + // This came from parsing XML, so we need to deserialize it sometime needDeser = true; if (operation != null) { this.name = operation.getName(); + myOperation = operation; // IF we're doc/literal... we can't count on the element name // being the method name. @@ -103,12 +109,12 @@ ServiceDesc.STYLE_DOCUMENT); } } - + public RPCElement(String namespace, String methodName, Object [] args) { this.setNamespaceURI(namespace); this.name = methodName; - + encodingStyle = Constants.URI_CURRENT_SOAP_ENC; for (int i = 0; args != null && i < args.length; i++) { @@ -121,92 +127,128 @@ } } } - + public RPCElement(String methodName) { encodingStyle = Constants.URI_CURRENT_SOAP_ENC; this.name = methodName; } - + public String getMethodName() { return name; } - + + public OperationDesc getOperation() + { + return myOperation; + } + protected Class defaultParamTypes[] = null; - + public Class [] getJavaParamTypes() { return defaultParamTypes; } - + public void deserialize() throws SAXException { needDeser = false; MessageContext msgContext = context.getMessageContext(); - Handler service = msgContext.getService(); - String clsName = null; + SOAPService service = msgContext.getService(); + OperationDesc [] operations = null; if (service != null) { - clsName = (String)service.getOption("className"); - } + ServiceDesc serviceDesc = service.getServiceDescription(); - if (clsName != null) { - ClassLoader cl = msgContext.getClassLoader(); - ClassCache cache = msgContext.getAxisEngine().getClassCache(); - JavaClass jc = null; - try { - jc = cache.lookup(clsName, cl); - } catch (ClassNotFoundException e) { - throw new SAXException(e); + // If we don't have a service description, create one + // via introspection and cache it in the SOAPService. + String clsName = (String)service.getOption("className"); + + if (clsName != null) { + ClassLoader cl = msgContext.getClassLoader(); + ClassCache cache = msgContext.getAxisEngine(). + getClassCache(); + JavaClass jc = null; + try { + jc = cache.lookup(clsName, cl); + } catch (ClassNotFoundException e) { + throw new SAXException(e); + } + TypeMapping tm = (TypeMapping)msgContext.getTypeMappingRegistry().getTypeMapping(msgContext.getEncodingStyle()); + serviceDesc.loadServiceDescByIntrospection(jc, tm); } - - if (log.isDebugEnabled()) { - log.debug(JavaUtils.getMessage( - "lookup00", name, clsName)); + + if (serviceDesc != null) { + // If we've got a service description now, we want to use + // the matching operations in there. + operations = serviceDesc.getOperationsByName(name); + + if (operations == null) { + String lc = Utils.xmlNameToJava(name); + operations = serviceDesc.getOperationsByName(lc); + } } - - Method[] method = jc.getMethod(name); - int numChildren = (getChildren() == null) ? 0 : getChildren().size(); - if (method != null) { - for (int i = 0; i < method.length; i++) { - defaultParamTypes = method[i].getParameterTypes(); - if (defaultParamTypes.length >= numChildren) { - try { - if (elementIsFirstParam) { - context.pushElementHandler(new RPCHandler(this)); - context.setCurElement(null); - } else { - context.pushElementHandler(new EnvelopeHandler(new RPCHandler(this))); - context.setCurElement(this); - } - - publishToHandler((org.xml.sax.ContentHandler) context); - } catch (SAXException e) { - // If there was a problem, try the next one. - params = new Vector(); - continue; + } + + // Figure out if we should be looking for out params or in params + // (i.e. is this message a response?) + Message msg = msgContext.getCurrentMessage(); + 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(); + + // 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]; + if (operation.getNumInParams() == numParams) { + // Set the operation so the RPCHandler can get at it + myOperation = operation; + try { + if (elementIsFirstParam) { + context.pushElementHandler(rpcHandler); + context.setCurElement(null); + } else { + context.pushElementHandler( + new EnvelopeHandler(rpcHandler)); + context.setCurElement(this); } - // Succeeded in deserializing! + + publishToHandler((org.xml.sax.ContentHandler) context); + + // Success!! return; + } catch (SAXException e) { + // If there was a problem, try the next one. + params = new Vector(); + continue; } } } } if (elementIsFirstParam) { - context.pushElementHandler(new RPCHandler(this)); + context.pushElementHandler(rpcHandler); context.setCurElement(null); } else { - context.pushElementHandler(new EnvelopeHandler(new RPCHandler(this))); + context.pushElementHandler(new EnvelopeHandler(rpcHandler)); context.setCurElement(this); } publishToHandler((org.xml.sax.ContentHandler)context); } - + /** This gets the FIRST param whose name matches. * !!! Should it return more in the case of duplicates? */ @@ -215,16 +257,16 @@ if (needDeser) { deserialize(); } - + for (int i = 0; i < params.size(); i++) { RPCParam param = (RPCParam)params.elementAt(i); if (param.getName().equals(name)) return param; } - + return null; } - + public Vector getParams() throws SAXException { if (needDeser) { @@ -233,7 +275,7 @@ return params; } - + public void addParam(RPCParam param) { param.setRPCCall(this); @@ -258,7 +300,7 @@ } context.startElement(new QName(namespaceURI,name), attributes); } - + for (int i = 0; i < params.size(); i++) { RPCParam param = (RPCParam)params.elementAt(i); if (!isRPC && encodingStyle.equals("")) { @@ -266,7 +308,7 @@ } param.serialize(context); } - + if (isRPC) { context.endElement(); } 1.30 +81 -52 xml-axis/java/src/org/apache/axis/message/RPCHandler.java Index: RPCHandler.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/message/RPCHandler.java,v retrieving revision 1.29 retrieving revision 1.30 diff -u -r1.29 -r1.30 --- RPCHandler.java 11 Mar 2002 14:25:59 -0000 1.29 +++ RPCHandler.java 22 Mar 2002 16:08:32 -0000 1.30 @@ -64,6 +64,8 @@ import org.apache.axis.Handler; 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.client.Call; import org.apache.axis.encoding.DeserializationContext; import org.apache.axis.encoding.Deserializer; @@ -82,21 +84,46 @@ import java.lang.reflect.Method; import java.util.Vector; +/** + * This is the SOAPHandler which is called for each RPC parameter as we're + * deserializing the XML for a method call or return. In other words for + * this XML: + * + * <methodName> + * <param1 xsi:type="xsd:string">Hello!</param1> + * <param2>3.14159</param2> + * </methodName> + * + * ...we'll get onStartChild() events for <param1> and <param2>. + * + * @author Glen Daniels ([EMAIL PROTECTED]) + */ public class RPCHandler extends SOAPHandler { protected static Log log = LogFactory.getLog(RPCHandler.class.getName()); - private RPCElement call; + private RPCElement rpcElem; private RPCParam currentParam; + private boolean isResponse; - public RPCHandler(RPCElement call) + public RPCHandler(RPCElement rpcElem, boolean isResponse) throws SAXException { - this.call = call; + this.rpcElem = rpcElem; + this.isResponse = isResponse; } + /** + * Register the start of a parameter (child element of the method call + * element). + * + * Our job here is to figure out a) which parameter this is (based on + * the QName of the element or its position), and b) what type it is + * (based on the xsi:type attribute or operation metadata) so we can + * successfully deserialize it. + */ public SOAPHandler onStartChild(String namespace, String localName, String prefix, @@ -104,23 +131,26 @@ DeserializationContext context) throws SAXException { - /** Potential optimizations: - * - * - Cache typeMappingRegistry - * - Cache service description - */ if (log.isDebugEnabled()) { - log.debug(JavaUtils.getMessage("enter00", "RPCHandler.onStartChild()")); + log.debug(JavaUtils.getMessage("enter00", + "RPCHandler.onStartChild()")); } - Vector params = call.getParams(); + Vector params = rpcElem.getParams(); // This is a param. currentParam = new RPCParam(namespace, localName, null); - call.addParam(currentParam); + rpcElem.addParam(currentParam); MessageElement curEl = context.getCurElement(); QName type = null; + QName qname = new QName(namespace, localName); + ParameterDesc paramDesc = null; + + OperationDesc operation = rpcElem.getOperation(); + + // Grab xsi:type attribute if present, on either this element or + // the referent (if it's an href). if (curEl.getHref() != null) { MessageElement ref = context.getElementByID(curEl.getHref()); if (ref != null) @@ -132,52 +162,50 @@ localName, attributes); } + if (log.isDebugEnabled()) { log.debug(JavaUtils.getMessage("typeFromAttr00", "" + type)); } - String isNil = attributes.getValue(Constants.URI_2001_SCHEMA_XSI,"nil"); + // If we have an operation descriptor, try to associate this parameter + // with the appropriate ParameterDesc + if (operation != null) { + // Try by name first + if (isResponse) { + paramDesc = operation.getOutputParamByQName(qname); + } else { + paramDesc = operation.getInputParamByQName(qname); + } - if ( isNil != null && isNil.equals("true") ) - return( (SOAPHandler) new DeserializerImpl() ); - - // xsi:type always overrides everything else - if (type == null) { - // check the introspected types - // - // NOTE : We don't check params.isEmpty() here because we - // must have added at least one above... - // - - // No xsi:type so in the return rpc case try to get it from - // the Call object - MessageContext msgContext = context.getMessageContext(); - Message msg = msgContext.getCurrentMessage(); - if ( msg != null && msg.getMessageType() == Message.RESPONSE ) { - Call c = (Call) msgContext.getProperty( MessageContext.CALL ); - if ( c != null ) { - // First look for this param by name - QName qname = new QName(namespace, localName); - type = c.getParameterTypeByQName(qname); - - // If we can't find it by name then assume it must - // be the return type - is this correct/safe???? - if ( type == null ) - type = c.getReturnType(); - } + // If that didn't work, try position + // FIXME : Do we need to be in EITHER named OR positional + // mode? I.e. will it screw us up to find something + // by position if we've already looked something up + // by name? I think so... + if (paramDesc == null) { + paramDesc = operation.getParameter(params.size() - 1); } - if (type == null && call.getJavaParamTypes() !=null && - params.size() <= call.getJavaParamTypes().length) { + if (paramDesc != null) { + // Keep the association so we can use it later + // (see RPCProvider.processMessage()) + currentParam.setParamDesc(paramDesc); - TypeMapping typeMap = context.getTypeMapping(); - int index = params.size()-1; - type = typeMap.getTypeQName(call.getJavaParamTypes()[index]); - if (log.isDebugEnabled()) { - log.debug(JavaUtils.getMessage("typeFromParms00", "" + type)); + if (type == null) { + type = paramDesc.getTypeQName(); } } + + // FIXME : We should check here to make sure any specified + // xsi:type jibes with the expected type in the ParamDesc!! } + + + String isNil = attributes.getValue(Constants.URI_2001_SCHEMA_XSI, + "nil"); + + if ( isNil != null && isNil.equals("true") ) + return( (SOAPHandler) new DeserializerImpl() ); Deserializer dser; if (type != null) { @@ -190,17 +218,18 @@ throw new SAXException(JavaUtils.getMessage( "noDeser01", localName,"" + type)); } - + dser.registerValueTarget( - new FieldTarget(currentParam, + new FieldTarget(currentParam, RPCParam.getValueField())); - + if (log.isDebugEnabled()) { - log.debug(JavaUtils.getMessage("exit00", "RPCHandler.onStartChild()")); + log.debug(JavaUtils.getMessage("exit00", + "RPCHandler.onStartChild()")); } return (SOAPHandler) dser; } - + public void endElement(String namespace, String localName, DeserializationContext context) throws SAXException @@ -209,6 +238,6 @@ log.debug(JavaUtils.getMessage("setProp00", "MessageContext", "RPCHandler.endElement().")); } - context.getMessageContext().setProperty("RPC", call); + context.getMessageContext().setProperty("RPC", rpcElem); } } 1.35 +13 -2 xml-axis/java/src/org/apache/axis/message/RPCParam.java Index: RPCParam.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/message/RPCParam.java,v retrieving revision 1.34 retrieving revision 1.35 diff -u -r1.34 -r1.35 --- RPCParam.java 2 Mar 2002 05:44:54 -0000 1.34 +++ RPCParam.java 22 Mar 2002 16:08:32 -0000 1.35 @@ -57,6 +57,7 @@ import org.apache.axis.encoding.SerializationContext; import org.apache.axis.utils.JavaUtils; +import org.apache.axis.description.ParameterDesc; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -79,7 +80,9 @@ private QName qname; public Object value; - + + private ParameterDesc paramDesc; + private static Field valueField; static { Class cls = RPCParam.class; @@ -140,7 +143,15 @@ { return valueField; } - + + public ParameterDesc getParamDesc() { + return paramDesc; + } + + public void setParamDesc(ParameterDesc paramDesc) { + this.paramDesc = paramDesc; + } + public void serialize(SerializationContext context) throws IOException { 1.49 +31 -29 xml-axis/java/src/org/apache/axis/providers/java/RPCProvider.java Index: RPCProvider.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/providers/java/RPCProvider.java,v retrieving revision 1.48 retrieving revision 1.49 diff -u -r1.48 -r1.49 --- RPCProvider.java 13 Mar 2002 19:50:58 -0000 1.48 +++ RPCProvider.java 22 Mar 2002 16:08:32 -0000 1.49 @@ -60,7 +60,7 @@ import org.apache.axis.MessageContext; import org.apache.axis.description.OperationDesc; import org.apache.axis.description.ServiceDesc; -import org.apache.axis.description.Parameter; +import org.apache.axis.description.ParameterDesc; import org.apache.axis.handlers.soap.SOAPService; import org.apache.axis.message.RPCElement; import org.apache.axis.message.RPCParam; @@ -78,6 +78,7 @@ import java.util.StringTokenizer; import java.util.Vector; import java.util.Iterator; +import java.util.ArrayList; /** * Implement message processing by walking over RPCElements of the @@ -100,7 +101,7 @@ throws Exception { if (log.isDebugEnabled()) { - log.debug(JavaUtils.getMessage("enter00", + log.debug(JavaUtils.getMessage("enter00", "RPCProvider.processMessage()")); } @@ -122,8 +123,8 @@ if (!(bodies.get(bNum) instanceof RPCElement)) { SOAPBodyElement bodyEl = (SOAPBodyElement)bodies.get(bNum); - if (operation != null) { - Parameter param = operation.getParameter(bNum); + if (bodyEl.isRoot() && operation != null) { + ParameterDesc param = operation.getParameter(bNum); Object val = bodyEl.getValueAsType(param.getTypeQName()); body = new RPCElement("", operation.getName(), @@ -138,15 +139,21 @@ String mName = body.getMethodName(); Vector args = body.getParams(); Object[] argValues = null ; - - + + if ( args != null && args.size() > 0 ) { argValues = new Object[ args.size()]; for ( int i = 0 ; i < args.size() ; i++ ) { - argValues[i] = ((RPCParam)args.get(i)).getValue() ; - + RPCParam rpcParam = (RPCParam)args.get(i); + ParameterDesc paramDesc = rpcParam.getParamDesc(); + if (paramDesc == null || paramDesc.getOrder() == -1) { + argValues[i] = rpcParam.getValue() ; + } else { + argValues[paramDesc.getOrder()] = rpcParam.getValue(); + } + if (log.isDebugEnabled()) { - log.debug(" " + JavaUtils.getMessage("value00", + log.debug(" " + JavaUtils.getMessage("value00", "" + argValues[i]) ); } } @@ -156,11 +163,6 @@ checkMethodName(msgContext, allowedMethods, mName); // Get the actual method to invoke. - // Since the method signature may contain output parameters - // (Holders) there is no easy way to match the number of arguments - // to a Method. Furthermore method overloading does not work in - // wsdl. Thus the following code only works if there is no - // overloading. int numberOfBodyArgs = args.size(); Method[] methods = getMethod(msgContext, jc, mName); @@ -186,14 +188,14 @@ Method method = methods[index]; ex = null; params = method.getParameterTypes(); - + // Don't bother with this one if it has FEWER params if (argValues != null) { if (params.length < argValues.length) continue; } - - // The number of method parameters must match the + + // The number of method parameters must match the // arguments taking into consideration output parameters. Object[] newArgValues = new Object[params.length]; int old = 0; @@ -286,7 +288,7 @@ resBody.setEncodingStyle(msgContext.getEncodingStyle()); if ( objRes != null ) { - // In the old skeleton a param list was returned, which + // In the old skeleton a param list was returned, which // contained the RPC params. Preserve this for now. if (objRes instanceof ParamList) { ParamList list = (ParamList)objRes; @@ -316,23 +318,23 @@ for (int i=0; i < argValues.length; i++) { Class heldType = JavaUtils.getHolderValueType(params[i]); if (heldType != null) { - // Create an RPCParam by converting the Holder back into + // Create an RPCParam by converting the Holder back into // the held type. resBody.addParam (new RPCParam (getParameterName(obj, methods[index], - i, + i, mName, args), JavaUtils.convert( - argValues[i], + argValues[i], heldType))); } } - + resEnv.addBodyElement( resBody ); } } - + protected Method[] getMethod(MessageContext msgContext, JavaClass jc, String mName) throws Exception { @@ -393,8 +395,8 @@ } /** - * Returns or creates the parameter name for the i'th parm of - * of the method specified. + * Returns or creates the parameter name for the i'th parm of + * of the method specified. * (Use i=-1 to access the return name.) */ protected QName getParameterName(Object obj, @@ -403,12 +405,12 @@ String mName) { return getParameterName(obj, method, i, mName, null); } - + /** - * Returns or creates the parameter name for the i'th parm of + * Returns or creates the parameter name for the i'th parm of * of the method specified, using the name in the appropriate * position of the rpcParams Vector if it is supplied. - * + * * (Use i=-1 to access the return name.) */ protected QName getParameterName(Object obj, @@ -418,7 +420,7 @@ Vector rpcParams) { QName parmName = null; // Emitter skeletons keep track of the parameter names - if (obj instanceof org.apache.axis.wsdl.Skeleton) + if (obj instanceof org.apache.axis.wsdl.Skeleton) parmName = ((org.apache.axis.wsdl.Skeleton)obj).getParameterName(method.getName(), i); if (parmName == null) { if (i >= 0) { 1.11 +9 -0 xml-axis/java/test/RPCDispatch/Service.java Index: Service.java =================================================================== RCS file: /home/cvs/xml-axis/java/test/RPCDispatch/Service.java,v retrieving revision 1.10 retrieving revision 1.11 diff -u -r1.10 -r1.11 --- Service.java 20 Feb 2002 18:59:22 -0000 1.10 +++ Service.java 22 Mar 2002 16:08:32 -0000 1.11 @@ -22,6 +22,15 @@ } /** + * Concatenate two strings - used to test out-of-order parameter + * matching. + */ + public String concatenate(String a1, String a2) + { + return a1 + a2; + } + + /** * Reverse the order of a struct */ public Data reverseData(Data input) throws Exception { 1.27 +28 -40 xml-axis/java/test/RPCDispatch/TestSerializedRPC.java Index: TestSerializedRPC.java =================================================================== RCS file: /home/cvs/xml-axis/java/test/RPCDispatch/TestSerializedRPC.java,v retrieving revision 1.26 retrieving revision 1.27 diff -u -r1.26 -r1.27 --- TestSerializedRPC.java 20 Feb 2002 18:59:22 -0000 1.26 +++ TestSerializedRPC.java 22 Mar 2002 16:08:32 -0000 1.27 @@ -57,6 +57,20 @@ reverse.setOption("className", "test.RPCDispatch.Service"); reverse.setOption("allowedMethods", "*"); provider.deployService(SOAPAction, reverse); + + // And deploy the type mapping + Class javaType = Data.class; + QName xmlType = new QName("urn:foo", "Data"); + BeanSerializerFactory sf = new BeanSerializerFactory(javaType, xmlType); + BeanDeserializerFactory df = new BeanDeserializerFactory(javaType, xmlType); + + TypeMappingRegistry tmr = engine.getTypeMappingRegistry(); + TypeMapping tm = (TypeMapping) tmr.getTypeMapping(Constants.URI_CURRENT_SOAP_ENC); + if (tm == null || tm == tmr.getDefaultTypeMapping()) { + tm = (TypeMapping) tmr.createTypeMapping(); + tmr.register(Constants.URI_CURRENT_SOAP_ENC, tm); + } + tm.register(javaType, xmlType, sf, df); } /** @@ -136,19 +150,6 @@ * Test a method that reverses a data structure */ public void testSerReverseData() throws Exception { - Class javaType = Data.class; - QName xmlType = new QName("urn:foo", "Data"); - BeanSerializerFactory sf = new BeanSerializerFactory(javaType, xmlType); - BeanDeserializerFactory df = new BeanDeserializerFactory(javaType, xmlType); - - TypeMappingRegistry tmr = engine.getTypeMappingRegistry(); - TypeMapping tm = (TypeMapping) tmr.getTypeMapping(Constants.URI_CURRENT_SOAP_ENC); - if (tm == null || tm == tmr.getDefaultTypeMapping()) { - tm = (TypeMapping) tmr.createTypeMapping(); - tmr.register(Constants.URI_CURRENT_SOAP_ENC, tm); - } - tm.register(javaType, xmlType, sf, df); - // invoke the service and verify the result String arg = "<arg0 xmlns:foo=\"urn:foo\" xsi:type=\"foo:Data\">"; arg += "<field1>5</field1><field2>abc</field2><field3>3</field3>"; @@ -161,19 +162,6 @@ * Test a method that reverses a data structure */ public void testReverseDataWithUntypedParam() throws Exception { - Class javaType = Data.class; - QName xmlType = new QName("urn:foo", "Data"); - BeanSerializerFactory sf = new BeanSerializerFactory(javaType, xmlType); - BeanDeserializerFactory df = new BeanDeserializerFactory(javaType, xmlType); - - TypeMappingRegistry tmr = engine.getTypeMappingRegistry(); - TypeMapping tm = (TypeMapping) tmr.getTypeMapping(Constants.URI_CURRENT_SOAP_ENC); - if (tm == null || tm == tmr.getDefaultTypeMapping()) { - tm = (TypeMapping) tmr.createTypeMapping(); - tmr.register(Constants.URI_CURRENT_SOAP_ENC, tm); - } - tm.register(javaType, xmlType, sf, df); - // invoke the service and verify the result String arg = "<arg0 xmlns:foo=\"urn:foo\">"; arg += "<field1>5</field1><field2>abc</field2><field3>3</field3>"; @@ -181,24 +169,22 @@ Data expected = new Data(3, "cba", 5); assertEquals("Did not reverse data as expected", expected, rpc("reverseData", arg, true)); } - + + /** + * Test out-of-order parameters, using the names to match + */ + public void testOutOfOrderParams() throws Exception { + String body = "<a2>world!</a2><a1>Hello, </a1>"; + String expected = "Hello, world!"; + assertEquals("Concatenated value was wrong", + expected, + rpc("concatenate", body, true)); + } + /** * Test DOM round tripping */ public void testArgAsDOM() throws Exception { - Class javaType = Data.class; - QName xmlType = new QName("urn:foo", "Data"); - BeanSerializerFactory sf = new BeanSerializerFactory(javaType, xmlType); - BeanDeserializerFactory df = new BeanDeserializerFactory(javaType, xmlType); - - TypeMappingRegistry tmr = engine.getTypeMappingRegistry(); - TypeMapping tm = (TypeMapping) tmr.getTypeMapping(Constants.URI_CURRENT_SOAP_ENC); - if (tm == null || tm == tmr.getDefaultTypeMapping()) { - tm = (TypeMapping) tmr.createTypeMapping(); - tmr.register(Constants.URI_CURRENT_SOAP_ENC, tm); - } - tm.register(javaType, xmlType, sf, df); - // invoke the service and verify the result String arg = "<arg0 xmlns:foo=\"urn:foo\">"; arg += "<field1>5</field1><field2>abc</field2><field3>3</field3>"; @@ -232,8 +218,10 @@ TestSerializedRPC tester = new TestSerializedRPC("Test Serialized RPC"); tester.testSerReverseString(); tester.testSerReverseData(); + tester.testReverseDataWithUntypedParam(); tester.testArgAsDOM(); tester.testOverloadedMethodDispatch(); + tester.testOutOfOrderParams(); } catch (Exception e) { e.printStackTrace(); } 1.16 +14 -7 xml-axis/java/test/encoding/TestArrayListConversions.java Index: TestArrayListConversions.java =================================================================== RCS file: /home/cvs/xml-axis/java/test/encoding/TestArrayListConversions.java,v retrieving revision 1.15 retrieving revision 1.16 diff -u -r1.15 -r1.16 --- TestArrayListConversions.java 13 Mar 2002 06:02:46 -0000 1.15 +++ TestArrayListConversions.java 22 Mar 2002 16:08:32 -0000 1.16 @@ -18,7 +18,7 @@ public class TestArrayListConversions extends TestCase { private static final String SERVICE_NAME = "TestArrayConversions"; - private Call call; + private AxisServer server; public TestArrayListConversions() { super("service"); @@ -49,18 +49,13 @@ public void init() { try { - Service ss = new Service(); - SimpleProvider provider = new SimpleProvider(); - AxisServer server = new AxisServer(provider); + server = new AxisServer(provider); SOAPService service = new SOAPService(new RPCProvider()); service.setOption("className", "test.encoding.TestArrayListConversions"); service.setOption("allowedMethods", "*"); provider.deployService(SERVICE_NAME, service); - - call = (Call) ss.createCall(); - call.setTransport(new LocalTransport(server)); } catch (Exception exp) { exp.printStackTrace(); } @@ -68,6 +63,9 @@ } public void testVectorConversion() throws Exception { + Call call = new Call(new Service()); + call.setTransport(new LocalTransport(server)); + Vector v = new Vector(); v.addElement("Hi there!"); v.addElement("This'll be a SOAP Array and then a LinkedList!"); @@ -77,6 +75,9 @@ } public void testLinkedListConversion() throws Exception { + Call call = new Call(new Service()); + call.setTransport(new LocalTransport(server)); + LinkedList l = new LinkedList(); l.add("Linked list item #1"); l.add("Second linked list item"); @@ -88,6 +89,9 @@ } public void testArrayConversion() throws Exception { + Call call = new Call(new Service()); + call.setTransport(new LocalTransport(server)); + Vector v = new Vector(); v.addElement("Hi there!"); v.addElement("This'll be a SOAP Array"); @@ -103,6 +107,9 @@ * back, and that it matches the data we send. */ public void testReturnAsVector() throws Exception { + Call call = new Call(new Service()); + call.setTransport(new LocalTransport(server)); + LinkedList l = new LinkedList(); l.add("Linked list item #1"); l.add("Second linked list item");