001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */package org.apache.cxf.aegis.databinding;
019:
020: import java.lang.reflect.Method;
021: import java.util.ArrayList;
022: import java.util.HashMap;
023: import java.util.HashSet;
024: import java.util.Iterator;
025: import java.util.List;
026: import java.util.Map;
027: import java.util.Set;
028:
029: import javax.xml.namespace.QName;
030: import javax.xml.stream.XMLStreamReader;
031: import javax.xml.stream.XMLStreamWriter;
032:
033: import org.apache.cxf.aegis.DatabindingException;
034: import org.apache.cxf.aegis.type.DefaultTypeMappingRegistry;
035: import org.apache.cxf.aegis.type.Type;
036: import org.apache.cxf.aegis.type.TypeMapping;
037: import org.apache.cxf.aegis.type.TypeMappingRegistry;
038: import org.apache.cxf.aegis.type.TypeUtil;
039: import org.apache.cxf.aegis.type.basic.BeanType;
040: import org.apache.cxf.aegis.util.XmlConstants;
041: import org.apache.cxf.common.classloader.ClassLoaderUtils;
042: import org.apache.cxf.databinding.DataBinding;
043: import org.apache.cxf.databinding.DataReader;
044: import org.apache.cxf.databinding.DataWriter;
045: import org.apache.cxf.frontend.MethodDispatcher;
046: import org.apache.cxf.frontend.SimpleMethodDispatcher;
047: import org.apache.cxf.service.Service;
048: import org.apache.cxf.service.factory.ServiceConstructionException;
049: import org.apache.cxf.service.model.AbstractMessageContainer;
050: import org.apache.cxf.service.model.FaultInfo;
051: import org.apache.cxf.service.model.MessagePartInfo;
052: import org.apache.cxf.service.model.OperationInfo;
053: import org.apache.cxf.service.model.SchemaInfo;
054: import org.apache.cxf.service.model.ServiceInfo;
055: import org.apache.ws.commons.schema.XmlSchema;
056: import org.apache.ws.commons.schema.XmlSchemaCollection;
057: import org.apache.ws.commons.schema.utils.NamespaceMap;
058: import org.jdom.Attribute;
059: import org.jdom.Document;
060: import org.jdom.Element;
061: import org.jdom.JDOMException;
062: import org.jdom.Namespace;
063: import org.jdom.output.DOMOutputter;
064:
065: /**
066: * Handles DataBidning functions for Aegis.
067: * <p>
068: * NOTE: There is an assumed 1:1 mapping between an AegisDatabidning and a Service!
069: */
070: public class AegisDatabinding implements DataBinding {
071:
072: public static final String CURRENT_MESSAGE_PART = "currentMessagePart";
073: public static final String TYPE_MAPPING_KEY = "type.mapping";
074: public static final String ENCODING_URI_KEY = "type.encodingUri";
075: public static final String WRITE_XSI_TYPE_KEY = "writeXsiType";
076: public static final String OVERRIDE_TYPES_KEY = "overrideTypesList";
077: public static final String READ_XSI_TYPE_KEY = "readXsiType";
078:
079: protected static final int IN_PARAM = 0;
080: protected static final int OUT_PARAM = 1;
081: protected static final int FAULT_PARAM = 2;
082:
083: private TypeMappingRegistry typeMappingRegistry;
084: private Map<MessagePartInfo, Type> part2Type;
085: private List overrideTypes;
086: private Service service;
087:
088: public AegisDatabinding() {
089: super ();
090: this .typeMappingRegistry = new DefaultTypeMappingRegistry(true);
091: part2Type = new HashMap<MessagePartInfo, Type>();
092: }
093:
094: @SuppressWarnings("unchecked")
095: public <T> DataReader<T> createReader(Class<T> cls) {
096: if (cls.equals(XMLStreamReader.class)) {
097: return (DataReader<T>) new XMLStreamDataReader(this );
098: } else {
099: throw new UnsupportedOperationException();
100: }
101: }
102:
103: @SuppressWarnings("unchecked")
104: public <T> DataWriter<T> createWriter(Class<T> cls) {
105: if (cls.equals(XMLStreamWriter.class)) {
106: return (DataWriter<T>) new XMLStreamDataWriter(this );
107: } else {
108: throw new UnsupportedOperationException();
109: }
110: }
111:
112: public Class<?>[] getSupportedReaderFormats() {
113: return new Class[] { XMLStreamReader.class };
114: }
115:
116: public Class<?>[] getSupportedWriterFormats() {
117: return new Class[] { XMLStreamWriter.class };
118: }
119:
120: public TypeMappingRegistry getTypeMappingRegistry() {
121: return typeMappingRegistry;
122: }
123:
124: public void setTypeMappingRegistry(
125: TypeMappingRegistry typeMappingRegistry) {
126: this .typeMappingRegistry = typeMappingRegistry;
127: }
128:
129: public void initialize(Service s) {
130: this .service = s;
131:
132: QName serviceName = s.getServiceInfos().get(0).getName();
133: TypeMapping serviceTM = typeMappingRegistry.createTypeMapping(
134: XmlConstants.XSD, true);
135: typeMappingRegistry.register(serviceName.getNamespaceURI(),
136: serviceTM);
137:
138: s.put(TypeMapping.class.getName(), serviceTM);
139:
140: Set<Type> deps = new HashSet<Type>();
141:
142: for (ServiceInfo info : s.getServiceInfos()) {
143: for (OperationInfo opInfo : info.getInterface()
144: .getOperations()) {
145: if (opInfo.isUnwrappedCapable()) {
146: initializeOperation(s, serviceTM, opInfo
147: .getUnwrappedOperation(), deps);
148: } else {
149: initializeOperation(s, serviceTM, opInfo, deps);
150: }
151: }
152: }
153:
154: List<Type> additional = getAdditionalTypes(s, serviceTM);
155:
156: if (additional != null) {
157: for (Type t : additional) {
158: if (!deps.contains(t)) {
159: deps.add(t);
160: }
161: }
162: }
163:
164: createSchemas(s, deps);
165: }
166:
167: List<Type> getAdditionalTypes(Service s, TypeMapping tm) {
168: List classes = (List) s.get(OVERRIDE_TYPES_KEY);
169:
170: this .overrideTypes = classes;
171:
172: if (classes != null) {
173: List<Type> types = new ArrayList<Type>();
174: for (Iterator it = classes.iterator(); it.hasNext();) {
175: String typeName = (String) it.next();
176: Class c;
177: try {
178: c = ClassLoaderUtils.loadClass(typeName,
179: TypeUtil.class);
180: } catch (ClassNotFoundException e) {
181: throw new DatabindingException(
182: "Could not find override type class: "
183: + typeName, e);
184: }
185:
186: Type t = tm.getType(c);
187: if (t == null) {
188: t = tm.getTypeCreator().createType(c);
189: tm.register(t);
190: }
191: if (t instanceof BeanType) {
192: BeanType bt = (BeanType) t;
193: bt.getTypeInfo().setExtension(true);
194: types.add(bt);
195: }
196: }
197: return types;
198: }
199: return null;
200: }
201:
202: private void initializeOperation(Service s, TypeMapping serviceTM,
203: OperationInfo opInfo, Set<Type> deps) {
204: try {
205: initializeMessage(s, serviceTM, opInfo.getInput(),
206: IN_PARAM, deps);
207:
208: if (opInfo.hasOutput()) {
209: initializeMessage(s, serviceTM, opInfo.getOutput(),
210: OUT_PARAM, deps);
211: }
212:
213: for (FaultInfo info : opInfo.getFaults()) {
214: initializeMessage(s, serviceTM, info, FAULT_PARAM, deps);
215: }
216:
217: } catch (DatabindingException e) {
218: e.prepend("Error initializing parameters for operation "
219: + opInfo.getName());
220: throw e;
221: }
222: }
223:
224: protected void initializeMessage(Service s, TypeMapping serviceTM,
225: AbstractMessageContainer container, int partType,
226: Set<Type> deps) {
227: for (Iterator itr = container.getMessageParts().iterator(); itr
228: .hasNext();) {
229: MessagePartInfo part = (MessagePartInfo) itr.next();
230:
231: Type type = getParameterType(s, serviceTM, part, partType);
232:
233: if (type.isAbstract()) {
234: part.setTypeQName(type.getSchemaType());
235: } else {
236: part.setElementQName(type.getSchemaType());
237: }
238:
239: part2Type.put(part, type);
240:
241: // QName elName = getSuggestedName(service, op, param)
242: deps.add(type);
243:
244: addDependencies(deps, type);
245: }
246: }
247:
248: private void addDependencies(Set<Type> deps, Type type) {
249: Set<Type> typeDeps = type.getDependencies();
250: if (typeDeps != null) {
251: for (Type t : typeDeps) {
252: if (!deps.contains(t)) {
253: deps.add(t);
254: addDependencies(deps, t);
255: }
256: }
257: }
258: }
259:
260: private void createSchemas(Service s, Set<Type> deps) {
261: Map<String, Set<Type>> tns2Type = new HashMap<String, Set<Type>>();
262: for (Type t : deps) {
263: String ns = t.getSchemaType().getNamespaceURI();
264: Set<Type> types = tns2Type.get(ns);
265: if (types == null) {
266: types = new HashSet<Type>();
267: tns2Type.put(ns, types);
268: }
269: types.add(t);
270: }
271:
272: for (Map.Entry<String, Set<Type>> entry : tns2Type.entrySet()) {
273: Element e = new Element("schema", "xsd", XmlConstants.XSD);
274:
275: e.setAttribute(new Attribute("targetNamespace", entry
276: .getKey()));
277: e.setAttribute(new Attribute("elementFormDefault",
278: "qualified"));
279: e.setAttribute(new Attribute("attributeFormDefault",
280: "qualified"));
281:
282: for (Type t : entry.getValue()) {
283: t.writeSchema(e);
284: }
285:
286: if (e.getChildren().size() == 0) {
287: continue;
288: }
289:
290: try {
291: XmlSchemaCollection col = new XmlSchemaCollection();
292: NamespaceMap nsMap = new NamespaceMap();
293: nsMap.add("xsd", "http://www.w3.org/2001/XMLSchema");
294:
295: for (Iterator itr = e.getAdditionalNamespaces()
296: .iterator(); itr.hasNext();) {
297: Namespace n = (Namespace) itr.next();
298: nsMap.add(n.getPrefix(), n.getURI());
299: }
300:
301: col.setNamespaceContext(nsMap);
302:
303: org.w3c.dom.Document schema = new DOMOutputter()
304: .output(new Document(e));
305:
306: for (ServiceInfo si : s.getServiceInfos()) {
307: SchemaInfo info = new SchemaInfo(si, entry.getKey());
308:
309: info.setElement(schema.getDocumentElement());
310:
311: XmlSchema xmlSchema = col.read(schema
312: .getDocumentElement());
313: info.setSchema(xmlSchema);
314:
315: info.setSystemId(entry.getKey());
316:
317: si.addSchema(info);
318: }
319: } catch (JDOMException e1) {
320: throw new ServiceConstructionException(e1);
321: }
322: }
323:
324: }
325:
326: public QName getSuggestedName(Service s, TypeMapping tm,
327: OperationInfo op, int param) {
328: Method m = getMethod(s, op);
329: if (m == null) {
330: return null;
331: }
332:
333: QName name = tm.getTypeCreator().getElementName(m, param);
334:
335: // No mapped name was specified, so if its a complex type use that name
336: // instead
337: if (name == null) {
338: Type type = tm.getTypeCreator().createType(m, param);
339:
340: if (type.isComplex() && !type.isAbstract()) {
341: name = type.getSchemaType();
342: }
343: }
344:
345: return name;
346: }
347:
348: private Type getParameterType(Service s, TypeMapping tm,
349: MessagePartInfo param, int paramtype) {
350: Type type = tm.getType(param.getTypeQName());
351:
352: /*
353: * if (type == null && tm.isRegistered(param.getTypeClass())) { type =
354: * tm.getType(param.getTypeClass()); part2type.put(param, type); }
355: */
356:
357: int offset = 0;
358: if (paramtype == OUT_PARAM) {
359: offset = 1;
360: }
361: if (type == null) {
362: OperationInfo op = param.getMessageInfo().getOperation();
363:
364: Method m = getMethod(s, op);
365: if (paramtype != FAULT_PARAM && m != null) {
366:
367: /*
368: * Note: we are not registering the type here, because it is an
369: * anonymous type. Potentially there could be many schema types
370: * with this name. For example, there could be many ns:in0
371: * paramters.
372: */
373: type = tm.getTypeCreator().createType(m,
374: param.getIndex() - offset);
375: } else {
376: type = tm.getTypeCreator().createType(
377: param.getTypeClass());
378: }
379:
380: type.setTypeMapping(tm);
381:
382: part2Type.put(param, type);
383: }
384:
385: return type;
386: }
387:
388: private Method getMethod(Service s, OperationInfo op) {
389: MethodDispatcher md = (MethodDispatcher) s
390: .get(MethodDispatcher.class.getName());
391: SimpleMethodDispatcher smd = (SimpleMethodDispatcher) md;
392: return smd.getPrimaryMethod(op);
393: }
394:
395: public Type getType(MessagePartInfo part) {
396: return part2Type.get(part);
397: }
398:
399: public List getOverrideTypes() {
400: return overrideTypes;
401: }
402:
403: public Service getService() {
404: return service;
405: }
406:
407: }
|