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
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
121 QName typeQName = new QName(anonTypeQNameString.substring(0, pos), anonTypeQNameString.substring(pos + 1));
122 key = new SchemaTypeKey(typeQName, isElement, isSimpleType, true, null);
123
124
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
135 if (null != TypeMappingLookup.getFactoryPair(key.getqName())) {
136 continue;
137 }
138
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
198
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
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
256
257
258
259
260
261
262
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
278
279
280
281
282
283
284
285
286
287
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
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
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
377 if (javaClass.isArray()) {
378 if (0 != variableMappings.length) {
379
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
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
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 }