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 }