View Javadoc

1   /**
2    *
3    *  Licensed to the Apache Software Foundation (ASF) under one or more
4    *  contributor license agreements.  See the NOTICE file distributed with
5    *  this work for additional information regarding copyright ownership.
6    *  The ASF licenses this file to You under the Apache License, Version 2.0
7    *  (the "License"); you may not use this file except in compliance with
8    *  the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing, software
13   *  distributed under the License is distributed on an "AS IS" BASIS,
14   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   *  See the License for the specific language governing permissions and
16   *  limitations under the License.
17   */
18  package org.apache.geronimo.axis.builder;
19  
20  import java.beans.IntrospectionException;
21  import java.beans.Introspector;
22  import java.beans.PropertyDescriptor;
23  import java.lang.reflect.Field;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  import javax.xml.namespace.QName;
33  import javax.xml.rpc.encoding.DeserializerFactory;
34  import javax.xml.rpc.encoding.SerializerFactory;
35  
36  import org.apache.axis.description.AttributeDesc;
37  import org.apache.axis.description.ElementDesc;
38  import org.apache.axis.description.FieldDesc;
39  import org.apache.axis.description.OperationDesc;
40  import org.apache.axis.description.ParameterDesc;
41  import org.apache.axis.encoding.DefaultJAXRPC11TypeMappingImpl;
42  import org.apache.axis.encoding.DefaultSOAPEncodingTypeMappingImpl;
43  import org.apache.axis.encoding.TypeMappingImpl;
44  import org.apache.axis.encoding.XMLType;
45  import org.apache.axis.encoding.ser.ArrayDeserializerFactory;
46  import org.apache.axis.encoding.ser.ArraySerializerFactory;
47  import org.apache.axis.encoding.ser.BeanDeserializerFactory;
48  import org.apache.axis.encoding.ser.BeanSerializerFactory;
49  import org.apache.axis.encoding.ser.EnumDeserializerFactory;
50  import org.apache.axis.encoding.ser.EnumSerializerFactory;
51  import org.apache.axis.encoding.ser.SimpleListDeserializerFactory;
52  import org.apache.axis.encoding.ser.SimpleListSerializerFactory;
53  import org.apache.commons.logging.Log;
54  import org.apache.commons.logging.LogFactory;
55  import org.apache.geronimo.axis.client.ArrayTypeInfo;
56  import org.apache.geronimo.axis.client.TypeInfo;
57  import org.apache.geronimo.common.DeploymentException;
58  import org.apache.geronimo.kernel.ClassLoading;
59  import org.apache.geronimo.xbeans.j2ee.JavaWsdlMappingType;
60  import org.apache.geronimo.xbeans.j2ee.JavaXmlTypeMappingType;
61  import org.apache.geronimo.xbeans.j2ee.VariableMappingType;
62  import org.apache.geronimo.webservices.builder.SchemaTypeKey;
63  import org.apache.xmlbeans.SchemaLocalAttribute;
64  import org.apache.xmlbeans.SchemaParticle;
65  import org.apache.xmlbeans.SchemaProperty;
66  import org.apache.xmlbeans.SchemaType;
67  import org.apache.xmlbeans.soap.SOAPArrayType;
68  import org.apache.xmlbeans.soap.SchemaWSDLArrayType;
69  
70  /**
71   * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $
72   */
73  public class HeavyweightTypeInfoBuilder implements TypeInfoBuilder {
74      private static final String SOAP_ENCODING_NS = "http://schemas.xmlsoap.org/soap/encoding/";
75      private static final String XML_SCHEMA_NS = "http://www.w3.org/2001/XMLSchema";
76      
77      private static final Log log = LogFactory.getLog(HeavyweightTypeInfoBuilder.class);
78  
79      private final ClassLoader cl;
80      private final Map schemaTypeKeyToSchemaTypeMap;
81      private final Set wrapperElementQNames;
82      private final Collection operations;
83      private final boolean hasEncoded;
84  
85      public HeavyweightTypeInfoBuilder(ClassLoader cl, Map schemaTypeKeyToSchemaTypeMap, Set wrapperElementQNames, Collection operations, boolean hasEncoded) {
86          this.cl = cl;
87          this.schemaTypeKeyToSchemaTypeMap = schemaTypeKeyToSchemaTypeMap;
88          this.wrapperElementQNames = wrapperElementQNames;
89          this.operations = operations;
90          this.hasEncoded = hasEncoded;
91      }
92  
93      public List buildTypeInfo(JavaWsdlMappingType mapping) throws DeploymentException {
94          List typeInfoList = new ArrayList();
95  
96          Set mappedTypeQNames = new HashSet();
97  
98          JavaXmlTypeMappingType[] javaXmlTypeMappings = mapping.getJavaXmlTypeMappingArray();
99          for (int j = 0; j < javaXmlTypeMappings.length; j++) {
100             JavaXmlTypeMappingType javaXmlTypeMapping = javaXmlTypeMappings[j];
101 
102             SchemaTypeKey key;
103             boolean isElement = javaXmlTypeMapping.getQnameScope().getStringValue().equals("element");
104             boolean isSimpleType = javaXmlTypeMapping.getQnameScope().getStringValue().equals("simpleType");
105             if (javaXmlTypeMapping.isSetRootTypeQname()) {
106                 QName typeQName = javaXmlTypeMapping.getRootTypeQname().getQNameValue();
107                 key = new SchemaTypeKey(typeQName, isElement, isSimpleType, false, null);
108 
109                 // Skip the wrapper elements.
110                 if (wrapperElementQNames.contains(typeQName)) {
111                     continue;
112                 }
113             } else if (javaXmlTypeMapping.isSetAnonymousTypeQname()) {
114                 String anonTypeQNameString = javaXmlTypeMapping.getAnonymousTypeQname().getStringValue();
115                 int pos = anonTypeQNameString.lastIndexOf(":");
116                 if (pos == -1) {
117                     throw new DeploymentException("anon QName is invalid, no final ':' " + anonTypeQNameString);
118                 }
119 
120                 //this appears to be ignored...
121                 QName typeQName = new QName(anonTypeQNameString.substring(0, pos), anonTypeQNameString.substring(pos + 1));
122                 key = new SchemaTypeKey(typeQName, isElement, isSimpleType, true, null);
123 
124                 // Skip the wrapper elements.
125                 if (wrapperElementQNames.contains(new QName(anonTypeQNameString.substring(0, pos), anonTypeQNameString.substring(pos + 2)))) {
126                     continue;
127                 }
128             } else {
129                 throw new DeploymentException("either root type qname or anonymous type qname must be set");
130             }
131 
132             SchemaType schemaType = (SchemaType) schemaTypeKeyToSchemaTypeMap.get(key);
133             if (schemaType == null) {
134                 // if it is a built-in type, then one assumes a redundant mapping. 
135                 if (null != TypeMappingLookup.getFactoryPair(key.getqName())) {
136                     continue;
137                 }
138 //              throw new DeploymentException("Schema type key " + key + " not found in analyzed schema: " + schemaTypeKeyToSchemaTypeMap);
139                 log.warn("Schema type key " + key + " not found in analyzed schema: " + schemaTypeKeyToSchemaTypeMap);
140                 continue;
141             }
142             mappedTypeQNames.add(key.getqName());
143 
144             String className = javaXmlTypeMapping.getJavaType().getStringValue().trim();
145             Class clazz = null;
146             try {
147                 clazz = ClassLoading.loadClass(className, cl);
148             } catch (ClassNotFoundException e2) {
149                 throw new DeploymentException("Could not load java type", e2);
150             }
151 
152             TypeInfo.UpdatableTypeInfo internalTypeInfo = defineSerializerPair(schemaType, clazz);
153 
154             populateInternalTypeInfo(clazz, key, schemaType, javaXmlTypeMapping, internalTypeInfo);
155 
156             typeInfoList.add(internalTypeInfo.buildTypeInfo());
157         }
158 
159         Map qNameToKey = new HashMap();
160         for (Iterator iter = schemaTypeKeyToSchemaTypeMap.keySet().iterator(); iter.hasNext();) {
161             SchemaTypeKey key = (SchemaTypeKey) iter.next();
162             qNameToKey.put(key.getqName(), key);
163         }
164 
165         for (Iterator iter = operations.iterator(); iter.hasNext();) {
166             OperationDesc operationDesc = (OperationDesc) iter.next();
167             ArrayList parameters = new ArrayList(operationDesc.getParameters());
168             ParameterDesc returnParameterDesc = operationDesc.getReturnParamDesc();
169             if (null != returnParameterDesc.getTypeQName() &&
170                     false == returnParameterDesc.getTypeQName().equals(XMLType.AXIS_VOID)) {
171                 parameters.add(returnParameterDesc);
172             }
173             for (Iterator iterator = parameters.iterator(); iterator.hasNext();) {
174                 ParameterDesc parameterDesc = (ParameterDesc) iterator.next();
175                 QName typeQName = parameterDesc.getTypeQName();
176                 if (null == typeQName) {
177                     continue;
178                 } else if (mappedTypeQNames.contains(typeQName)) {
179                     continue;
180                 } else if (typeQName.getNamespaceURI().equals(XML_SCHEMA_NS) ||
181                         typeQName.getNamespaceURI().equals(SOAP_ENCODING_NS)) {
182                     continue;
183                 }
184 
185                 SchemaTypeKey key = (SchemaTypeKey) qNameToKey.get(typeQName);
186                 if (null == key) {
187                     log.warn("Type QName [" + typeQName + "] defined by operation [" +
188                             operationDesc + "] has not been found in schema: " + schemaTypeKeyToSchemaTypeMap);
189                     continue;
190                 }
191                 SchemaType schemaType = (SchemaType) schemaTypeKeyToSchemaTypeMap.get(key);
192                 mappedTypeQNames.add(key.getqName());
193 
194                 if (false == schemaType.isSimpleType()) {
195                     if (false == parameterDesc.getJavaType().isArray()) {
196                         if (false == mappedTypeQNames.contains(schemaType.getName())) {
197                             // TODO: this lookup is not enough: the jaxrpc mapping file may define an anonymous
198                             // mapping.
199                             log.warn("Operation [" + operationDesc + "] uses XML type [" + schemaType +
200                                     "], whose mapping is not declared by the jaxrpc mapping file.\n Continuing deployment; " +
201                                     "yet, the deployment is not-portable.");
202                         }
203                         continue;
204                     }
205                 }
206 
207                 Class clazz = parameterDesc.getJavaType();
208                 TypeInfo.UpdatableTypeInfo internalTypeInfo =  defineSerializerPair(schemaType, clazz);
209                 setTypeQName(internalTypeInfo, key);
210                 internalTypeInfo.setFields(new FieldDesc[0]);
211 
212                 typeInfoList.add(internalTypeInfo.buildTypeInfo());
213             }
214         }
215 
216         return typeInfoList;
217     }
218 
219     private TypeInfo.UpdatableTypeInfo defineSerializerPair(SchemaType schemaType, Class clazz)
220             throws DeploymentException {
221         TypeInfo.UpdatableTypeInfo internalTypeInfo = new TypeInfo.UpdatableTypeInfo();
222         Class serializerFactoryClass = null;
223         Class deserializerFactoryClass = null;
224         if (schemaType.isSimpleType()) {
225             if (SchemaType.ATOMIC == schemaType.getSimpleVariety()) {
226                 if (clazz.isArray()) {
227                     internalTypeInfo = new ArrayTypeInfo.UpdatableArrayTypeInfo();
228                     serializerFactoryClass = ArraySerializerFactory.class;
229                     deserializerFactoryClass = ArrayDeserializerFactory.class;
230                     //TODO set componentType, componentQName
231                 } else if (null != schemaType.getEnumerationValues()) {
232                     serializerFactoryClass = EnumSerializerFactory.class;
233                     deserializerFactoryClass = EnumDeserializerFactory.class;
234                 } else {
235                     QName typeQName = schemaType.getPrimitiveType().getName();
236                     FactoryPair pair = (FactoryPair) TypeMappingLookup.getFactoryPair(typeQName);
237                     if (null == pair) {
238                         throw new DeploymentException("Primitive type [" + typeQName + "] is not registered.");
239                     }
240                     serializerFactoryClass = pair.serializerFactoryClass;
241                     deserializerFactoryClass = pair.deserializerFactoryClass;
242                 }
243             } else if (SchemaType.LIST == schemaType.getSimpleVariety()) {
244                 serializerFactoryClass = SimpleListSerializerFactory.class;
245                 deserializerFactoryClass = SimpleListDeserializerFactory.class;
246             } else {
247                 throw new DeploymentException("Schema type [" + schemaType + "] is invalid.");
248             }
249         } else {
250             if (clazz.isArray()) {
251                 internalTypeInfo = new ArrayTypeInfo.UpdatableArrayTypeInfo();
252                 serializerFactoryClass = ArraySerializerFactory.class;
253                 deserializerFactoryClass = ArrayDeserializerFactory.class;
254                 QName componentType = null;
255                 //First, handle case that looks like this:
256 //                <complexType name="ArrayOfstring">
257 //                    <complexContent>
258 //                        <restriction base="soapenc:Array">
259 //                            <attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:string[]"/>
260 //                        </restriction>
261 //                    </complexContent>
262 //                </complexType>
263                 SchemaLocalAttribute arrayTypeAttribute =  schemaType.getAttributeModel().getAttribute(new QName(SOAP_ENCODING_NS, "arrayType"));
264                 if (arrayTypeAttribute != null) {
265                     SchemaWSDLArrayType wsdlArrayType = (SchemaWSDLArrayType) arrayTypeAttribute;
266                     SOAPArrayType soapArrayType = wsdlArrayType.getWSDLArrayType();
267                     if (soapArrayType != null) {
268                         componentType = soapArrayType.getQName();
269                         log.debug("extracted componentType " + componentType + " from schemaType " + schemaType);
270                     } else {
271                         log.info("no SOAPArrayType for component from schemaType " + schemaType);
272                     }
273                 } else {
274                     log.warn("No soap array info for schematype: " + schemaType);
275                 }
276                 if (componentType == null) {
277                     //If that didn't work, try to handle case like this:
278 //                    <complexType name="ArrayOfstring1">
279 //                        <complexContent>
280 //                            <restriction base="soapenc:Array">
281 //                                <sequence>
282 //                                    <element name="string1" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
283 //                                </sequence>
284 //                            </restriction>
285 //                        </complexContent>
286 //                    </complexType>
287                     //todo consider if we should check for maxOccurs > 1
288                     if (schemaType.getBaseType().getName().equals(new QName(SOAP_ENCODING_NS, "Array"))) {
289                         SchemaProperty[] properties = schemaType.getDerivedProperties();
290                         if (properties.length != 1) {
291                             throw new DeploymentException("more than one element inside array definition: " + schemaType);
292                         }
293                         componentType = properties[0].getType().getName();
294                         log.debug("determined component type from element type");
295                     }
296 
297                 }
298 
299                 ((ArrayTypeInfo.UpdatableArrayTypeInfo)internalTypeInfo).setComponentType(componentType);
300                 //If we understand the axis comments correctly, componentQName is never set for j2ee ws.
301             } else {
302                 QName typeQName;
303                 if (SchemaType.SIMPLE_CONTENT == schemaType.getContentType()) {
304                     typeQName = schemaType.getBaseType().getName();
305                 } else if (SchemaType.EMPTY_CONTENT == schemaType.getContentType() ||
306                         SchemaType.ELEMENT_CONTENT == schemaType.getContentType() ||
307                         SchemaType.MIXED_CONTENT == schemaType.getContentType()) {
308                     typeQName = schemaType.getName();
309                 } else {
310                     throw new DeploymentException("Schema type [" + schemaType + "] is invalid.");
311                 }
312                 FactoryPair pair = (FactoryPair) TypeMappingLookup.getFactoryPair(typeQName);
313                 if (null != pair) {
314                     serializerFactoryClass = pair.serializerFactoryClass;
315                     deserializerFactoryClass = pair.deserializerFactoryClass;
316                 } else {
317                     serializerFactoryClass = BeanSerializerFactory.class;
318                     deserializerFactoryClass = BeanDeserializerFactory.class;
319                 }
320             }
321         }
322 
323         internalTypeInfo.setClazz(clazz);
324         internalTypeInfo.setSerializerClass(serializerFactoryClass);
325         internalTypeInfo.setDeserializerClass(deserializerFactoryClass);
326         return internalTypeInfo;
327     }
328 
329     private void setTypeQName(TypeInfo.UpdatableTypeInfo typeInfo, SchemaTypeKey key) {
330         //figure out the name axis expects to look up under.
331         QName axisKey = key.getElementQName();
332         if (axisKey == null) {
333             axisKey = key.getqName();
334         }
335         typeInfo.setQName(axisKey);
336     }
337 
338     private void populateInternalTypeInfo(Class javaClass, SchemaTypeKey key, SchemaType schemaType, JavaXmlTypeMappingType javaXmlTypeMapping, TypeInfo.UpdatableTypeInfo typeInfo) throws DeploymentException {
339         String ns = key.getqName().getNamespaceURI();
340         typeInfo.setCanSearchParents(schemaType.getDerivationType() == SchemaType.DT_RESTRICTION);
341 
342         setTypeQName(typeInfo, key);
343 
344         Map paramNameToType = new HashMap();
345         if (null == schemaType.getContentModel()) {
346             ;
347         } else if (SchemaParticle.SEQUENCE == schemaType.getContentModel().getParticleType()
348                 || SchemaParticle.ALL == schemaType.getContentModel().getParticleType()) {
349             SchemaParticle[] properties = schemaType.getContentModel().getParticleChildren();
350             for (int i = 0; i < properties.length; i++) {
351                 SchemaParticle parameter = properties[i];
352                 paramNameToType.put(parameter.getName(), parameter);
353             }
354         } else if (SchemaParticle.ELEMENT == schemaType.getContentModel().getParticleType()) {
355             SchemaParticle parameter = schemaType.getContentModel();
356             paramNameToType.put(parameter.getName(), parameter);
357         } else {
358             throw new DeploymentException("Only element, sequence, and all particle types are supported." +
359                     " SchemaType name =" + schemaType.getName());
360         }
361 
362         Map attNameToType = new HashMap();
363         if (null != schemaType.getAttributeModel()) {
364             SchemaLocalAttribute[] attributes = schemaType.getAttributeModel().getAttributes();
365             for (int i = 0; i < attributes.length; i++) {
366                 SchemaLocalAttribute attribute = attributes[i];
367                 Object old = attNameToType.put(attribute.getName().getLocalPart(), attribute);
368                 if (old != null) {
369                     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);
370                 }
371             }
372         }
373         
374         VariableMappingType[] variableMappings = javaXmlTypeMapping.getVariableMappingArray();
375 
376         // short-circuit the processing of arrays as they should not define variable-mapping elements. 
377         if (javaClass.isArray()) {
378             if (0 != variableMappings.length) {
379                 // for portability reason we simply warn and not fail.
380                 log.warn("Ignoring variable-mapping defined for class " + javaClass + " which is an array.");
381             }
382             typeInfo.setFields(new FieldDesc[0]);
383             return;
384         }
385 
386         FieldDesc[] fields = new FieldDesc[variableMappings.length];
387         typeInfo.setFields(fields);
388 
389         PropertyDescriptor[] propertyDescriptors = new PropertyDescriptor[0];
390         try {
391             propertyDescriptors = Introspector.getBeanInfo(javaClass).getPropertyDescriptors();
392         } catch (IntrospectionException e) {
393             throw new DeploymentException("Class " + javaClass + " is not a valid javabean", e);
394         }
395         Map properties = new HashMap();
396         for (int i = 0; i < propertyDescriptors.length; i++) {
397             PropertyDescriptor propertyDescriptor = propertyDescriptors[i];
398             properties.put(propertyDescriptor.getName(), propertyDescriptor.getPropertyType());
399         }
400         for (int i = 0; i < variableMappings.length; i++) {
401             VariableMappingType variableMapping = variableMappings[i];
402             String fieldName = variableMapping.getJavaVariableName().getStringValue().trim();
403 
404             if (variableMapping.isSetXmlAttributeName()) {
405                 AttributeDesc attributeDesc = new AttributeDesc();
406                 attributeDesc.setFieldName(fieldName);
407                 Class javaType = (Class) properties.get(fieldName);
408                 if (javaType == null) {
409                     throw new DeploymentException("field name " + fieldName + " not found in " + properties);
410                 }
411                 String attributeLocalName = variableMapping.getXmlAttributeName().getStringValue().trim();
412                 QName xmlName = new QName("", attributeLocalName);
413                 attributeDesc.setXmlName(xmlName);
414 
415                 SchemaLocalAttribute attribute = (SchemaLocalAttribute) attNameToType.get(attributeLocalName);
416                 if (null == attribute) {
417                     throw new DeploymentException("attribute " + xmlName + " not found in schema " + schemaType.getName());
418                 }
419                 attributeDesc.setXmlType(attribute.getType().getName());
420 
421                 fields[i] = attributeDesc;
422             } else {
423                 ElementDesc elementDesc = new ElementDesc();
424                 elementDesc.setFieldName(fieldName);
425                 Class javaType = (Class) properties.get(fieldName);
426                 if (javaType == null) {
427                     //see if it is a public field
428                     try {
429                         Field field = javaClass.getField(fieldName);
430                         javaType = field.getType();
431                     } catch (NoSuchFieldException e) {
432                         throw new DeploymentException("field name " + fieldName + " not found in " + properties);
433                     }
434                 }
435                 QName xmlName = new QName("", variableMapping.getXmlElementName().getStringValue().trim());
436                 SchemaParticle particle = (SchemaParticle) paramNameToType.get(xmlName);
437                 if (null == particle) {
438                     xmlName = new QName(ns, variableMapping.getXmlElementName().getStringValue().trim());
439                     particle = (SchemaParticle) paramNameToType.get(xmlName);
440                     if (null == particle) {
441                         throw new DeploymentException("element " + xmlName + " not found in schema " + schemaType.getName());
442                     }
443                 } else if (SchemaParticle.ELEMENT != particle.getParticleType()) {
444                     throw new DeploymentException(xmlName + " is not an element in schema " + schemaType.getName());
445                 }
446                 elementDesc.setNillable(particle.isNillable() || hasEncoded);
447                 elementDesc.setXmlName(xmlName);
448                 if (null != particle.getType().getName()) {
449                     elementDesc.setXmlType(particle.getType().getName());
450                 } else {
451                     QName anonymousName;
452                     if (key.isAnonymous()) {
453                         anonymousName = new QName(key.getqName().getNamespaceURI(), key.getqName().getLocalPart() +
454                                 ">" + particle.getName().getLocalPart());
455                     } else {
456                         anonymousName = new QName(key.getqName().getNamespaceURI(),
457                                 ">" + key.getqName().getLocalPart() + ">" + particle.getName().getLocalPart());
458                     }
459                     elementDesc.setXmlType(anonymousName);
460                 }
461 
462                 if (javaType.isArray()) {
463                     elementDesc.setMinOccurs(particle.getIntMinOccurs());
464                     elementDesc.setMaxOccurs(particle.getIntMaxOccurs());
465                     //TODO axis seems to have the wrong name for this property based on how it is used
466                     elementDesc.setMaxOccursUnbounded(particle.getIntMaxOccurs() > 1);
467                 }
468 
469                 fields[i] = elementDesc;
470             }
471         }
472     }
473 
474     private static class TypeMappingLookup {
475         private static final TypeMappingImpl SOAP_TM = DefaultSOAPEncodingTypeMappingImpl.getSingleton();
476         private static final TypeMappingImpl JAXRPC_TM = DefaultJAXRPC11TypeMappingImpl.getSingleton();
477 
478         public static FactoryPair getFactoryPair(QName xmlType) {
479             Class clazz = SOAP_TM.getClassForQName(xmlType, null, null);
480             SerializerFactory sf;
481             DeserializerFactory df;
482             if (null != clazz) {
483                 sf = SOAP_TM.getSerializer(clazz, xmlType);
484                 df = SOAP_TM.getDeserializer(clazz, xmlType, null);
485             } else {
486                 clazz = JAXRPC_TM.getClassForQName(xmlType, null, null);
487                 if (null == clazz) {
488                     return null;
489                 }
490                 sf = JAXRPC_TM.getSerializer(clazz, xmlType);
491                 df = JAXRPC_TM.getDeserializer(clazz, xmlType, null);
492             }
493             return new FactoryPair(sf.getClass(), df.getClass());
494         }
495     }
496 
497     private static class FactoryPair {
498         private final Class serializerFactoryClass;
499         private final Class deserializerFactoryClass;
500 
501         private FactoryPair(Class serializerFactoryClass, Class deserializerFactoryClass) {
502             this.serializerFactoryClass = serializerFactoryClass;
503             this.deserializerFactoryClass = deserializerFactoryClass;
504         }
505     }
506 }