001    /**
002     *
003     *  Licensed to the Apache Software Foundation (ASF) under one or more
004     *  contributor license agreements.  See the NOTICE file distributed with
005     *  this work for additional information regarding copyright ownership.
006     *  The ASF licenses this file to You under the Apache License, Version 2.0
007     *  (the "License"); you may not use this file except in compliance with
008     *  the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     *  Unless required by applicable law or agreed to in writing, software
013     *  distributed under the License is distributed on an "AS IS" BASIS,
014     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     *  See the License for the specific language governing permissions and
016     *  limitations under the License.
017     */
018    package org.apache.geronimo.axis.builder;
019    
020    import java.lang.reflect.Method;
021    import java.math.BigDecimal;
022    import java.math.BigInteger;
023    import java.net.URI;
024    import java.util.ArrayList;
025    import java.util.Calendar;
026    import java.util.Collection;
027    import java.util.Collections;
028    import java.util.HashMap;
029    import java.util.HashSet;
030    import java.util.Iterator;
031    import java.util.Map;
032    import java.util.Set;
033    
034    import javax.wsdl.BindingInput;
035    import javax.wsdl.BindingOperation;
036    import javax.wsdl.Fault;
037    import javax.wsdl.Message;
038    import javax.wsdl.Part;
039    import javax.wsdl.OperationType;
040    import javax.wsdl.extensions.soap.SOAPBody;
041    import javax.xml.namespace.QName;
042    
043    import org.apache.axis.constants.Style;
044    import org.apache.axis.constants.Use;
045    import org.apache.axis.description.FaultDesc;
046    import org.apache.axis.description.OperationDesc;
047    import org.apache.axis.description.ParameterDesc;
048    import org.apache.axis.soap.SOAPConstants;
049    import org.apache.axis.encoding.XMLType;
050    import org.apache.geronimo.axis.client.OperationInfo;
051    import org.apache.geronimo.common.DeploymentException;
052    import org.apache.geronimo.kernel.ClassLoading;
053    import org.apache.geronimo.xbeans.j2ee.ConstructorParameterOrderType;
054    import org.apache.geronimo.xbeans.j2ee.ExceptionMappingType;
055    import org.apache.geronimo.xbeans.j2ee.JavaWsdlMappingType;
056    import org.apache.geronimo.xbeans.j2ee.MethodParamPartsMappingType;
057    import org.apache.geronimo.xbeans.j2ee.ServiceEndpointMethodMappingType;
058    import org.apache.geronimo.xbeans.j2ee.WsdlMessageMappingType;
059    import org.apache.geronimo.xbeans.j2ee.WsdlReturnValueMappingType;
060    import org.apache.xmlbeans.SchemaParticle;
061    import org.apache.xmlbeans.SchemaProperty;
062    import org.apache.xmlbeans.SchemaType;
063    import org.objectweb.asm.Type;
064    import org.apache.geronimo.xbeans.j2ee.JavaXmlTypeMappingType;
065    import org.apache.geronimo.webservices.builder.SchemaInfoBuilder;
066    import org.apache.geronimo.webservices.builder.WSDescriptorParser;
067    
068    /**
069     * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $
070     */
071    public class HeavyweightOperationDescBuilder extends OperationDescBuilder {
072    
073        private final JavaWsdlMappingType mapping;
074        private final ServiceEndpointMethodMappingType methodMapping;
075        private final SOAPBody soapBody;
076    
077    
078        private final Map exceptionMap;
079        private final SchemaInfoBuilder schemaInfoBuilder;
080        private final ClassLoader classLoader;
081        private final boolean rpcStyle;
082        private final boolean documentStyle;
083        private final boolean wrappedStyle;
084        private final boolean isEncoded;
085        private final Map publicTypes = new HashMap();
086        private final Map anonymousTypes = new HashMap();
087    
088        /* Keep track of in and out parameter names so we can verify that
089         * everything has been mapped and mapped correctly
090         */
091        private final Set inParamNames = new HashSet();
092        private final Set outParamNames = new HashSet();
093        private final Class serviceEndpointInterface;
094    
095        /**
096         * Track the wrapper elements
097         */
098        private final Set wrapperElementQNames = new HashSet();
099    
100        public HeavyweightOperationDescBuilder(BindingOperation bindingOperation, JavaWsdlMappingType mapping, ServiceEndpointMethodMappingType methodMapping, Style defaultStyle, Map exceptionMap, SchemaInfoBuilder schemaInfoBuilder, JavaXmlTypeMappingType[] javaXmlTypeMappingTypes, ClassLoader classLoader, Class serviceEndpointInterface) throws DeploymentException {
101            super(bindingOperation);
102            this.mapping = mapping;
103            this.methodMapping = methodMapping;
104            this.exceptionMap = exceptionMap;
105            this.schemaInfoBuilder = schemaInfoBuilder;
106            for (int i = 0; i < javaXmlTypeMappingTypes.length; i++) {
107                JavaXmlTypeMappingType javaXmlTypeMappingType = javaXmlTypeMappingTypes[i];
108                String javaClassName = javaXmlTypeMappingType.getJavaType().getStringValue().trim();
109                if (javaXmlTypeMappingType.isSetAnonymousTypeQname()) {
110                    String anonymousTypeQName = javaXmlTypeMappingType.getAnonymousTypeQname().getStringValue().trim();
111                    anonymousTypes.put(anonymousTypeQName, javaClassName);
112                } else if (javaXmlTypeMappingType.isSetRootTypeQname()) {
113                    QName qname = javaXmlTypeMappingType.getRootTypeQname().getQNameValue();
114                    publicTypes.put(qname, javaClassName);
115                }
116            }
117            this.classLoader = classLoader;
118            this.serviceEndpointInterface = serviceEndpointInterface;
119            BindingInput bindingInput = bindingOperation.getBindingInput();
120            this.soapBody = (SOAPBody) SchemaInfoBuilder.getExtensibilityElement(SOAPBody.class, bindingInput.getExtensibilityElements());
121    
122            wrappedStyle = methodMapping.isSetWrappedElement();
123            if (false == wrappedStyle) {
124                Style style = Style.getStyle(soapOperation.getStyle(), defaultStyle);
125                if (style == Style.RPC) {
126                    rpcStyle = true;
127                    documentStyle = false;
128                } else {
129                    rpcStyle = false;
130                    documentStyle = true;
131                }
132            } else {
133                rpcStyle = false;
134                documentStyle = false;
135            }
136            isEncoded = Use.getUse(soapBody.getUse()) == Use.ENCODED;
137        }
138    
139        public Set getWrapperElementQNames() throws DeploymentException {
140            buildOperationDesc();
141    
142            return Collections.unmodifiableSet(wrapperElementQNames);
143        }
144    
145        public boolean isEncoded() {
146            return isEncoded;
147        }
148    
149        public OperationInfo buildOperationInfo(SOAPConstants soapVersion) throws DeploymentException {
150            buildOperationDesc();
151    
152            String soapActionURI = soapOperation.getSoapActionURI();
153            boolean usesSOAPAction = (soapActionURI != null);
154            QName operationQName = getOperationQName();
155    
156            String methodName = methodMapping.getJavaMethodName().getStringValue().trim();
157    
158            ArrayList parameters = operationDesc.getParameters();
159            Type[] parameterASMTypes = new Type[parameters.size()];
160            for (int i = 0; i < parameters.size(); i++) {
161                ParameterDesc parameterDesc = (ParameterDesc) parameters.get(i);
162                parameterASMTypes[i] = Type.getType(parameterDesc.getJavaType());
163            }
164    
165            Type returnASMType = (operationDesc.getReturnClass() != null) ? Type.getType(operationDesc.getReturnClass()) : Type.VOID_TYPE;
166    
167            String methodDesc = Type.getMethodDescriptor(returnASMType, parameterASMTypes);
168            OperationInfo operationInfo = new OperationInfo(operationDesc, usesSOAPAction, soapActionURI, soapVersion, operationQName, methodName, methodDesc);
169            return operationInfo;
170        }
171    
172        private QName getOperationQName() {
173            if (wrappedStyle) {
174                Map parts = operation.getInput().getMessage().getParts();
175                if (parts != null && !parts.isEmpty()) {
176                    for (Iterator iterator = parts.values().iterator(); iterator.hasNext();) {
177                        Part part = (Part) iterator.next();
178                        return part.getElementName();
179                    }
180                }
181            }
182            return getOperationNameFromSOAPBody();
183    
184        }
185    
186        public OperationDesc buildOperationDesc() throws DeploymentException {
187            if (built) {
188                return operationDesc;
189            }
190            built = true;
191    
192            operationDesc.setName(operationName);
193    
194            // Set to 'document', 'rpc' or 'wrapped'
195            if (wrappedStyle) {
196                operationDesc.setStyle(Style.WRAPPED);
197            } else if (rpcStyle) {
198                operationDesc.setStyle(Style.RPC);
199            } else {
200                operationDesc.setStyle(Style.DOCUMENT);
201            }
202    
203            // Set to 'encoded' or 'literal'
204            Use use = Use.getUse(soapBody.getUse());
205            operationDesc.setUse(use);
206    
207    
208            MethodParamPartsMappingType[] paramMappings = methodMapping.getMethodParamPartsMappingArray();
209    
210            /* Put the ParameterDesc instance in an array so they can be ordered properly
211             * before they are added to the the OperationDesc.
212             */
213            ParameterDesc[] parameterDescriptions = new ParameterDesc[paramMappings.length];
214    
215    
216            // MAP PARAMETERS
217            for (int i = 0; i < paramMappings.length; i++) {
218                MethodParamPartsMappingType paramMapping = paramMappings[i];
219                int position = paramMapping.getParamPosition().getBigIntegerValue().intValue();
220    
221                ParameterDesc parameterDesc = mapParameter(paramMapping);
222    
223                parameterDescriptions[position] = parameterDesc;
224            }
225    
226            if (wrappedStyle) {
227                Part inputPart = getWrappedPart(input);
228                QName name = inputPart.getElementName();
229                SchemaType operationType = (SchemaType) schemaInfoBuilder.getComplexTypesInWsdl().get(name);
230    
231                Set expectedInParams = new HashSet();
232    
233                // schemaType should be complex using xsd:sequence compositor
234                SchemaParticle parametersType = operationType.getContentModel();
235                //parametersType can be null if the element has empty content such as
236    //            <element name="getMarketSummary">
237    //             <complexType>
238    //              <sequence/>
239    //             </complexType>
240    //            </element>
241    
242                if (parametersType != null) {
243                    if (SchemaParticle.ELEMENT == parametersType.getParticleType()) {
244                        expectedInParams.add(parametersType.getName().getLocalPart());
245                    } else if (SchemaParticle.SEQUENCE == parametersType.getParticleType()) {
246                        SchemaParticle[] parameters = parametersType.getParticleChildren();
247                        for (int i = 0; i < parameters.length; i++) {
248                            expectedInParams.add(parameters[i].getName().getLocalPart());
249                        }
250                    }
251                }
252                if (!inParamNames.equals(expectedInParams)) {
253                    throw new DeploymentException("Not all wrapper children were mapped for operation name" + operationName);
254                }
255            } else {
256                //check that all input message parts are mapped
257                if (!inParamNames.equals(input.getParts().keySet())) {
258                    throw new DeploymentException("Not all input message parts were mapped for operation name" + operationName);
259                }
260            }
261    
262            Class[] paramTypes = new Class[parameterDescriptions.length];
263            for (int i = 0; i < parameterDescriptions.length; i++) {
264                ParameterDesc parameterDescription = parameterDescriptions[i];
265                if (parameterDescription == null) {
266                    throw new DeploymentException("There is no mapping for parameter number " + i + " for operation " + operationName);
267                }
268                operationDesc.addParameter(parameterDescription);
269                paramTypes[i] = parameterDescription.getJavaType();
270            }
271    
272            String methodName = methodMapping.getJavaMethodName().getStringValue().trim();
273            Method method = null;
274            try {
275                method = serviceEndpointInterface.getMethod(methodName, paramTypes);
276            } catch (NoSuchMethodException e) {
277                String args = "(";
278                for (int i = 0; i < paramTypes.length; i++) {
279                    args += paramTypes[i].getName();
280                    if (i < paramTypes.length - 1) {
281                        args += ",";
282                    }
283                }
284                args += ")";
285    
286                throw new DeploymentException("Mapping references non-existent method in service-endpoint: " + methodName + args);
287            }
288    
289            operationDesc.setMethod(method);
290    
291            // MAP RETURN TYPE
292            operationDesc.setMep(operation.getStyle());
293            if (methodMapping.isSetWsdlReturnValueMapping()) {
294                mapReturnType();
295            } else if (operation.getStyle() == OperationType.REQUEST_RESPONSE) {
296                //TODO WARNING THIS APPEARS TO SUBVERT THE COMMENT IN j2ee_jaxrpc_mapping_1_1.xsd IN service-endpoint-method-mappingType:
297                //The wsdl-return-value-mapping is not specified for one-way operations.
298                operationDesc.setReturnQName(null);             //??
299                operationDesc.setReturnType(XMLType.AXIS_VOID);
300                operationDesc.setReturnClass(void.class);
301            }
302    
303            if (null != output && wrappedStyle) {
304                Part inputPart = getWrappedPart(output);
305                QName name = inputPart.getElementName();
306                SchemaType operationType = (SchemaType) schemaInfoBuilder.getComplexTypesInWsdl().get(name);
307    
308                Set expectedOutParams = new HashSet();
309    
310                // schemaType should be complex using xsd:sequence compositor
311                SchemaParticle parametersType = operationType.getContentModel();
312                //again, no output can give null parametersType
313                if (parametersType != null) {
314                    if (SchemaParticle.ELEMENT == parametersType.getParticleType()) {
315                        expectedOutParams.add(parametersType.getName().getLocalPart());
316                    } else if (SchemaParticle.SEQUENCE == parametersType.getParticleType()) {
317                        SchemaParticle[] parameters = parametersType.getParticleChildren();
318                        for (int i = 0; i < parameters.length; i++) {
319                            expectedOutParams.add(parameters[i].getName().getLocalPart());
320                        }
321                    }
322                }
323                if (!outParamNames.equals(expectedOutParams)) {
324                    throw new DeploymentException("Not all wrapper children were mapped to parameters or a return value for operation " + operationName);
325                }
326            } else if (null != output) {
327                if (!outParamNames.equals(output.getParts().keySet())) {
328                    throw new DeploymentException("Not all output message parts were mapped to parameters or a return value for operation " + operationName);
329                }
330            }
331    
332            Map faultMap = operation.getFaults();
333            for (Iterator iterator = faultMap.entrySet().iterator(); iterator.hasNext();) {
334                Map.Entry entry = (Map.Entry) iterator.next();
335                String faultName = (String) entry.getKey();
336                Fault fault = (Fault) entry.getValue();
337                FaultDesc faultDesc = mapException(faultName, fault);
338    
339                operationDesc.addFault(faultDesc);
340            }
341            return operationDesc;
342        }
343    
344        //see jaxrpc 1.1 4.2.1
345        private static final Map qnameToClassMap = new HashMap();
346    
347        static {
348            qnameToClassMap.put(new QName("http://www.w3.org/2001/XMLSchema", "string"), String.class);
349            qnameToClassMap.put(new QName("http://www.w3.org/2001/XMLSchema", "integer"), BigInteger.class);
350            qnameToClassMap.put(new QName("http://www.w3.org/2001/XMLSchema", "int"), int.class);
351            qnameToClassMap.put(new QName("http://www.w3.org/2001/XMLSchema", "long"), long.class);
352            qnameToClassMap.put(new QName("http://www.w3.org/2001/XMLSchema", "short"), short.class);
353            qnameToClassMap.put(new QName("http://www.w3.org/2001/XMLSchema", "decimal"), BigDecimal.class);
354            qnameToClassMap.put(new QName("http://www.w3.org/2001/XMLSchema", "float"), float.class);
355            qnameToClassMap.put(new QName("http://www.w3.org/2001/XMLSchema", "double"), double.class);
356            qnameToClassMap.put(new QName("http://www.w3.org/2001/XMLSchema", "boolean"), boolean.class);
357            qnameToClassMap.put(new QName("http://www.w3.org/2001/XMLSchema", "byte"), byte.class);
358            qnameToClassMap.put(new QName("http://www.w3.org/2001/XMLSchema", "unsignedInt"), long.class);
359            qnameToClassMap.put(new QName("http://www.w3.org/2001/XMLSchema", "unsignedShort"), int.class);
360            qnameToClassMap.put(new QName("http://www.w3.org/2001/XMLSchema", "unsignedByte"), short.class);
361            qnameToClassMap.put(new QName("http://www.w3.org/2001/XMLSchema", "QName"), QName.class);
362            qnameToClassMap.put(new QName("http://www.w3.org/2001/XMLSchema", "dateTime"), Calendar.class);
363            qnameToClassMap.put(new QName("http://www.w3.org/2001/XMLSchema", "date"), Calendar.class);
364            qnameToClassMap.put(new QName("http://www.w3.org/2001/XMLSchema", "time"), Calendar.class);
365            qnameToClassMap.put(new QName("http://www.w3.org/2001/XMLSchema", "anyURI"), URI.class);
366            qnameToClassMap.put(new QName("http://www.w3.org/2001/XMLSchema", "base64Binary"), byte[].class);
367            qnameToClassMap.put(new QName("http://www.w3.org/2001/XMLSchema", "hexBinary"), byte[].class);
368            qnameToClassMap.put(new QName("http://www.w3.org/2001/XMLSchema", "anySimpleType"), String.class);
369        }
370    
371    
372        private FaultDesc mapException(String faultName, Fault fault) throws DeploymentException {
373            Message message = fault.getMessage();
374            QName messageQName = message.getQName();
375            ExceptionMappingType exceptionMapping = (ExceptionMappingType) exceptionMap.get(messageQName);
376            if (exceptionMapping == null) {
377                throw new DeploymentException("No exception mapping for fault " + faultName + " and fault message " + messageQName + " for operation " + operationName);
378            }
379            String className = exceptionMapping.getExceptionType().getStringValue().trim();
380            //TODO investigate whether there are other cases in which the namespace of faultQName can be determined.
381            //this is weird, but I can't figure out what it should be.
382            //if part has an element rather than a type, it should be part.getElementName() (see below)
383            QName faultQName = new QName("", faultName);
384            Part part;
385            if (exceptionMapping.isSetWsdlMessagePartName()) {
386                //According to schema documentation, this will only be set when several headerfaults use the same message.
387                String headerFaultMessagePartName = exceptionMapping.getWsdlMessagePartName().getStringValue();
388                part = message.getPart(headerFaultMessagePartName);
389            } else {
390                part = (Part) message.getOrderedParts(null).iterator().next();
391            }
392            QName faultTypeQName;// = part.getElementName() == null ? part.getTypeName() : part.getElementName();
393            if (part.getElementName() == null) {
394                faultTypeQName = part.getTypeName();
395                if (faultTypeQName == null) {
396                    throw new DeploymentException("Neither type nor element name supplied for part: " + part);
397                }
398            } else {
399                faultQName = part.getElementName();
400                faultTypeQName = (QName) schemaInfoBuilder.getElementToTypeMap().get(part.getElementName());
401                if (faultTypeQName == null) {
402                    throw new DeploymentException("Can not find type for: element: " + part.getElementName() + ", known elements: " + schemaInfoBuilder.getElementToTypeMap());
403                }
404            }
405            SchemaType complexType = (SchemaType) schemaInfoBuilder.getComplexTypesInWsdl().get(faultTypeQName);
406            boolean isComplex = complexType != null;
407            FaultDesc faultDesc = new FaultDesc(faultQName, className, faultTypeQName, isComplex);
408    
409            //constructor parameters
410            if (exceptionMapping.isSetConstructorParameterOrder()) {
411                if (!isComplex) {
412                    throw new DeploymentException("ConstructorParameterOrder can only be set for complex types, not " + faultTypeQName);
413                }
414                Map elementMap = new HashMap();
415                SchemaProperty[] properties = complexType.getProperties();
416                for (int i = 0; i < properties.length; i++) {
417                    SchemaProperty property = properties[i];
418                    QName elementName = property.getName();
419                    SchemaType elementType = property.getType();
420                    elementMap.put(elementName.getLocalPart(), elementType);
421                }
422                ArrayList parameterTypes = new ArrayList();
423                ConstructorParameterOrderType constructorParameterOrder = exceptionMapping.getConstructorParameterOrder();
424                for (int i = 0; i < constructorParameterOrder.getElementNameArray().length; i++) {
425                    String elementName = constructorParameterOrder.getElementNameArray(i).getStringValue().trim();
426                    SchemaType elementType = (SchemaType) elementMap.get(elementName);
427                    Class javaElementType;
428    
429                    QName elementTypeQName = elementType.getName();
430                    if (elementTypeQName != null) {
431                        if (schemaInfoBuilder.getComplexTypesInWsdl().containsKey(elementType)) {
432                            String javaClassName = (String) publicTypes.get(elementTypeQName);
433                            if (javaClassName == null) {
434                                throw new DeploymentException("No class mapped for element type: " + elementType);
435                            }
436                            javaElementType = getJavaClass(javaClassName);
437                        } else {
438                            javaElementType = (Class) qnameToClassMap.get(elementTypeQName);
439                            if (javaElementType == null) {
440                                throw new DeploymentException("Unknown type: " + elementType + " of name: " + elementName + " and QName: " + elementTypeQName);
441                            }
442                        }
443                    } else {
444                        //anonymous type
445                        //anonymous type qname is constructed using rules 1.b and 2.b
446                        String anonymousQName = complexType.getName().getNamespaceURI() + ":>" + complexType.getName().getLocalPart() + ">" + elementName;
447                        String javaClassName = (String) anonymousTypes.get(anonymousQName);
448                        if (javaClassName == null) {
449                            if (elementType.isSimpleType()) {
450                                //maybe it's a restriction of a built in simple type
451                                SchemaType baseType = elementType.getBaseType();
452                                QName simpleTypeQName = baseType.getName();
453                                javaElementType = (Class) qnameToClassMap.get(simpleTypeQName);
454                                if (javaElementType == null) {
455                                    throw new DeploymentException("Unknown simple type: " + elementType + " of name: " + elementName + " and QName: " + simpleTypeQName);
456                                }
457                            } else {
458                                throw new DeploymentException("No class mapped for anonymous type: " + anonymousQName);
459                            }
460                        } else {
461                            javaElementType = getJavaClass(javaClassName);
462                        }
463                    }
464                    //todo faultTypeQName is speculative
465                    //todo outheader might be true!
466                    ParameterDesc parameterDesc = new ParameterDesc(faultTypeQName, ParameterDesc.OUT, elementTypeQName, javaElementType, false, false);
467                    parameterTypes.add(parameterDesc);
468                }
469                faultDesc.setParameters(parameterTypes);
470            }
471            return faultDesc;
472        }
473    
474        private Class getJavaClass(String javaClassName) throws DeploymentException {
475            try {
476                Class javaClass = ClassLoading.loadClass(javaClassName, classLoader);
477                return javaClass;
478            } catch (ClassNotFoundException e) {
479                throw new DeploymentException("Could not load class", e);
480            }
481        }
482    
483        private void mapReturnType() throws DeploymentException {
484            QName returnType = null;
485            QName returnQName = null;
486            Class returnClass = null;
487    
488            if (output == null) {
489                throw new DeploymentException("No output message, but a mapping for it for operation " + operationName);
490            }
491            WsdlReturnValueMappingType wsdlReturnValueMapping = methodMapping.getWsdlReturnValueMapping();
492            String returnClassName = wsdlReturnValueMapping.getMethodReturnValue().getStringValue().trim();
493            try {
494                returnClass = ClassLoading.loadClass(returnClassName, classLoader);
495            } catch (ClassNotFoundException e) {
496                throw new DeploymentException("Could not load return type for operation " + operationName, e);
497            }
498    
499            QName wsdlMessageQName = wsdlReturnValueMapping.getWsdlMessage().getQNameValue();
500    
501            if (!wsdlMessageQName.equals(output.getQName())) {
502                throw new DeploymentException("OutputMessage has QName: " + output.getQName() + " but mapping specifies: " + wsdlMessageQName + " for operation " + operationName);
503            }
504    
505            if (wsdlReturnValueMapping.isSetWsdlMessagePartName()) {
506                String wsdlMessagePartName = wsdlReturnValueMapping.getWsdlMessagePartName().getStringValue().trim();
507                if (outParamNames.contains(wsdlMessagePartName)) {
508                    throw new DeploymentException("output message part " + wsdlMessagePartName + " has both an INOUT or OUT mapping and a return value mapping for operation " + operationName);
509                }
510    
511                if (wrappedStyle) {
512                    Part outPart = getWrappedPart(output);
513                    SchemaParticle returnParticle = getWrapperChild(outPart, wsdlMessagePartName);
514                    //TODO this makes little sense but may be correct, see comments in axis Parameter class
515                    //the part name qname is really odd.
516                    returnQName = new QName("", returnParticle.getName().getLocalPart());
517                    returnType = returnParticle.getType().getName();
518                } else if (rpcStyle) {
519                    Part part = output.getPart(wsdlMessagePartName);
520                    if (part == null) {
521                        throw new DeploymentException("No part for wsdlMessagePartName " + wsdlMessagePartName + " in output message for operation " + operationName);
522                    }
523                    returnQName = new QName("", part.getName());
524                    returnType = part.getTypeName();
525                } else {
526                    Part part = output.getPart(wsdlMessagePartName);
527                    if (part == null) {
528                        throw new DeploymentException("No part for wsdlMessagePartName " + wsdlMessagePartName + " in output message for operation " + operationName);
529                    }
530                    returnQName = getPartName(part);
531                    returnType = returnQName;
532                }
533    
534                outParamNames.add(wsdlMessagePartName);
535            } else {
536                //what does this mean????
537            }
538    
539            operationDesc.setReturnQName(returnQName);
540            operationDesc.setReturnType(returnType);
541            operationDesc.setReturnClass(returnClass);
542        }
543    
544        private ParameterDesc mapParameter(MethodParamPartsMappingType paramMapping) throws DeploymentException {
545            WsdlMessageMappingType wsdlMessageMappingType = paramMapping.getWsdlMessageMapping();
546            QName wsdlMessageQName = wsdlMessageMappingType.getWsdlMessage().getQNameValue();
547            String wsdlMessagePartName = wsdlMessageMappingType.getWsdlMessagePartName().getStringValue().trim();
548    
549            String parameterMode = wsdlMessageMappingType.getParameterMode().getStringValue().trim();
550            byte mode = ParameterDesc.modeFromString(parameterMode);
551            boolean isInParam = mode == ParameterDesc.IN || mode == ParameterDesc.INOUT;
552            boolean isOutParam = mode == ParameterDesc.OUT || mode == ParameterDesc.INOUT;
553    
554            if (isOutParam && output == null) {
555                throw new DeploymentException("Mapping for output parameter " + wsdlMessagePartName + " found, but no output message for operation " + operationName);
556            }
557            boolean isSoapHeader = wsdlMessageMappingType.isSetSoapHeader();
558            boolean inHeader = isSoapHeader && isInParam;
559            boolean outHeader = isSoapHeader && isOutParam;
560    
561            QName paramQName;
562            QName paramTypeQName;
563    
564            Part part = null;
565            SchemaParticle inParameter = null;
566            if (isInParam) {
567                if (!wsdlMessageQName.equals(input.getQName())) {
568                    throw new DeploymentException("QName of input message: " + input.getQName() +
569                            " does not match mapping message QName: " + wsdlMessageQName + " for operation " + operationName);
570                }
571                if (wrappedStyle) {
572                    Part inPart = getWrappedPart(input);
573                    // the local name of the global element refered by the part is equal to the operation name
574                    QName name = inPart.getElementName();
575                    if (false == name.getLocalPart().equals(operationName)) {
576                        throw new DeploymentException("message " + input.getQName() + " refers to a global element named " +
577                                name.getLocalPart() + ", which is not equal to the operation name " + operationName);
578                    }
579                    inParameter = getWrapperChild(inPart, wsdlMessagePartName);
580                    //TODO this makes little sense but may be correct, see comments in axis Parameter class
581                    //the part name qname is really odd.
582                    paramQName = new QName("", inParameter.getName().getLocalPart());
583                    paramTypeQName = inParameter.getType().getName();
584                } else if (rpcStyle) {
585                    part = input.getPart(wsdlMessagePartName);
586                    if (part == null) {
587                        throw new DeploymentException("No part for wsdlMessagePartName " + wsdlMessagePartName + " in input message for operation " + operationName);
588                    }
589                    //TODO this makes little sense but may be correct, see comments in axis Parameter class
590                    //the part name qname is really odd.
591                    paramQName = new QName("", part.getName());
592                    paramTypeQName = part.getTypeName();
593                } else {
594                    part = input.getPart(wsdlMessagePartName);
595                    if (part == null) {
596                        throw new DeploymentException("No part for wsdlMessagePartName " + wsdlMessagePartName + " in input message for operation " + operationName);
597                    }
598                    paramQName = getPartName(part);
599                    paramTypeQName = paramQName;
600                }
601                inParamNames.add(wsdlMessagePartName);
602                if (isOutParam) {
603                    if (wrappedStyle) {
604                        Part outPart = getWrappedPart(output);
605                        SchemaParticle outParameter = getWrapperChild(outPart, wsdlMessagePartName);
606                        if (inParameter.getType() != outParameter.getType()) {
607                            throw new DeploymentException("The wrapper children " + wsdlMessagePartName +
608                                    " do not have the same type for operation " + operationName);
609                        }
610                    } else if (rpcStyle) {
611                        //inout, check that part of same name and type is in output message
612                        Part outPart = output.getPart(wsdlMessagePartName);
613                        if (outPart == null) {
614                            throw new DeploymentException("No part for wsdlMessagePartName " + wsdlMessagePartName + " in output message for INOUT parameter of operation " + operationName);
615                        }
616                        // TODO this cannot happen.
617                        if (!part.getName().equals(outPart.getName())) {
618                            throw new DeploymentException("Mismatched input part name: " + part.getName() + " and output part name: " + outPart.getName() + " for INOUT parameter for wsdlMessagePartName " + wsdlMessagePartName + " for operation " + operationName);
619                        }
620                        if (!(part.getElementName() == null ? outPart.getElementName() == null : part.getElementName().equals(outPart.getElementName()))) {
621                            throw new DeploymentException("Mismatched input part element name: " + part.getElementName() + " and output part element name: " + outPart.getElementName() + " for INOUT parameter for wsdlMessagePartName " + wsdlMessagePartName + " for operation " + operationName);
622                        }
623                        if (!(part.getTypeName() == null ? outPart.getTypeName() == null : part.getTypeName().equals(outPart.getTypeName()))) {
624                            throw new DeploymentException("Mismatched input part type name: " + part.getTypeName() + " and output part type name: " + outPart.getTypeName() + " for INOUT parameter for wsdlMessagePartName " + wsdlMessagePartName + " for operation " + operationName);
625                        }
626                    } else {
627                        part = output.getPart(wsdlMessagePartName);
628                        if (part == null) {
629                            throw new DeploymentException("No part for wsdlMessagePartName " + wsdlMessagePartName + " in output message for operation " + operationName);
630                        }
631                        paramQName = getPartName(part);
632                        paramTypeQName = paramQName;
633                    }
634                    outParamNames.add(wsdlMessagePartName);
635                }
636            } else if (isOutParam) {
637                if (!wsdlMessageQName.equals(output.getQName())) {
638                    throw new DeploymentException("QName of output message: " + output.getQName() +
639                            " does not match mapping message QName: " + wsdlMessageQName + " for operation " + operationName);
640                }
641                if (wrappedStyle) {
642                    Part outPart = getWrappedPart(output);
643                    SchemaParticle outParameter = getWrapperChild(outPart, wsdlMessagePartName);
644                    //TODO this makes little sense but may be correct, see comments in axis Parameter class
645                    //the part name qname is really odd.
646                    paramQName = new QName("", outParameter.getName().getLocalPart());
647                    paramTypeQName = outParameter.getType().getName();
648                } else if (rpcStyle) {
649                    part = output.getPart(wsdlMessagePartName);
650                    if (part == null) {
651                        throw new DeploymentException("No part for wsdlMessagePartName " + wsdlMessagePartName + " in output message for operation " + operationName);
652                    }
653                    //TODO this makes little sense but may be correct, see comments in axis Parameter class
654                    //the part name qname is really odd.
655                    paramQName = new QName("", part.getName());
656                    paramTypeQName = part.getTypeName();
657                } else {
658                    part = output.getPart(wsdlMessagePartName);
659                    if (part == null) {
660                        throw new DeploymentException("No part for wsdlMessagePartName " + wsdlMessagePartName + " in output message for operation " + operationName);
661                    }
662                    paramQName = getPartName(part);
663                    paramTypeQName = paramQName;
664                }
665                outParamNames.add(wsdlMessagePartName);
666            } else {
667                throw new AssertionError("a param mapping has to be IN or OUT or INOUT");
668            }
669    
670            //use complexTypeMap
671            boolean isComplexType = schemaInfoBuilder.getComplexTypesInWsdl().containsKey(paramTypeQName);
672            String paramJavaTypeName = paramMapping.getParamType().getStringValue().trim();
673            boolean isInOnly = mode == ParameterDesc.IN;
674            Class actualParamJavaType = WSDescriptorParser.getHolderType(paramJavaTypeName, isInOnly, paramTypeQName, isComplexType, mapping, classLoader);
675    
676            ParameterDesc parameterDesc = new ParameterDesc(paramQName, mode, paramTypeQName, actualParamJavaType, inHeader, outHeader);
677            return parameterDesc;
678        }
679    
680        private QName getPartName(Part part) {
681            return null == part.getElementName() ? part.getTypeName() : part.getElementName();
682        }
683    
684        private Part getWrappedPart(Message message) throws DeploymentException {
685            // in case of wrapped element, the message has only one part.
686            Collection parts = message.getParts().values();
687            if (1 != parts.size()) {
688                throw new DeploymentException("message " + message.getQName() + " has " + parts.size() +
689                        " parts and should only have one as wrapper style mapping is specified for operation " +
690                        operationName);
691            }
692            return (Part) parts.iterator().next();
693        }
694    
695        private SchemaParticle getWrapperChild(Part part, String wsdlMessagePartName) throws DeploymentException {
696            QName name = part.getElementName();
697    
698            wrapperElementQNames.add(name);
699    
700            SchemaType operationType = (SchemaType) schemaInfoBuilder.getComplexTypesInWsdl().get(name);
701            if (null == operationType) {
702                throw new DeploymentException("No global element named " + name + " for operation " + operationName);
703            }
704    
705            // schemaType should be complex using xsd:sequence compositor
706            SchemaParticle parametersType = operationType.getContentModel();
707            if (SchemaParticle.ELEMENT == parametersType.getParticleType()) {
708                if (parametersType.getName().getLocalPart().equals(wsdlMessagePartName)) {
709                    return parametersType;
710                }
711                throw new DeploymentException("Global element named " + name +
712                        " does not define a child element named " + wsdlMessagePartName +
713                        " required by the operation " + operationName);
714            } else if (SchemaParticle.SEQUENCE == parametersType.getParticleType()) {
715                SchemaParticle[] parameters = parametersType.getParticleChildren();
716                for (int i = 0; i < parameters.length; i++) {
717                    SchemaParticle parameter = parameters[i];
718                    QName element = parameter.getName();
719                    if (element.getLocalPart().equals(wsdlMessagePartName)) {
720                        return parameter;
721                    }
722                }
723                throw new DeploymentException("Global element named " + name +
724                        " does not define a child element named " + wsdlMessagePartName +
725                        " required by the operation " + operationName);
726            } else {
727                throw new DeploymentException("Global element named " + name +
728                        " is not a sequence for operation " + operationName);
729            }
730        }
731    
732        /**
733         * Supporting the Document/Literal Wrapped pattern
734         *
735         * See http://www-106.ibm.com/developerworks/webservices/library/ws-whichwsdl/ for a nice explanation and example
736         *
737         * wrapped-element tag is used
738         * WSDL message with a single part
739         * part uses the 'element' attribute to point to an elemement in the types section
740         * the element type and the element's name match the operation name
741         */
742    }