You are viewing a plain text version of this content. The canonical link for it is here.
Posted to java-dev@axis.apache.org by gd...@apache.org on 2002/03/22 17:08:33 UTC

cvs commit: xml-axis/java/test/encoding TestArrayListConversions.java

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 (gdaniels@apache.org)
    */
  @@ -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 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.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 (gdaniels@apache.org)
   */
  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 apache@apache.org.
    *
    * 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 (gdaniels@apache.org)
  + */
   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");