FileDocCategorySizeDatePackage
JavaServiceDesc.javaAPI DocApache Axis 1.456644Sat Apr 22 18:57:26 BST 2006org.apache.axis.description

JavaServiceDesc

public class JavaServiceDesc extends Object implements ServiceDesc
A ServiceDesc is an abstract description of a service. 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)

Fields Summary
protected static Log
log
private String
name
The name of this service
private String
documentation
The documentation of this service
private org.apache.axis.constants.Style
style
Style/Use
private org.apache.axis.constants.Use
use
private boolean
useSet
private ArrayList
operations
Our operations - a list of OperationDescs
private List
namespaceMappings
A collection of namespaces which will map to this service
private String
wsdlFileName
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
endpointURL
An endpoint URL which someone has specified for this service. If this is set, WSDL generation will pick it up instead of defaulting to the transport URL.
private HashMap
properties
Place to store user-extensible service-related properties
private HashMap
name2OperationsMap
Lookup caches
private HashMap
qname2OperationsMap
private transient HashMap
method2OperationMap
private List
allowedMethods
null allows everything, an empty ArrayList allows nothing
private List
disallowedMethods
List if disallowed methods
private Class
implClass
Implementation class
private boolean
isSkeletonClass
Is the implementation a Skeleton? If this is true, it will generate a Fault to provide OperationDescs via WSDD.
private transient Method
skelMethod
Cached copy of the skeleton "getOperationDescByName" method
private ArrayList
stopClasses
Classes at which we should stop looking up the inheritance chain when introspecting
private transient HashMap
method2ParamsMap
Lookup caches
private OperationDesc
messageServiceDefaultOp
private ArrayList
completedNames
Method names for which we have completed any introspection necessary
private TypeMapping
tm
Our typemapping for resolving Java<->XML type issues
private TypeMappingRegistry
tmr
private boolean
haveAllSkeletonMethods
private boolean
introspectionComplete
Constructors Summary
public JavaServiceDesc()
Default constructor


           
      
    
Methods Summary
public voidaddOperationDesc(OperationDesc operation)

        operations.add(operation);
        operation.setParent(this);
        if (name2OperationsMap == null) {
            name2OperationsMap = new HashMap();
        }

        // Add name to name2Operations Map
        String name = operation.getName();
        ArrayList overloads = (ArrayList)name2OperationsMap.get(name);
        if (overloads == null) {
            overloads = new ArrayList();
            name2OperationsMap.put(name, overloads);
        } else if (JavaUtils.isTrue(
                AxisProperties.getProperty(Constants.WSIBP11_COMPAT_PROPERTY)) &&
                overloads.size() > 0) {
            throw new RuntimeException(Messages.getMessage("noOverloadedOperations", name));
        }
        overloads.add(operation);
    
private intcheckMessageMethod(java.lang.reflect.Method method)

        // Collect the types so we know what we're dealing with in the target
        // method.
        Class [] params = method.getParameterTypes();

        if (params.length == 1) {
            if ((params[0] == Element[].class) &&
                    (method.getReturnType() == Element[].class)) {
                return OperationDesc.MSG_METHOD_ELEMENTARRAY;
            }

            if ((params[0] == SOAPBodyElement[].class) &&
                    (method.getReturnType() == SOAPBodyElement[].class)) {
                return OperationDesc.MSG_METHOD_BODYARRAY;
            }

            if ((params[0] == Document.class) &&
                    (method.getReturnType() == Document.class)) {
                return OperationDesc.MSG_METHOD_DOCUMENT;
            }
        } else if (params.length == 2) {
            if (((params[0] == SOAPEnvelope.class) &&
                    (params[1] == SOAPEnvelope.class)) || 
                ((params[0] == javax.xml.soap.SOAPEnvelope.class) &&
                    (params[1] == javax.xml.soap.SOAPEnvelope.class)) &&
                    (method.getReturnType() == void.class)){
                return OperationDesc.MSG_METHOD_SOAPENVELOPE;
            }
        }
        if( null != allowedMethods && !allowedMethods.isEmpty() )
          throw new InternalException (Messages.getMessage("badMsgMethodParams",
                                                         method.getName()));
        return    OperationDesc.MSG_METHOD_NONCONFORMING;                                              
    
private voidcreateFaultMetadata(java.lang.reflect.Method method, OperationDesc operation)

        // Create Exception Types
        Class[] exceptionTypes = method.getExceptionTypes();

        for (int i=0; i < exceptionTypes.length; i++) {
            // Every remote method declares a java.rmi.RemoteException
            // Only interested in application specific exceptions.
            // Ignore java and javax package exceptions.
            Class ex = exceptionTypes[i];
            if (ex != java.rmi.RemoteException.class &&
                ex != org.apache.axis.AxisFault.class &&
                !ex.getName().startsWith("java.") &&
                !ex.getName().startsWith("javax.")) {

                // For JSR 101 v.1.0, there is a simple fault mapping
                // and a complexType fault mapping...both mappings
                // generate a class that extends (directly or indirectly)
                // Exception.
                // When converting java back to wsdl it is not possible
                // to determine which way to do the mapping,
                // so it is always mapped back using the complexType
                // fault mapping because it is more useful (i.e. it
                // establishes a hierarchy of exceptions).  Note that this
                // will not cause any roundtripping problems.
                // Rich


                /* Old Simple Type Mode
                Field[] f = ex.getDeclaredFields();
                ArrayList exceptionParams = new ArrayList();
                for (int j = 0; j < f.length; j++) {
                    int mod = f[j].getModifiers();
                    if (Modifier.isPublic(mod) &&
                         !Modifier.isStatic(mod)) {
                        QName qname = new QName("", f[j].getName());
                        QName typeQName = tm.getTypeQName(f[j].getType());
                        ParameterDesc param = new ParameterDesc(qname,
                                                                ParameterDesc.IN,
                                                                typeQName);
                        param.setJavaType(f[j].getType());
                        exceptionParams.add(param);
                    }
                }
                String pkgAndClsName = ex.getName();
                FaultDesc fault = new FaultDesc();
                fault.setName(pkgAndClsName);
                fault.setParameters(exceptionParams);
                operation.addFault(fault);
                */

                FaultDesc fault = operation.getFaultByClass(ex, false);
                boolean isNew;
                
                // If we didn't find one, create a new one
                if (fault == null) {
                    fault = new FaultDesc();
                    isNew = true;
                } else {
                    isNew = false;
                }
                
                // Try to fil in any parts of the faultDesc that aren't there
                
                // XMLType
                QName xmlType = fault.getXmlType();
                if (xmlType == null) {
                    fault.setXmlType(getTypeMapping().getTypeQName(ex));
                }
                
                // Name and Class Name
                String pkgAndClsName = ex.getName();
                if (fault.getClassName() == null) {
                    fault.setClassName(pkgAndClsName);
                }
                if (fault.getName() == null) {
                    String name = pkgAndClsName.substring(
                            pkgAndClsName.lastIndexOf('.") + 1,
                            pkgAndClsName.length());
                    fault.setName(name);
                }
                
                // Parameters
                // We add a single parameter which points to the type
                if (fault.getParameters() == null) {
                    if (xmlType == null) {
                        xmlType = getTypeMapping().getTypeQName(ex);
                    }
                    QName qname = fault.getQName();
                    if (qname == null) {
                        qname = new QName("", "fault");
                    }
                    ParameterDesc param = new ParameterDesc(
                            qname,
                            ParameterDesc.IN,
                            xmlType);
                    param.setJavaType(ex);
                    ArrayList exceptionParams = new ArrayList();
                    exceptionParams.add(param);
                    fault.setParameters(exceptionParams);
                }
                
                // QName
                if (fault.getQName() == null) {
                    fault.setQName(new QName(pkgAndClsName));
                }

                if (isNew) {
                    // Add the fault to the operation
                    operation.addFault(fault);
                }
            }
        }
    
private voidcreateOperationForMethod(java.lang.reflect.Method method)
Make an OperationDesc from a Java method. In the absence of deployment metadata, this code will introspect a Method and create an appropriate OperationDesc. If the class implements the Skeleton interface, we will use the metadata from there in constructing the OperationDesc. If not, we use parameter names from the bytecode debugging info if available, or "in0", "in1", etc. if not.

        // If we've already got it, never mind
        if (method2OperationMap.get(method) != null) {
            return;
        }

        Class [] paramTypes = method.getParameterTypes();

        // And if we've already got an exact match (i.e. an override),
        // never mind

        ArrayList overloads = name2OperationsMap == null ? null :
                (ArrayList)name2OperationsMap.get(method.getName());
        if (overloads != null && !overloads.isEmpty()) {
            // Search each OperationDesc that already has a Method
            // associated with it, and check for parameter type equivalence.
            for (int i = 0; i < overloads.size(); i++) {
                OperationDesc op = (OperationDesc)overloads.get(i);
                Method checkMethod = op.getMethod();
                if (checkMethod != null) {
                    Class [] others = checkMethod.getParameterTypes();
                    if (paramTypes.length == others.length) {
                        int j = 0;
                        for (; j < others.length; j++) {
                            if (!others[j].equals(paramTypes[j]))
                                break;
                        }
                        // If we got all the way through, we have a match.
                        if (j == others.length)
                            return;
                    }
                }
            }
        }

        boolean isWSICompliant = JavaUtils.isTrue(
                AxisProperties.getProperty(Constants.WSIBP11_COMPAT_PROPERTY));
        
        // Make an OperationDesc, fill in common stuff
        OperationDesc operation = new OperationDesc();
        
        // If we're WS-I compliant, we can't have overloaded operation names.
        // If we find duplicates, we generate unique names for them and map
        // those names to the correct Method.
        String name = method.getName();
        if (isWSICompliant && name2OperationsMap != null) {
            Collection methodNames = name2OperationsMap.keySet();
            name = JavaUtils.getUniqueValue(methodNames, name);
        }
        operation.setName(name);
        String defaultNS = "";
        if (namespaceMappings != null && !namespaceMappings.isEmpty()) {
            // If we have a default namespace mapping, require callers to
            // use that namespace.
            defaultNS = (String)namespaceMappings.get(0);
        }
        if(defaultNS.length() == 0) {
            defaultNS = Namespaces.makeNamespace(method.getDeclaringClass().getName());
        }
        operation.setElementQName(new QName(defaultNS, name));
        operation.setMethod(method);

        // If this is a MESSAGE style service, set up the OperationDesc
        // appropriately.
        if (style == Style.MESSAGE) {
            int messageOperType = checkMessageMethod(method);
            if(messageOperType == OperationDesc.MSG_METHOD_NONCONFORMING) return;
            if (messageOperType == -1) {
                throw new InternalException("Couldn't match method to any of the allowable message-style patterns!");
            }
            operation.setMessageOperationStyle(messageOperType);
            operation.setReturnClass(Object.class);
            operation.setReturnType(Constants.XSD_ANYTYPE);
        } else {
            // For other styles, continue here.
            Class retClass = method.getReturnType();
            operation.setReturnClass(retClass);
            QName typeQName = getTypeQName(retClass);
            operation.setReturnType(typeQName);

            String [] paramNames = getParamNames(method);

            for (int k = 0; k < paramTypes.length; k++) {
                Class type = paramTypes[k];
                ParameterDesc paramDesc = new ParameterDesc();
                // param should be unqualified if we're using rpc style,
                // or should use the operation's namespace if its document style
                String paramNamespace = (this.style == Style.RPC ? "" : operation.getElementQName().getNamespaceURI());

                // If we have a name for this param, use it, otherwise call
                // it "in*"
                if (paramNames != null && paramNames[k] != null &&
                        paramNames[k].length()>0) {
                    paramDesc.setQName(new QName(paramNamespace, paramNames[k]));
                } else {
                    paramDesc.setQName(new QName(paramNamespace, "in" + k));
                }

                // If it's a Holder, mark it INOUT, and set the XML type QName
                // to the held type.  Otherwise it's IN.

                Class heldClass = JavaUtils.getHolderValueType(type);
                if (heldClass != null) {
                    paramDesc.setMode(ParameterDesc.INOUT);
                    paramDesc.setTypeQName(getTypeQName(heldClass));
                } else {
                    paramDesc.setMode(ParameterDesc.IN);
                    paramDesc.setTypeQName(getTypeQName(type));
                }
                paramDesc.setJavaType(type);
                operation.addParameter(paramDesc);
            }
        }

        createFaultMetadata(method, operation);

        addOperationDesc(operation);
        method2OperationMap.put(method, operation);
    
private voidcreateOperationsForName(java.lang.Class implClass, java.lang.String methodName)
Look for methods matching this name, and for each one, create an OperationDesc (if it's not already in our list). TODO: Make this more efficient

        // If we're a Skeleton deployment, skip the statics.
        if (isSkeletonClass) {
            if (methodName.equals("getOperationDescByName") ||
                methodName.equals("getOperationDescs"))
                return;
        }
        
        Method [] methods = getMethods(implClass);

        for (int i = 0; i < methods.length; i++) {
            Method method = methods[i];
            if (Modifier.isPublic(method.getModifiers()) &&
                method.getName().equals(methodName) &&
                !isServiceLifeCycleMethod(implClass, method)) {
                createOperationForMethod(method);
            }
        }

        Class superClass = implClass.getSuperclass();
        if (superClass != null &&
                !superClass.getName().startsWith("java.") &&
                !superClass.getName().startsWith("javax.") &&
                    (stopClasses == null ||
                        !stopClasses.contains(superClass.getName()))) {
            createOperationsForName(superClass, methodName);
        }
    
public java.util.ListgetAllowedMethods()

        return allowedMethods;
    
public java.lang.StringgetDefaultNamespace()

        if (namespaceMappings == null || namespaceMappings.isEmpty())
            return null;
        return (String)namespaceMappings.get(0);
    
public java.util.ListgetDisallowedMethods()

        return disallowedMethods;
    
public java.lang.StringgetDocumentation()
get the documentation for the service

    	return documentation; 
    
public java.lang.StringgetEndpointURL()

        return endpointURL;
    
public java.lang.ClassgetImplClass()

        return implClass;
    
private java.lang.reflect.Method[]getMethods(java.lang.Class implClass)

        if (implClass.isInterface()){
            // only return methods that are not part of start classes
            List methodsList = new ArrayList();
            Method[] methods = implClass.getMethods();
            if (methods != null) {
                for (int i = 0; i < methods.length; i++) {
                    String declaringClass = methods[i].getDeclaringClass().getName();
                    if (!declaringClass.startsWith("java.") &&
                        !declaringClass.startsWith("javax.")) {
                        methodsList.add(methods[i]);
                    }
                }
            }
            return (Method[])methodsList.toArray(new Method[]{}); 
        } else {
            return implClass.getDeclaredMethods();
        }
    
public java.lang.StringgetName()
the name of the service

        return name;
    
public OperationDescgetOperationByElementQName(javax.xml.namespace.QName qname)
Map an XML QName to an operation. Returns the first one it finds in the case of mulitple matches.

return
null for no match

        OperationDesc [] overloads = getOperationsByQName(qname);

        // Return the first one....
        if ((overloads != null) && overloads.length > 0)
            return overloads[0];

        return null;
    
public OperationDescgetOperationByName(java.lang.String methodName)
Return an operation matching the given method name. Note that if we have multiple overloads for this method, we will return the first one.

return
null for no match

        // If we need to load up operations from introspection data, do it.
        // This returns fast if we don't need to do anything, so it's not very
        // expensive.
        getSyncedOperationsForName(implClass, methodName);

        if (name2OperationsMap == null)
            return null;

        ArrayList overloads = (ArrayList)name2OperationsMap.get(methodName);
        if (overloads == null) {
            return null;
        }

        return (OperationDesc)overloads.get(0);
    
public java.util.ArrayListgetOperations()
get all the operations as a list of OperationDescs. this method triggers an evaluation of the valid operations by introspection, so use sparingly

return
reference to the operations array. This is not a copy

        loadServiceDescByIntrospection();  // Just in case...
        return operations;
    
public OperationDesc[]getOperationsByName(java.lang.String methodName)
get all overloaded operations by name

param
methodName
return
null for no match, or an array of OperationDesc objects

        getSyncedOperationsForName(implClass, methodName);

        if (name2OperationsMap == null)
            return null;

        ArrayList overloads = (ArrayList)name2OperationsMap.get(methodName);
        if (overloads == null) {
            return null;
        }

        OperationDesc [] array = new OperationDesc [overloads.size()];
        return (OperationDesc[])overloads.toArray(array);
    
public OperationDesc[]getOperationsByQName(javax.xml.namespace.QName qname)
Return all operations which match this QName (i.e. get all the overloads)

return
null for no match

        // Look in our mapping of QNames -> operations.

        // But first, let's make sure we've initialized said mapping....
        initQNameMap();

        ArrayList overloads = (ArrayList)qname2OperationsMap.get(qname);
        if (overloads == null) {
            // Nothing specifically matching this QName.
            if (name2OperationsMap != null) {
                if ((isWrapped() ||
                     ((style == Style.MESSAGE) &&
                      (getDefaultNamespace() == null)))) {
                    // Try ignoring the namespace....?
                    overloads = (ArrayList) name2OperationsMap.get(qname.getLocalPart());
                } else {
                    // TODO the above code is weird: a JavaServiceDesc can  be document or rpc and
                    // still define a WSDL operation using a wrapper style  mapping.
                    // The following code handles this case.
                    Object ops = name2OperationsMap.get(qname.getLocalPart());
                    if (ops != null) {
                        overloads = new ArrayList((Collection) ops);
                        for (Iterator iter = overloads.iterator(); iter.hasNext();) {
                            OperationDesc operationDesc = (OperationDesc) iter.next();
                            if (Style.WRAPPED != operationDesc.getStyle()) {
                                iter.remove();
                            }
                        }
                    }
                }
            }
            // Handle the case where a single Message-style operation wants
            // to accept anything.
            if ((style == Style.MESSAGE) && (messageServiceDefaultOp != null))
                return new OperationDesc [] { messageServiceDefaultOp };

            if (overloads == null)
                return null;
        }

        getSyncedOperationsForName(implClass,
                                   ((OperationDesc)overloads.get(0)).getName());

        // Sort the overloads by number of arguments - prevents us calling methods
        // with more parameters than supplied in the request (with missing parameters
        // defaulted to null) when a perfectly good method exists with exactly the
        // supplied parameters.
        Collections.sort(overloads,
            new Comparator() {
                public int compare(Object o1, Object o2)
                {
                    Method meth1 = ((OperationDesc)o1).getMethod();
                    Method meth2 = ((OperationDesc)o2).getMethod();
                    return (meth1.getParameterTypes().length -
                                         meth2.getParameterTypes().length);
                }
            });

        OperationDesc [] array = new OperationDesc [overloads.size()];
        return (OperationDesc[])overloads.toArray(array);
    
private java.lang.String[]getParamNames(java.lang.reflect.Method method)

        synchronized (method2ParamsMap) {
            String [] paramNames = (String []) method2ParamsMap.get(method);
            if(paramNames != null)
                return paramNames;
            paramNames = ParamNameExtractor.getParameterNamesFromDebugInfo(method);
            method2ParamsMap.put(method, paramNames);
            return paramNames;
        }
    
public java.lang.ObjectgetProperty(java.lang.String name)

        if (properties == null)
            return null;

        return properties.get(name);
    
public java.util.ArrayListgetStopClasses()

        return stopClasses;
    
public org.apache.axis.constants.StylegetStyle()
What kind of service is this?

return

        return style;
    
private voidgetSyncedOperationsForName(java.lang.Class implClass, java.lang.String methodName)
Makes sure we have completely synchronized OperationDescs with the implementation class.

        // If we're a Skeleton deployment, skip the statics.
        if (isSkeletonClass) {
            if (methodName.equals("getOperationDescByName") ||
                methodName.equals("getOperationDescs"))
                return;
        }
        
        // If we have no implementation class, don't worry about it (we're
        // probably on the client)
        if (implClass == null)
            return;

        // If we're done introspecting, or have completed this method, return
        if (completedNames == null || completedNames.contains(methodName))
            return;

        // Skip it if it's not a sanctioned method name
        if ((allowedMethods != null) &&
            !allowedMethods.contains(methodName))
            return;

        if ((disallowedMethods != null) &&
            disallowedMethods.contains(methodName))
            return;

        // If we're a skeleton class, make sure we don't already have any
        // OperationDescs for this name (as that might cause conflicts),
        // then load them up from the Skeleton class.
        if (isSkeletonClass && !haveAllSkeletonMethods) {
            // FIXME : Check for existing ones and fault if found

            if (skelMethod == null) {
                // Grab metadata from the Skeleton for parameter info
                try {
                    skelMethod = implClass.getDeclaredMethod(
                                            "getOperationDescByName",
                                            new Class [] { String.class });
                } catch (NoSuchMethodException e) {
                } catch (SecurityException e) {
                }
                if (skelMethod == null) {
                    // FIXME : Throw an error?
                    return;
                }
            }
            try {
                List skelList =
                        (List)skelMethod.invoke(implClass,
                                new Object [] { methodName });
                if (skelList != null) {
                    Iterator i = skelList.iterator();
                    while (i.hasNext()) {
                        addOperationDesc((OperationDesc)i.next());
                    }
                }
            } catch (IllegalAccessException e) {
                if(log.isDebugEnabled()) {
                    log.debug(Messages.getMessage("exception00"), e);
                }
                return;
            } catch (IllegalArgumentException e) {
                if(log.isDebugEnabled()) {
                    log.debug(Messages.getMessage("exception00"), e);
                }
                return;
            } catch (InvocationTargetException e) {
                if(log.isDebugEnabled()) {
                    log.debug(Messages.getMessage("exception00"), e);
                }
                return;
            }
        }

        // OK, go find any current OperationDescs for this method name and
        // make sure they're synced with the actual class.
        if (name2OperationsMap != null) {
            ArrayList currentOverloads =
                    (ArrayList)name2OperationsMap.get(methodName);
            if (currentOverloads != null) {
                // For each one, sync it to the implementation class' methods
                for (Iterator i = currentOverloads.iterator(); i.hasNext();) {
                    OperationDesc oper = (OperationDesc) i.next();
                    if (oper.getMethod() == null) {
                        syncOperationToClass(oper, implClass);
                    }
                }
            }
        }

        // Now all OperationDescs from deployment data have been completely
        // filled in.  So we now make new OperationDescs for any method
        // overloads which were not covered above.
        // NOTE : This is the "lenient" approach, which allows you to
        // specify one overload and still get the others by introspection.
        // We could equally well return above if we found OperationDescs,
        // and have a rule that if you specify any overloads, you must specify
        // all the ones you want accessible.

        createOperationsForName(implClass, methodName);

        // Note that we never have to look at this method name again.
        completedNames.add(methodName);
    
public TypeMappinggetTypeMapping()

        if(tm == null) {
            return DefaultTypeMappingImpl.getSingletonDelegate();
//            throw new RuntimeException(Messages.getMessage("noDefaultTypeMapping00"));
        }
        return tm;
    
public TypeMappingRegistrygetTypeMappingRegistry()

        if (tmr == null) {
            tmr = new TypeMappingRegistryImpl(false);
        }
        return tmr;
    
private javax.xml.namespace.QNamegetTypeQName(java.lang.Class javaClass)

        QName typeQName;
        TypeMapping tm = getTypeMapping();
        if (style == Style.RPC) {
            typeQName = tm.getTypeQName(javaClass);
        } else {
            typeQName = tm.getTypeQNameExact(javaClass);
            if (typeQName == null && javaClass.isArray()) {
                typeQName = tm.getTypeQName(javaClass.getComponentType());
            } else {
                typeQName = tm.getTypeQName(javaClass);
            }
        }
        return typeQName;
    
private java.lang.StringgetUniqueOperationName(java.lang.String name)

        int i = 1;
        String candidate;
        do {
            candidate = name + i++;
        } while (name2OperationsMap.get(candidate) != null);

        return candidate;
    
public org.apache.axis.constants.UsegetUse()
What kind of use is this?

return

        return use;
    
public java.lang.StringgetWSDLFile()
the wsdl file of the service. When null, it means that the wsdl should be autogenerated

return
filename or null

        return wsdlFileName;
    
private synchronized voidinitQNameMap()

        if (qname2OperationsMap == null) {
            loadServiceDescByIntrospection();

            qname2OperationsMap = new HashMap();
            for (Iterator i = operations.iterator(); i.hasNext();) {
                OperationDesc operationDesc = (OperationDesc) i.next();
                QName qname = operationDesc.getElementQName();
                ArrayList list = (ArrayList)qname2OperationsMap.get(qname);
                if (list == null) {
                    list = new ArrayList();
                    qname2OperationsMap.put(qname, list);
                }
                list.add(operationDesc);
            }
        }
    
public booleanisInitialized()

        return implClass != null;
    
private booleanisServiceLifeCycleMethod(java.lang.Class implClass, java.lang.reflect.Method m)
Is this method from ServiceLifeCycle interface?

param
m
return
true if this method is from ServiceLifeCycle interface

        if(javax.xml.rpc.server.ServiceLifecycle.class.isAssignableFrom(implClass)) {
            String methodName = m.getName(); 

            if(methodName.equals("init")) {
                // Check if the method signature is 
                // "public abstract void init(Object context) throws ServiceException;"
                Class[] classes = m.getParameterTypes(); 
                if(classes != null && 
                   classes.length == 1 && 
                   classes[0] == Object.class && 
                   m.getReturnType() == Void.TYPE) {
                    return true;
                }
            } else if (methodName.equals("destroy")){
                // Check if the method signature is 
                // "public abstract void destroy();"
                Class[] classes = m.getParameterTypes(); 
                if(classes != null && 
                   classes.length == 0 &&
                   m.getReturnType() == Void.TYPE) {
                    return true;
                }
            }
        }
        return false;
    
public booleanisWrapped()
Determine whether or not this is a "wrapped" invocation, i.e. whether the outermost XML element of the "main" body element represents a method call, with the immediate children of that element representing arguments to the method.

return
true if this is wrapped (i.e. RPC or WRAPPED style), false otherwise

        return ((style == Style.RPC) || 
                (style == Style.WRAPPED));
    
public voidloadServiceDescByIntrospection()
Fill in a service description by introspecting the implementation class.

        loadServiceDescByIntrospection(implClass);

        // Setting this to null means there is nothing more to do, and it
        // avoids future string compares.
        completedNames = null;
    
public voidloadServiceDescByIntrospection(java.lang.Class implClass)
Fill in a service description by introspecting the implementation class.

        if (introspectionComplete || implClass == null) {
            return;
        }

        // set the implementation class for the service description
        this.implClass = implClass;
        if (Skeleton.class.isAssignableFrom(implClass)) {
            isSkeletonClass = true;
            loadSkeletonOperations();
        }

        /** If the class knows what it should be exporting,
        * respect its wishes.
        */
        AxisServiceConfig axisConfig = null;
        try {
            Method method = implClass.getDeclaredMethod(
                    "getAxisServiceConfig", new Class [] {});
            if (method != null && Modifier.isStatic(method.getModifiers())) {
                axisConfig = (AxisServiceConfig)method.invoke(null, null);
            }
        } catch (Exception e) {
            // No problem, just continue without...
        }

        if (axisConfig != null) {
            String allowedMethodsStr = axisConfig.getAllowedMethods();
            if (allowedMethodsStr != null && !"*".equals(allowedMethodsStr)) {
                ArrayList methodList = new ArrayList();
                StringTokenizer tokenizer =
                        new StringTokenizer(allowedMethodsStr, " ,");
                while (tokenizer.hasMoreTokens()) {
                    methodList.add(tokenizer.nextToken());
                }
                setAllowedMethods(methodList);
            }
        }

        loadServiceDescByIntrospectionRecursive(implClass);

        // All operations should now be synchronized.  Check it.
        for (Iterator iterator = operations.iterator(); iterator.hasNext();) {
            OperationDesc operation = (OperationDesc) iterator.next();
            if (operation.getMethod() == null) {
                throw new InternalException(
                        Messages.getMessage("badWSDDOperation",
                                            operation.getName(),
                                            "" + operation.getNumParams()));
            }
        }

        if ((style == Style.MESSAGE) && operations.size() == 1) {
            messageServiceDefaultOp = (OperationDesc)operations.get(0);
        }

        introspectionComplete = true;
    
public voidloadServiceDescByIntrospection(java.lang.Class cls, TypeMapping tm)
Fill in a service description by introspecting the implementation class. This version takes the implementation class and the in-scope TypeMapping.

        // Should we complain if the implClass changes???
        implClass = cls;
        this.tm = tm;

        if (Skeleton.class.isAssignableFrom(implClass)) {
            isSkeletonClass = true;
            loadSkeletonOperations();
        }

        loadServiceDescByIntrospection();
    
private voidloadServiceDescByIntrospectionRecursive(java.lang.Class implClass)
Recursive helper class for loadServiceDescByIntrospection

        if (Skeleton.class.equals(implClass)) {
            return;
        }

        Method [] methods = getMethods(implClass);

        for (int i = 0; i < methods.length; i++) {
            if (Modifier.isPublic(methods[i].getModifiers()) && !isServiceLifeCycleMethod(implClass, methods[i])) {
                getSyncedOperationsForName(implClass, methods[i].getName());
            }
        }

        if (implClass.isInterface()) {
            Class [] superClasses = implClass.getInterfaces();
            for (int i = 0; i < superClasses.length; i++) {
                Class superClass = superClasses[i];
                if (!superClass.getName().startsWith("java.") &&
                        !superClass.getName().startsWith("javax.") &&
                        (stopClasses == null ||
                        !stopClasses.contains(superClass.getName()))) {
                    loadServiceDescByIntrospectionRecursive(superClass);
                }
            }
        } else {
            Class superClass = implClass.getSuperclass();
            if (superClass != null &&
                    !superClass.getName().startsWith("java.") &&
                    !superClass.getName().startsWith("javax.") &&
                    (stopClasses == null ||
                        !stopClasses.contains(superClass.getName()))) {
                loadServiceDescByIntrospectionRecursive(superClass);
            }
        }
    
private voidloadSkeletonOperations()

        Method method = null;
        try {
            method = implClass.getDeclaredMethod("getOperationDescs",
                                                 new Class [] {});
        } catch (NoSuchMethodException e) {
        } catch (SecurityException e) {
        }
        if (method == null) {
            // FIXME : Throw an error?
            return;
        }

        try {
            Collection opers = (Collection)method.invoke(implClass, null);
            for (Iterator i = opers.iterator(); i.hasNext();) {
                OperationDesc skelDesc = (OperationDesc)i.next();
                addOperationDesc(skelDesc);
            }
        } catch (IllegalAccessException e) {
            if(log.isDebugEnabled()) {
                log.debug(Messages.getMessage("exception00"), e);
            }
            return;
        } catch (IllegalArgumentException e) {
            if(log.isDebugEnabled()) {
                log.debug(Messages.getMessage("exception00"), e);
            }
            return;
        } catch (InvocationTargetException e) {
            if(log.isDebugEnabled()) {
                log.debug(Messages.getMessage("exception00"), e);
            }
            return;
        }
        haveAllSkeletonMethods = true;
    
public voidremoveOperationDesc(OperationDesc operation)

        operations.remove(operation);
        operation.setParent(null);

        if (name2OperationsMap != null) {
            String name = operation.getName();
            ArrayList overloads = (ArrayList)name2OperationsMap.get(name);
            if (overloads != null) {
                overloads.remove(operation);
                if (overloads.size() == 0) {
                    name2OperationsMap.remove(name);
                }
            }
        }
        
        if (qname2OperationsMap != null) {
            QName qname = operation.getElementQName();
            ArrayList list = (ArrayList)qname2OperationsMap.get(qname);
            if (list != null) {
                list.remove(operation);
            }
        }
        
        if (method2OperationMap != null) {
            Method method = operation.getMethod();
            if (method != null) {
                method2OperationMap.remove(method);
            }
        }
    
public voidsetAllowedMethods(java.util.List allowedMethods)

        this.allowedMethods = allowedMethods;
    
public voidsetDefaultNamespace(java.lang.String namespace)

        if (namespaceMappings == null)
            namespaceMappings = new ArrayList();
        namespaceMappings.add(0, namespace);
    
public voidsetDisallowedMethods(java.util.List disallowedMethods)

        this.disallowedMethods = disallowedMethods;
    
public voidsetDocumentation(java.lang.String documentation)
set the documentation for the service

    	this.documentation = documentation;
    
public voidsetEndpointURL(java.lang.String endpointURL)

        this.endpointURL = endpointURL;
    
public voidsetImplClass(java.lang.Class implClass)
set the implementation class

Warning: You cannot call getInitializedServiceDesc() after setting this as it uses this to indicate its work has already been done.

param
implClass
throws
IllegalArgumentException if the implementation class is already set

        if (this.implClass != null)
            throw new IllegalArgumentException(
                    Messages.getMessage("implAlreadySet"));

        this.implClass = implClass;
        if (Skeleton.class.isAssignableFrom(implClass)) {
            isSkeletonClass = true;
            loadSkeletonOperations();
        }
    
public voidsetName(java.lang.String name)
the name of the service

param
name

        this.name = name;
    
public voidsetNamespaceMappings(java.util.List namespaces)

        namespaceMappings = namespaces;
    
public voidsetProperty(java.lang.String name, java.lang.Object value)

        if (properties == null) {
            properties = new HashMap();
        }
        properties.put(name, value);
    
public voidsetStopClasses(java.util.ArrayList stopClasses)

        this.stopClasses = stopClasses;
    
public voidsetStyle(org.apache.axis.constants.Style style)

        this.style = style;
        if (!useSet) {
            // Use hasn't been explicitly set, so track style
            use = style == Style.RPC ? Use.ENCODED : Use.LITERAL;
        }
    
public voidsetTypeMapping(TypeMapping tm)

        this.tm = tm;
    
public voidsetTypeMappingRegistry(TypeMappingRegistry tmr)

        this.tmr = tmr;
    
public voidsetUse(org.apache.axis.constants.Use use)

        useSet = true;
        this.use = use;
    
public voidsetWSDLFile(java.lang.String wsdlFileName)
set the wsdl file of the service; this causes the named file to be returned on a ?wsdl, probe, not introspection generated wsdl.

param
wsdlFileName filename or null to re-enable introspection

        this.wsdlFileName = wsdlFileName;
    
private voidsyncOperationToClass(OperationDesc oper, java.lang.Class implClass)
Synchronize an existing OperationDesc to a java.lang.Method. This method is used when the deployer has specified operation metadata and we want to match that up with a real java Method so that the Operation-level dispatch carries us all the way to the implementation. Search the declared methods on the implementation class to find one with an argument list which matches our parameter list.

        // ------------------------------------------------
        // Developer Note:
        //
        // The goal of the sync code is to associate
        // the OperationDesc/ParamterDesc with the
        // target Method.  There are a number of ways to get to this
        // point depending on what information
        // is available.  Here are the main scenarios:
        //
        // A) Deployment with wsdd (non-skeleton):
        //   * OperationDesc/ParameterDesc loaded from deploy.wsdd
        //   * Loaded ParameterDesc does not have javaType,
        //     so it is discovered using the TypeMappingRegistry
        //     (also loaded via deploy.wsdd) and the
        //     typeQName specified by the ParameterDesc.
        //   * Sync occurs using the discovered
        //     javaTypes and the javaTypes of the Method
        //     parameters
        //
        // B) Deployment with no wsdd OperationDesc info (non-skeleton):
        //   * Implementation Class introspected to build
        //     OperationDesc/ParameterDesc.
        //   * ParameterDesc is known via introspection.
        //   * ParameterDesc are discovered using javaType
        //     and TypeMappingRegistry.
        //   * Sync occurs using the introspected
        //     javaTypes and the javaTypes of the Method
        //     parameters
        //
        // C) Deployment with wsdd (skeleton):
        //   * OperationDesc/ParameterDesc loaded from the Skeleton
        //   * In this scenario the ParameterDescs' already
        //     have javaTypes (see E below).
        //   * Sync occurs using the ParameterDesc
        //     javaTypes and the javaTypes of the Method
        //     parameters.
        //
        // D) Commandline Java2WSDL loading non-Skeleton Class/Interface
        //   * Class/Interface introspected to build
        //     OperationDesc/ParameterDesc.
        //   * The javaTypes of the ParameterDesc are set using introspection.
        //   * typeQNames are determined for built-in types using
        //     from the default TypeMappingRegistry.  Other
        //     typeQNames are guessed from the javaType.  Note
        //     that there is no loaded TypeMappingRegistry.
        //   * Sync occurs using the ParameterDesc
        //     javaTypes and the javaTypes of the Method
        //     parameters.
        //
        // E) Commandline Java2WSDL loading Skeleton Class
        //   * OperationDesc/ParameterDesc loaded from Skeleton
        //   * Each ParameterDesc has an appropriate typeQName
        //   * Each ParameterDesc also has a javaType, which is
        //     essential for sync'ing up with the
        //     method since there is no loaded TypeMappingRegistry.
        //   * Syncronization occurs using the ParameterDesc
        //     javaTypes and the javaTypes of the Method
        //     parameters.
        //
        // So in each scenario, the ultimate sync'ing occurs
        // using the javaTypes of the ParameterDescs and the
        // javaTypes of the Method parameters.
        //
        // ------------------------------------------------

        // If we're already mapped to a Java method, no need to do anything.
        if (oper.getMethod() != null)
            return;

        // Find the method.  We do this once for each Operation.
        
        Method[] methods = getMethods(implClass);
        // A place to keep track of possible matches
        Method possibleMatch = null;
        
        for (int i = 0; i < methods.length; i++) {
            Method method = methods[i];
            if (Modifier.isPublic(method.getModifiers()) &&
                    method.getName().equals(oper.getName()) &&
                    method2OperationMap.get(method) == null) {

                if (style == Style.MESSAGE) {
                    int messageOperType = checkMessageMethod(method);
                    if(messageOperType == OperationDesc.MSG_METHOD_NONCONFORMING) continue;
                    if (messageOperType == -1) {
                        throw new InternalException("Couldn't match method to any of the allowable message-style patterns!");
                    }
                    oper.setMessageOperationStyle(messageOperType);

                    // Don't bother checking params if we're message style
                    possibleMatch = method;
                    break;
                }

                // Check params
                Class [] paramTypes = method.getParameterTypes();
                if (paramTypes.length != oper.getNumParams())
                    continue;

                int j;
                boolean conversionNecessary = false;
                for (j = 0; j < paramTypes.length; j++) {
                    Class type = paramTypes[j];
                    Class actualType = type;
                    if (Holder.class.isAssignableFrom(type)) {
                        actualType = JavaUtils.getHolderValueType(type);
                    }
                    ParameterDesc param = oper.getParameter(j);
                    QName typeQName = param.getTypeQName();
                    if (typeQName == null) {
                        // No typeQName is available.  Set it using
                        // information from the actual type.
                        // (Scenarios B and D)
                        // There is no need to try and match with
                        // the Method parameter javaType because
                        // the ParameterDesc is being constructed
                        // by introspecting the Method.
                        typeQName = getTypeMapping().getTypeQName(actualType);
                        param.setTypeQName(typeQName);
                    } else {
                        // A type qname is available.
                        // Ensure that the ParameterDesc javaType
                        // is convertable to the Method parameter type
                        //
                        // Use the available javaType (Scenarios C and E)
                        // or get one from the TMR (Scenario A).
                        Class paramClass = param.getJavaType();
                        if (paramClass != null &&
                            JavaUtils.getHolderValueType(paramClass) != null) {
                            paramClass = JavaUtils.getHolderValueType(paramClass);
                        }
                        if (paramClass == null) {
                            paramClass = getTypeMapping().getClassForQName(param.getTypeQName(),
                                                                           type);
                        }

                        if (paramClass != null) {
                            // This is a match if the paramClass is somehow
                            // convertable to the "real" parameter type.  If not,
                            // break out of this loop.
                            if (!JavaUtils.isConvertable(paramClass, actualType)) {
                                break;
                            }
                            
                            if (!actualType.isAssignableFrom(paramClass)) {
                                // This doesn't fit without conversion
                                conversionNecessary = true;
                            }
                        }
                    }
                    // In all scenarios the ParameterDesc javaType is set to
                    // match the javaType in the corresponding parameter.
                    // This is essential.
                    param.setJavaType(type);
                }

                if (j != paramTypes.length) {
                    // failed.
                    continue;
                }
                
                // This is our latest possibility
                possibleMatch = method;

                // If this is exactly it, stop now.  Otherwise keep looking
                // just in case we find a better match.
                if (!conversionNecessary) {
                    break;
                }

            }
        }

        // At this point, we may or may not have a possible match.
        // FIXME : Should we prefer an exact match from a base class over
        //         a with-conversion match from the target class?  If so,
        //         we'll need to change the logic below.
        if (possibleMatch != null) {
            Class returnClass = possibleMatch.getReturnType();
            oper.setReturnClass(returnClass);
            
            QName returnType = oper.getReturnType();
            if (returnType == null) {
                oper.setReturnType(getTypeMapping().getTypeQName(returnClass));
            }

            // Do the faults
            createFaultMetadata(possibleMatch, oper);
                
            oper.setMethod(possibleMatch);
            method2OperationMap.put(possibleMatch, oper);
            return;
        }

        // Didn't find a match.  Try the superclass, if appropriate
        Class superClass = implClass.getSuperclass();
        if (superClass != null &&
                !superClass.getName().startsWith("java.") &&
                !superClass.getName().startsWith("javax.") &&
                (stopClasses == null ||
                          !stopClasses.contains(superClass.getName()))) {
            syncOperationToClass(oper, superClass);
        }

        // Exception if sync fails to find method for operation
        if (oper.getMethod() == null) {
            InternalException ie =
                new InternalException(Messages.getMessage("serviceDescOperSync00",
                                                           oper.getName(),
                                                           implClass.getName()));
            throw ie;
        }