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.beans.IntrospectionException;
020    import java.beans.Introspector;
021    import java.beans.PropertyDescriptor;
022    import java.lang.reflect.Field;
023    import java.util.ArrayList;
024    import java.util.Collection;
025    import java.util.HashMap;
026    import java.util.HashSet;
027    import java.util.Iterator;
028    import java.util.List;
029    import java.util.Map;
030    import java.util.Set;
031    import javax.xml.namespace.QName;
032    import javax.xml.rpc.encoding.DeserializerFactory;
033    import javax.xml.rpc.encoding.SerializerFactory;
034    
035    import org.apache.axis.description.AttributeDesc;
036    import org.apache.axis.description.ElementDesc;
037    import org.apache.axis.description.FieldDesc;
038    import org.apache.axis.description.OperationDesc;
039    import org.apache.axis.description.ParameterDesc;
040    import org.apache.axis.encoding.DefaultJAXRPC11TypeMappingImpl;
041    import org.apache.axis.encoding.DefaultSOAPEncodingTypeMappingImpl;
042    import org.apache.axis.encoding.TypeMappingImpl;
043    import org.apache.axis.encoding.XMLType;
044    import org.apache.axis.encoding.ser.ArrayDeserializerFactory;
045    import org.apache.axis.encoding.ser.ArraySerializerFactory;
046    import org.apache.axis.encoding.ser.BeanDeserializerFactory;
047    import org.apache.axis.encoding.ser.BeanSerializerFactory;
048    import org.apache.axis.encoding.ser.EnumDeserializerFactory;
049    import org.apache.axis.encoding.ser.EnumSerializerFactory;
050    import org.apache.axis.encoding.ser.SimpleListDeserializerFactory;
051    import org.apache.axis.encoding.ser.SimpleListSerializerFactory;
052    import org.apache.commons.logging.Log;
053    import org.apache.commons.logging.LogFactory;
054    import org.apache.geronimo.axis.client.ArrayTypeInfo;
055    import org.apache.geronimo.axis.client.TypeInfo;
056    import org.apache.geronimo.common.DeploymentException;
057    import org.apache.geronimo.kernel.ClassLoading;
058    import org.apache.geronimo.xbeans.j2ee.JavaWsdlMappingType;
059    import org.apache.geronimo.xbeans.j2ee.JavaXmlTypeMappingType;
060    import org.apache.geronimo.xbeans.j2ee.VariableMappingType;
061    import org.apache.geronimo.webservices.builder.SchemaTypeKey;
062    import org.apache.xmlbeans.SchemaLocalAttribute;
063    import org.apache.xmlbeans.SchemaParticle;
064    import org.apache.xmlbeans.SchemaProperty;
065    import org.apache.xmlbeans.SchemaType;
066    import org.apache.xmlbeans.soap.SOAPArrayType;
067    import org.apache.xmlbeans.soap.SchemaWSDLArrayType;
068    
069    /**
070     * @version $Rev: 547679 $ $Date: 2007-06-15 09:34:32 -0400 (Fri, 15 Jun 2007) $
071     */
072    public class HeavyweightTypeInfoBuilder implements TypeInfoBuilder {
073        private static final String SOAP_ENCODING_NS = "http://schemas.xmlsoap.org/soap/encoding/";
074        private static final String XML_SCHEMA_NS = "http://www.w3.org/2001/XMLSchema";
075        
076        private static final Log log = LogFactory.getLog(HeavyweightTypeInfoBuilder.class);
077    
078        private final ClassLoader cl;
079        private final Map schemaTypeKeyToSchemaTypeMap;
080        private final Set wrapperElementQNames;
081        private final Collection operations;
082        private final boolean hasEncoded;
083    
084        public HeavyweightTypeInfoBuilder(ClassLoader cl, Map schemaTypeKeyToSchemaTypeMap, Set wrapperElementQNames, Collection operations, boolean hasEncoded) {
085            this.cl = cl;
086            this.schemaTypeKeyToSchemaTypeMap = schemaTypeKeyToSchemaTypeMap;
087            this.wrapperElementQNames = wrapperElementQNames;
088            this.operations = operations;
089            this.hasEncoded = hasEncoded;
090        }
091    
092        public List buildTypeInfo(JavaWsdlMappingType mapping) throws DeploymentException {
093            List typeInfoList = new ArrayList();
094    
095            Set mappedTypeQNames = new HashSet();
096    
097            JavaXmlTypeMappingType[] javaXmlTypeMappings = mapping.getJavaXmlTypeMappingArray();
098            for (int j = 0; j < javaXmlTypeMappings.length; j++) {
099                JavaXmlTypeMappingType javaXmlTypeMapping = javaXmlTypeMappings[j];
100    
101                SchemaTypeKey key;
102                boolean isElement = javaXmlTypeMapping.getQnameScope().getStringValue().equals("element");
103                boolean isSimpleType = javaXmlTypeMapping.getQnameScope().getStringValue().equals("simpleType");
104                if (javaXmlTypeMapping.isSetRootTypeQname()) {
105                    QName typeQName = javaXmlTypeMapping.getRootTypeQname().getQNameValue();
106                    key = new SchemaTypeKey(typeQName, isElement, isSimpleType, false, null);
107    
108                    // Skip the wrapper elements.
109                    if (wrapperElementQNames.contains(typeQName)) {
110                        continue;
111                    }
112                } else if (javaXmlTypeMapping.isSetAnonymousTypeQname()) {
113                    String anonTypeQNameString = javaXmlTypeMapping.getAnonymousTypeQname().getStringValue();
114                    int pos = anonTypeQNameString.lastIndexOf(":");
115                    if (pos == -1) {
116                        throw new DeploymentException("anon QName is invalid, no final ':' " + anonTypeQNameString);
117                    }
118    
119                    //this appears to be ignored...
120                    QName typeQName = new QName(anonTypeQNameString.substring(0, pos), anonTypeQNameString.substring(pos + 1));
121                    key = new SchemaTypeKey(typeQName, isElement, isSimpleType, true, null);
122    
123                    // Skip the wrapper elements.
124                    if (wrapperElementQNames.contains(new QName(anonTypeQNameString.substring(0, pos), anonTypeQNameString.substring(pos + 2)))) {
125                        continue;
126                    }
127                } else {
128                    throw new DeploymentException("either root type qname or anonymous type qname must be set");
129                }
130    
131                SchemaType schemaType = (SchemaType) schemaTypeKeyToSchemaTypeMap.get(key);
132                if (schemaType == null) {
133                    // if it is a built-in type, then one assumes a redundant mapping. 
134                    if (null != TypeMappingLookup.getFactoryPair(key.getqName())) {
135                        continue;
136                    }
137    //              throw new DeploymentException("Schema type key " + key + " not found in analyzed schema: " + schemaTypeKeyToSchemaTypeMap);
138                    log.warn("Schema type key " + key + " not found in analyzed schema: " + schemaTypeKeyToSchemaTypeMap);
139                    continue;
140                }
141                mappedTypeQNames.add(key.getqName());
142    
143                String className = javaXmlTypeMapping.getJavaType().getStringValue().trim();
144                Class clazz = null;
145                try {
146                    clazz = ClassLoading.loadClass(className, cl);
147                } catch (ClassNotFoundException e2) {
148                    throw new DeploymentException("Could not load java type", e2);
149                }
150    
151                TypeInfo.UpdatableTypeInfo internalTypeInfo = defineSerializerPair(schemaType, clazz);
152    
153                populateInternalTypeInfo(clazz, key, schemaType, javaXmlTypeMapping, internalTypeInfo);
154    
155                typeInfoList.add(internalTypeInfo.buildTypeInfo());
156            }
157    
158            Map qNameToKey = new HashMap();
159            for (Iterator iter = schemaTypeKeyToSchemaTypeMap.keySet().iterator(); iter.hasNext();) {
160                SchemaTypeKey key = (SchemaTypeKey) iter.next();
161                qNameToKey.put(key.getqName(), key);
162            }
163    
164            for (Iterator iter = operations.iterator(); iter.hasNext();) {
165                OperationDesc operationDesc = (OperationDesc) iter.next();
166                ArrayList parameters = new ArrayList(operationDesc.getParameters());
167                ParameterDesc returnParameterDesc = operationDesc.getReturnParamDesc();
168                if (null != returnParameterDesc.getTypeQName() &&
169                        false == returnParameterDesc.getTypeQName().equals(XMLType.AXIS_VOID)) {
170                    parameters.add(returnParameterDesc);
171                }
172                for (Iterator iterator = parameters.iterator(); iterator.hasNext();) {
173                    ParameterDesc parameterDesc = (ParameterDesc) iterator.next();
174                    QName typeQName = parameterDesc.getTypeQName();
175                    if (null == typeQName) {
176                        continue;
177                    } else if (mappedTypeQNames.contains(typeQName)) {
178                        continue;
179                    } else if (typeQName.getNamespaceURI().equals(XML_SCHEMA_NS) ||
180                            typeQName.getNamespaceURI().equals(SOAP_ENCODING_NS)) {
181                        continue;
182                    }
183    
184                    SchemaTypeKey key = (SchemaTypeKey) qNameToKey.get(typeQName);
185                    if (null == key) {
186                        log.warn("Type QName [" + typeQName + "] defined by operation [" +
187                                operationDesc + "] has not been found in schema: " + schemaTypeKeyToSchemaTypeMap);
188                        continue;
189                    }
190                    SchemaType schemaType = (SchemaType) schemaTypeKeyToSchemaTypeMap.get(key);
191                    mappedTypeQNames.add(key.getqName());
192    
193                    if (false == schemaType.isSimpleType()) {
194                        if (false == parameterDesc.getJavaType().isArray()) {
195                            if (false == mappedTypeQNames.contains(schemaType.getName())) {
196                                // TODO: this lookup is not enough: the jaxrpc mapping file may define an anonymous
197                                // mapping.
198                                log.warn("Operation [" + operationDesc + "] uses XML type [" + schemaType +
199                                        "], whose mapping is not declared by the jaxrpc mapping file.\n Continuing deployment; " +
200                                        "yet, the deployment is not-portable.");
201                            }
202                            continue;
203                        }
204                    }
205    
206                    Class clazz = parameterDesc.getJavaType();
207                    TypeInfo.UpdatableTypeInfo internalTypeInfo =  defineSerializerPair(schemaType, clazz);
208                    setTypeQName(internalTypeInfo, key);
209                    internalTypeInfo.setFields(new FieldDesc[0]);
210    
211                    typeInfoList.add(internalTypeInfo.buildTypeInfo());
212                }
213            }
214    
215            return typeInfoList;
216        }
217    
218        private TypeInfo.UpdatableTypeInfo defineSerializerPair(SchemaType schemaType, Class clazz)
219                throws DeploymentException {
220            TypeInfo.UpdatableTypeInfo internalTypeInfo = new TypeInfo.UpdatableTypeInfo();
221            Class serializerFactoryClass = null;
222            Class deserializerFactoryClass = null;
223            if (schemaType.isSimpleType()) {
224                if (SchemaType.ATOMIC == schemaType.getSimpleVariety()) {
225                    if (clazz.isArray()) {
226                        internalTypeInfo = new ArrayTypeInfo.UpdatableArrayTypeInfo();
227                        serializerFactoryClass = ArraySerializerFactory.class;
228                        deserializerFactoryClass = ArrayDeserializerFactory.class;
229                        //TODO set componentType, componentQName
230                    } else if (null != schemaType.getEnumerationValues()) {
231                        serializerFactoryClass = EnumSerializerFactory.class;
232                        deserializerFactoryClass = EnumDeserializerFactory.class;
233                    } else {
234                        QName typeQName = schemaType.getPrimitiveType().getName();
235                        FactoryPair pair = (FactoryPair) TypeMappingLookup.getFactoryPair(typeQName);
236                        if (null == pair) {
237                            throw new DeploymentException("Primitive type [" + typeQName + "] is not registered.");
238                        }
239                        serializerFactoryClass = pair.serializerFactoryClass;
240                        deserializerFactoryClass = pair.deserializerFactoryClass;
241                    }
242                } else if (SchemaType.LIST == schemaType.getSimpleVariety()) {
243                    serializerFactoryClass = SimpleListSerializerFactory.class;
244                    deserializerFactoryClass = SimpleListDeserializerFactory.class;
245                } else {
246                    throw new DeploymentException("Schema type [" + schemaType + "] is invalid.");
247                }
248            } else {
249                if (clazz.isArray()) {
250                    internalTypeInfo = new ArrayTypeInfo.UpdatableArrayTypeInfo();
251                    serializerFactoryClass = ArraySerializerFactory.class;
252                    deserializerFactoryClass = ArrayDeserializerFactory.class;
253                    QName componentType = null;
254                    //First, handle case that looks like this:
255    //                <complexType name="ArrayOfstring">
256    //                    <complexContent>
257    //                        <restriction base="soapenc:Array">
258    //                            <attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:string[]"/>
259    //                        </restriction>
260    //                    </complexContent>
261    //                </complexType>
262                    SchemaLocalAttribute arrayTypeAttribute =  schemaType.getAttributeModel().getAttribute(new QName(SOAP_ENCODING_NS, "arrayType"));
263                    if (arrayTypeAttribute != null) {
264                        SchemaWSDLArrayType wsdlArrayType = (SchemaWSDLArrayType) arrayTypeAttribute;
265                        SOAPArrayType soapArrayType = wsdlArrayType.getWSDLArrayType();
266                        if (soapArrayType != null) {
267                            componentType = soapArrayType.getQName();
268                            log.debug("extracted componentType " + componentType + " from schemaType " + schemaType);
269                        } else {
270                            log.info("no SOAPArrayType for component from schemaType " + schemaType);
271                        }
272                    } else {
273                        log.warn("No soap array info for schematype: " + schemaType);
274                    }
275                    if (componentType == null) {
276                        //If that didn't work, try to handle case like this:
277    //                    <complexType name="ArrayOfstring1">
278    //                        <complexContent>
279    //                            <restriction base="soapenc:Array">
280    //                                <sequence>
281    //                                    <element name="string1" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
282    //                                </sequence>
283    //                            </restriction>
284    //                        </complexContent>
285    //                    </complexType>
286                        //todo consider if we should check for maxOccurs > 1
287                        if (schemaType.getBaseType().getName().equals(new QName(SOAP_ENCODING_NS, "Array"))) {
288                            SchemaProperty[] properties = schemaType.getDerivedProperties();
289                            if (properties.length != 1) {
290                                throw new DeploymentException("more than one element inside array definition: " + schemaType);
291                            }
292                            componentType = properties[0].getType().getName();
293                            log.debug("determined component type from element type");
294                        }
295    
296                    }
297    
298                    ((ArrayTypeInfo.UpdatableArrayTypeInfo)internalTypeInfo).setComponentType(componentType);
299                    //If we understand the axis comments correctly, componentQName is never set for j2ee ws.
300                } else {
301                    QName typeQName;
302                    if (SchemaType.SIMPLE_CONTENT == schemaType.getContentType()) {
303                        typeQName = schemaType.getBaseType().getName();
304                    } else if (SchemaType.EMPTY_CONTENT == schemaType.getContentType() ||
305                            SchemaType.ELEMENT_CONTENT == schemaType.getContentType() ||
306                            SchemaType.MIXED_CONTENT == schemaType.getContentType()) {
307                        typeQName = schemaType.getName();
308                    } else {
309                        throw new DeploymentException("Schema type [" + schemaType + "] is invalid.");
310                    }
311                    FactoryPair pair = (FactoryPair) TypeMappingLookup.getFactoryPair(typeQName);
312                    if (null != pair) {
313                        serializerFactoryClass = pair.serializerFactoryClass;
314                        deserializerFactoryClass = pair.deserializerFactoryClass;
315                    } else {
316                        serializerFactoryClass = BeanSerializerFactory.class;
317                        deserializerFactoryClass = BeanDeserializerFactory.class;
318                    }
319                }
320            }
321    
322            internalTypeInfo.setClazz(clazz);
323            internalTypeInfo.setSerializerClass(serializerFactoryClass);
324            internalTypeInfo.setDeserializerClass(deserializerFactoryClass);
325            return internalTypeInfo;
326        }
327    
328        private void setTypeQName(TypeInfo.UpdatableTypeInfo typeInfo, SchemaTypeKey key) {
329            //figure out the name axis expects to look up under.
330            QName axisKey = key.getElementQName();
331            if (axisKey == null) {
332                axisKey = key.getqName();
333            }
334            typeInfo.setQName(axisKey);
335        }
336    
337        private void populateInternalTypeInfo(Class javaClass, SchemaTypeKey key, SchemaType schemaType, JavaXmlTypeMappingType javaXmlTypeMapping, TypeInfo.UpdatableTypeInfo typeInfo) throws DeploymentException {
338            String ns = key.getqName().getNamespaceURI();
339            typeInfo.setCanSearchParents(schemaType.getDerivationType() == SchemaType.DT_RESTRICTION);
340    
341            setTypeQName(typeInfo, key);
342    
343            Map paramNameToType = new HashMap();
344            if (null == schemaType.getContentModel()) {
345                ;
346            } else if (SchemaParticle.SEQUENCE == schemaType.getContentModel().getParticleType()
347                    || SchemaParticle.ALL == schemaType.getContentModel().getParticleType()) {
348                SchemaParticle[] properties = schemaType.getContentModel().getParticleChildren();
349                for (int i = 0; i < properties.length; i++) {
350                    SchemaParticle parameter = properties[i];
351                    paramNameToType.put(parameter.getName(), parameter);
352                }
353            } else if (SchemaParticle.ELEMENT == schemaType.getContentModel().getParticleType()) {
354                SchemaParticle parameter = schemaType.getContentModel();
355                paramNameToType.put(parameter.getName(), parameter);
356            } else {
357                throw new DeploymentException("Only element, sequence, and all particle types are supported." +
358                        " SchemaType name =" + schemaType.getName());
359            }
360    
361            Map attNameToType = new HashMap();
362            if (null != schemaType.getAttributeModel()) {
363                SchemaLocalAttribute[] attributes = schemaType.getAttributeModel().getAttributes();
364                for (int i = 0; i < attributes.length; i++) {
365                    SchemaLocalAttribute attribute = attributes[i];
366                    Object old = attNameToType.put(attribute.getName().getLocalPart(), attribute);
367                    if (old != null) {
368                        throw new DeploymentException("Complain to your expert group member, spec does not support attributes with the same local name and differing namespaces: original: " + old + ", duplicate local name: " + attribute);
369                    }
370                }
371            }
372            
373            VariableMappingType[] variableMappings = javaXmlTypeMapping.getVariableMappingArray();
374    
375            // short-circuit the processing of arrays as they should not define variable-mapping elements. 
376            if (javaClass.isArray()) {
377                if (0 != variableMappings.length) {
378                    // for portability reason we simply warn and not fail.
379                    log.warn("Ignoring variable-mapping defined for class " + javaClass + " which is an array.");
380                }
381                typeInfo.setFields(new FieldDesc[0]);
382                return;
383            }
384    
385            FieldDesc[] fields = new FieldDesc[variableMappings.length];
386            typeInfo.setFields(fields);
387    
388            PropertyDescriptor[] propertyDescriptors = new PropertyDescriptor[0];
389            try {
390                propertyDescriptors = Introspector.getBeanInfo(javaClass).getPropertyDescriptors();
391            } catch (IntrospectionException e) {
392                throw new DeploymentException("Class " + javaClass + " is not a valid javabean", e);
393            }
394            Map properties = new HashMap();
395            for (int i = 0; i < propertyDescriptors.length; i++) {
396                PropertyDescriptor propertyDescriptor = propertyDescriptors[i];
397                properties.put(propertyDescriptor.getName(), propertyDescriptor.getPropertyType());
398            }
399            for (int i = 0; i < variableMappings.length; i++) {
400                VariableMappingType variableMapping = variableMappings[i];
401                String fieldName = variableMapping.getJavaVariableName().getStringValue().trim();
402    
403                if (variableMapping.isSetXmlAttributeName()) {
404                    AttributeDesc attributeDesc = new AttributeDesc();
405                    attributeDesc.setFieldName(fieldName);
406                    Class javaType = (Class) properties.get(fieldName);
407                    if (javaType == null) {
408                        throw new DeploymentException("field name " + fieldName + " not found in " + properties);
409                    }
410                    String attributeLocalName = variableMapping.getXmlAttributeName().getStringValue().trim();
411                    QName xmlName = new QName("", attributeLocalName);
412                    attributeDesc.setXmlName(xmlName);
413    
414                    SchemaLocalAttribute attribute = (SchemaLocalAttribute) attNameToType.get(attributeLocalName);
415                    if (null == attribute) {
416                        throw new DeploymentException("attribute " + xmlName + " not found in schema " + schemaType.getName());
417                    }
418                    attributeDesc.setXmlType(attribute.getType().getName());
419    
420                    fields[i] = attributeDesc;
421                } else {
422                    ElementDesc elementDesc = new ElementDesc();
423                    elementDesc.setFieldName(fieldName);
424                    Class javaType = (Class) properties.get(fieldName);
425                    if (javaType == null) {
426                        //see if it is a public field
427                        try {
428                            Field field = javaClass.getField(fieldName);
429                            javaType = field.getType();
430                        } catch (NoSuchFieldException e) {
431                            throw new DeploymentException("field name " + fieldName + " not found in " + properties, e);
432                        }
433                    }
434                    QName xmlName = new QName("", variableMapping.getXmlElementName().getStringValue().trim());
435                    SchemaParticle particle = (SchemaParticle) paramNameToType.get(xmlName);
436                    if (null == particle) {
437                        xmlName = new QName(ns, variableMapping.getXmlElementName().getStringValue().trim());
438                        particle = (SchemaParticle) paramNameToType.get(xmlName);
439                        if (null == particle) {
440                            throw new DeploymentException("element " + xmlName + " not found in schema " + schemaType.getName());
441                        }
442                    } else if (SchemaParticle.ELEMENT != particle.getParticleType()) {
443                        throw new DeploymentException(xmlName + " is not an element in schema " + schemaType.getName());
444                    }
445                    elementDesc.setNillable(particle.isNillable() || hasEncoded);
446                    elementDesc.setXmlName(xmlName);
447                    if (null != particle.getType().getName()) {
448                        elementDesc.setXmlType(particle.getType().getName());
449                    } else {
450                        QName anonymousName;
451                        if (key.isAnonymous()) {
452                            anonymousName = new QName(key.getqName().getNamespaceURI(), key.getqName().getLocalPart() +
453                                    ">" + particle.getName().getLocalPart());
454                        } else {
455                            anonymousName = new QName(key.getqName().getNamespaceURI(),
456                                    ">" + key.getqName().getLocalPart() + ">" + particle.getName().getLocalPart());
457                        }
458                        elementDesc.setXmlType(anonymousName);
459                    }
460    
461                    if (javaType.isArray()) {
462                        elementDesc.setMinOccurs(particle.getIntMinOccurs());
463                        elementDesc.setMaxOccurs(particle.getIntMaxOccurs());
464                        //TODO axis seems to have the wrong name for this property based on how it is used
465                        elementDesc.setMaxOccursUnbounded(particle.getIntMaxOccurs() > 1);
466                    }
467    
468                    fields[i] = elementDesc;
469                }
470            }
471        }
472    
473        private static class TypeMappingLookup {
474            private static final TypeMappingImpl SOAP_TM = DefaultSOAPEncodingTypeMappingImpl.getSingleton();
475            private static final TypeMappingImpl JAXRPC_TM = DefaultJAXRPC11TypeMappingImpl.getSingleton();
476    
477            public static FactoryPair getFactoryPair(QName xmlType) {
478                Class clazz = SOAP_TM.getClassForQName(xmlType, null, null);
479                SerializerFactory sf;
480                DeserializerFactory df;
481                if (null != clazz) {
482                    sf = SOAP_TM.getSerializer(clazz, xmlType);
483                    df = SOAP_TM.getDeserializer(clazz, xmlType, null);
484                } else {
485                    clazz = JAXRPC_TM.getClassForQName(xmlType, null, null);
486                    if (null == clazz) {
487                        return null;
488                    }
489                    sf = JAXRPC_TM.getSerializer(clazz, xmlType);
490                    df = JAXRPC_TM.getDeserializer(clazz, xmlType, null);
491                }
492                return new FactoryPair(sf.getClass(), df.getClass());
493            }
494        }
495    
496        private static class FactoryPair {
497            private final Class serializerFactoryClass;
498            private final Class deserializerFactoryClass;
499    
500            private FactoryPair(Class serializerFactoryClass, Class deserializerFactoryClass) {
501                this.serializerFactoryClass = serializerFactoryClass;
502                this.deserializerFactoryClass = deserializerFactoryClass;
503            }
504        }
505    }