001: package org.objectweb.celtix.bus.jaxws;
002:
003: import java.io.StringWriter;
004: import java.lang.reflect.Array;
005: import java.lang.reflect.GenericArrayType;
006: import java.lang.reflect.Method;
007: import java.lang.reflect.ParameterizedType;
008: import java.lang.reflect.Type;
009: import java.util.HashSet;
010: import java.util.Map;
011: import java.util.Set;
012: import java.util.concurrent.ConcurrentHashMap;
013:
014: import javax.jws.WebMethod;
015: import javax.xml.bind.JAXBContext;
016: import javax.xml.bind.JAXBElement;
017: import javax.xml.bind.JAXBException;
018: import javax.xml.bind.MarshalException;
019: import javax.xml.bind.Marshaller;
020: import javax.xml.bind.UnmarshalException;
021: import javax.xml.bind.Unmarshaller;
022:
023: import javax.xml.bind.annotation.XmlElementDecl;
024: import javax.xml.bind.annotation.XmlRootElement;
025: import javax.xml.namespace.QName;
026: import javax.xml.validation.Schema;
027: import javax.xml.ws.Holder;
028: import javax.xml.ws.ProtocolException;
029: import javax.xml.ws.RequestWrapper;
030: import javax.xml.ws.ResponseWrapper;
031:
032: import org.w3c.dom.Node;
033:
034: import org.objectweb.celtix.common.util.StringUtils;
035: import org.objectweb.celtix.ws.addressing.wsdl.AttributedQNameType;
036: import org.objectweb.celtix.ws.addressing.wsdl.ObjectFactory;
037: import org.objectweb.celtix.ws.addressing.wsdl.ServiceNameType;
038:
039: /**
040: * JAXBEncoderDecoder
041: * @author apaibir
042: */
043: public final class JAXBEncoderDecoder {
044:
045: static Map<Class, JAXBContext> contextMap = new ConcurrentHashMap<Class, JAXBContext>();
046:
047: private JAXBEncoderDecoder() {
048: }
049:
050: public static JAXBContext createJAXBContextForClass(Class cls)
051: throws JAXBException {
052: JAXBContext context = contextMap.get(cls);
053: if (context == null) {
054: Set<Class> classes = new HashSet<Class>();
055: getClassesForContext(cls, classes, cls.getClassLoader());
056: classes.add(AttributedQNameType.class);
057: classes.add(ServiceNameType.class);
058: classes.add(ObjectFactory.class);
059: try {
060: context = JAXBContext.newInstance(classes
061: .toArray(new Class[classes.size()]));
062: contextMap.put(cls, context);
063: } catch (JAXBException ex) {
064: throw ex;
065: }
066: }
067: return context;
068: }
069:
070: private static Class getValidClass(Class cls) {
071: if (cls.isEnum()) {
072: return cls;
073: }
074: if (cls.isArray()) {
075: return getValidClass(cls.getComponentType());
076: }
077:
078: if (cls == Holder.class || cls == Object.class
079: || cls == String.class) {
080: cls = null;
081: } else if (cls.isPrimitive() || cls.isInterface()
082: || cls.isAnnotation()) {
083: cls = null;
084: }
085: if (cls != null) {
086: try {
087: if (cls.getConstructor(new Class[0]) == null) {
088: cls = null;
089: }
090: } catch (NoSuchMethodException ex) {
091: cls = null;
092: }
093: }
094: return cls;
095: }
096:
097: private static void addClass(Class cls, Set<Class> classes) {
098: if (cls.isArray()) {
099: classes.add(cls);
100: return;
101: /*
102: Class c2 = getValidClass(cls);
103: if (c2 != null) {
104: classes.add(cls);
105: return;
106: }*/
107: }
108: cls = getValidClass(cls);
109: if (null != cls) {
110: if (cls.isEnum()) {
111: // The object factory stuff doesn't work for enums
112: classes.add(cls);
113: }
114: String name = cls.getPackage().getName() + ".ObjectFactory";
115: try {
116: cls = Class.forName(name, false, cls.getClassLoader());
117: if (cls != null) {
118: classes.add(cls);
119: }
120: } catch (ClassNotFoundException ex) {
121: //cannot add factory, just add the class
122: classes.add(cls);
123: }
124: }
125: }
126:
127: private static void addType(Type cls, Set<Class> classes) {
128: if (cls instanceof Class) {
129: addClass((Class) cls, classes);
130: } else if (cls instanceof ParameterizedType) {
131: for (Type t2 : ((ParameterizedType) cls)
132: .getActualTypeArguments()) {
133: addType(t2, classes);
134: }
135: } else if (cls instanceof GenericArrayType) {
136: GenericArrayType gt = (GenericArrayType) cls;
137: addType(gt.getGenericComponentType(), classes);
138: }
139: }
140:
141: //collect ALL the classes that are accessed by the class
142: private static void getClassesForContext(Class<?> theClass,
143: Set<Class> classes, ClassLoader loader) {
144: Method methods[] = theClass.getMethods();
145: for (Method meth : methods) {
146: //only methods marked as WebMethods are interesting to us
147: WebMethod webMethod = meth.getAnnotation(WebMethod.class);
148: if (webMethod == null) {
149: continue;
150: }
151:
152: for (Type t : meth.getGenericParameterTypes()) {
153: addType(t, classes);
154: }
155: addType(meth.getGenericReturnType(), classes);
156:
157: if (meth.getReturnType().isArray()) {
158: addClass(meth.getReturnType(), classes);
159: }
160: for (Class cls : meth.getParameterTypes()) {
161: addClass(cls, classes);
162: }
163:
164: for (Class<?> cls : meth.getExceptionTypes()) {
165: //addClass(cls, classes);
166: try {
167: Method fim = cls.getMethod("getFaultInfo",
168: new Class[0]);
169: addClass(fim.getReturnType(), classes);
170: } catch (NoSuchMethodException ex) {
171: //ignore - not a valid JAXB fault thing
172: }
173: }
174: try {
175: //Get the RequestWrapper
176: RequestWrapper reqWrapper = meth
177: .getAnnotation(RequestWrapper.class);
178: if (reqWrapper != null) {
179: Class cls = Class.forName(reqWrapper.className(),
180: false, loader);
181: addClass(cls, classes);
182: }
183: //Get the RequestWrapper
184: ResponseWrapper respWrapper = meth
185: .getAnnotation(ResponseWrapper.class);
186: if (respWrapper != null) {
187: Class cls = Class.forName(respWrapper.className(),
188: false, loader);
189: addClass(cls, classes);
190: }
191: } catch (ClassNotFoundException ex) {
192: //ignore
193: }
194: }
195:
196: for (Class intf : theClass.getInterfaces()) {
197: getClassesForContext(intf, classes, loader);
198: }
199: if (theClass.getSuperclass() != null) {
200: getClassesForContext(theClass.getSuperclass(), classes,
201: loader);
202: }
203: }
204:
205: public static void marshall(JAXBContext context, Schema schema,
206: Object elValue, QName elNname, Node destNode) {
207:
208: try {
209: if (context == null) {
210: context = JAXBContext.newInstance(elValue.getClass());
211: }
212: Object mObj = elValue;
213: Marshaller u = context.createMarshaller();
214: u.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
215: u.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
216: Boolean.TRUE);
217:
218: if (elValue.getClass().isAnnotationPresent(
219: XmlRootElement.class)) {
220: String packageName = elValue.getClass().getPackage()
221: .getName();
222: Class<?> objectFactory = Class.forName(packageName
223: + ".ObjectFactory", false, elValue.getClass()
224: .getClassLoader());
225:
226: Method methods[] = objectFactory.getDeclaredMethods();
227: for (Method method : methods) {
228: if (method.getParameterTypes().length == 1
229: && method.getParameterTypes()[0]
230: .equals(elValue.getClass())) {
231:
232: XmlElementDecl elementDecl = method
233: .getAnnotation(XmlElementDecl.class);
234: if (null != elementDecl) {
235: QName elementType = new QName(elementDecl
236: .namespace(), elementDecl.name());
237: if (elementType.equals(elNname)) {
238: mObj = method.invoke(objectFactory
239: .newInstance(), elValue);
240: }
241: }
242: }
243: }
244: } else {
245: mObj = JAXBElement.class.getConstructor(
246: new Class[] { QName.class, Class.class,
247: Object.class }).newInstance(elNname,
248: mObj.getClass(), mObj);
249: }
250: u.setSchema(schema);
251: u.marshal(mObj, destNode);
252: } catch (MarshalException me) {
253: // It's helpful to include the cause in the case of
254: // schema validation exceptions.
255: String message = "Marshalling error ";
256: if (me.getCause() != null) {
257: message += me.getCause();
258: }
259: throw new ProtocolException(message, me);
260: } catch (Exception ex) {
261: throw new ProtocolException("Marshalling Error", ex);
262: }
263: }
264:
265: public static Object unmarshall(JAXBContext context, Schema schema,
266: Node srcNode, QName elName, Class<?> clazz) {
267: Object obj = null;
268: try {
269: if (context == null) {
270: context = JAXBContext.newInstance(clazz);
271: }
272: Unmarshaller u = context.createUnmarshaller();
273: u.setSchema(schema);
274:
275: obj = (clazz != null) ? u.unmarshal(srcNode, clazz) : u
276: .unmarshal(srcNode);
277:
278: if (obj instanceof JAXBElement<?>) {
279: JAXBElement<?> el = (JAXBElement<?>) obj;
280: if (isSame(el.getName(), elName)) {
281: obj = el.getValue();
282: }
283: }
284: } catch (UnmarshalException ue) {
285: // It's helpful to include the cause in the case of
286: // schema validation exceptions.
287: String message = "Unmarshalling error ";
288: if (ue.getCause() != null) {
289: message += ue.getCause();
290: }
291: throw new ProtocolException(message, ue);
292: } catch (Exception ex) {
293: throw new ProtocolException("Unmarshalling error", ex);
294: }
295: return obj;
296: }
297:
298: private static boolean isSame(QName messageQName, QName methodQName) {
299: boolean same = false;
300: if (StringUtils.isEmpty(messageQName.getNamespaceURI())) {
301: same = messageQName.getLocalPart().equals(
302: methodQName.getLocalPart());
303: } else {
304: same = messageQName.equals(methodQName);
305: }
306: return same;
307: }
308:
309: public static Object unmarshall(JAXBContext context, Schema schema,
310: Node srcNode, QName elName) {
311: Object obj = null;
312: try {
313: Unmarshaller u = context.createUnmarshaller();
314: u.setSchema(schema);
315:
316: obj = u.unmarshal(srcNode);
317:
318: if (obj instanceof JAXBElement<?>) {
319: JAXBElement<?> el = (JAXBElement<?>) obj;
320: if (el.getName().equals(elName)) {
321: obj = el.getValue();
322: }
323: }
324: } catch (UnmarshalException ue) {
325: // It's helpful to include the cause in the case of
326: // schema validation exceptions.
327: String message = "Unmarshalling error ";
328: if (ue.getCause() != null) {
329: message += ue.getCause();
330: }
331: throw new ProtocolException(message, ue);
332:
333: } catch (Exception ex) {
334: throw new ProtocolException("Unmarshalling error", ex);
335: }
336: return obj;
337: }
338:
339: public static Class getClassFromType(Type t) {
340: if (t instanceof Class) {
341: return (Class) t;
342: } else if (t instanceof GenericArrayType) {
343: GenericArrayType g = (GenericArrayType) t;
344: return Array.newInstance(
345: getClassFromType(g.getGenericComponentType()), 0)
346: .getClass();
347: } else if (t instanceof ParameterizedType) {
348: ParameterizedType p = (ParameterizedType) t;
349: return getClassFromType(p.getRawType());
350: }
351: //TypeVariable and WildCardType are not handled as it is unlikely such Types will
352: // JAXB Code Generated.
353: assert false;
354: throw new IllegalArgumentException(
355: "Cannot get Class object from unknown Type");
356: }
357:
358: public static String toString(Object obj) throws JAXBException {
359: String name = obj.getClass().getPackage().getName();
360: JAXBContext context = JAXBContext.newInstance(name);
361: JAXBElement<Object> el = new JAXBElement<Object>(new QName(
362: "test"), Object.class, obj);
363: Marshaller m = context.createMarshaller();
364: StringWriter writer = new StringWriter();
365: m.marshal(el, writer);
366:
367: return writer.toString();
368: }
369: }
|