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