private void syncOperationToClass()

in axis-rt-core/src/main/java/org/apache/axis/description/JavaServiceDesc.java [573:787]


    private void syncOperationToClass(OperationDesc oper, Class implClass)
    {
        // ------------------------------------------------
        // 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;
        }
    }