View Javadoc

1    /****************************************************************************
2    * Copyright (c) 2005, 2006, 2007, 2008, 2009 Imola Informatica.
3    * All rights reserved. This program and the accompanying materials
4    * are made available under the terms of the LGPL License v2.1
5    * which accompanies this distribution, and is available at
6    * http://www.gnu.org/licenses/lgpl.html
7    ****************************************************************************/
8   
9   package it.imolinfo.jbi4corba.jbi.cxf;
10  
11  import it.imolinfo.jbi4corba.Logger;
12  import it.imolinfo.jbi4corba.LoggerFactory;
13  import it.imolinfo.jbi4corba.jbi.Messages;
14  
15  import java.lang.reflect.Array;
16  import java.lang.reflect.Constructor;
17  import java.lang.reflect.Field;
18  import java.lang.reflect.GenericArrayType;
19  import java.lang.reflect.Method;
20  import java.lang.reflect.ParameterizedType;
21  import java.lang.reflect.Type;
22  import java.util.ArrayList;
23  import java.util.HashMap;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.StringTokenizer;
27  
28  import javax.xml.bind.JAXBContext;
29  import javax.xml.bind.JAXBElement;
30  import javax.xml.bind.Unmarshaller;
31  import javax.xml.bind.annotation.XmlAccessorType;
32  import javax.xml.namespace.QName;
33  import javax.xml.stream.XMLStreamConstants;
34  import javax.xml.stream.XMLStreamException;
35  import javax.xml.stream.XMLStreamReader;
36  import javax.xml.xpath.XPathConstants;
37  
38  import org.apache.cxf.common.util.StringUtils;
39  import org.apache.cxf.databinding.DataBinding;
40  import org.apache.cxf.databinding.DataReader;
41  import org.apache.cxf.helpers.CastUtils;
42  import org.apache.cxf.helpers.DOMUtils;
43  import org.apache.cxf.helpers.XPathUtils;
44  import org.apache.cxf.interceptor.Fault;
45  import org.apache.cxf.jaxb.JAXBDataBinding;
46  import org.apache.cxf.message.Message;
47  import org.apache.cxf.phase.AbstractPhaseInterceptor;
48  import org.apache.cxf.phase.Phase;
49  import org.apache.cxf.service.Service;
50  import org.apache.cxf.service.model.BindingOperationInfo;
51  import org.apache.cxf.service.model.FaultInfo;
52  import org.apache.cxf.service.model.MessagePartInfo;
53  import org.apache.cxf.staxutils.StaxUtils;
54  import org.w3c.dom.Element;
55  import org.w3c.dom.Node;
56  
57  /**
58   * Intercaptor that converts the XML to Exceptions. Taken from the CXF
59   * ClientFaultConverter class (fied some jaxb issue with our exceptions).
60   * 
61   * @author marco.
62   */
63  @SuppressWarnings("unchecked")
64  public class Jbi4CorbaConsumerExceptionInterceptor extends
65  		AbstractPhaseInterceptor<Message> {
66  
67  	/**
68  	 * Logger.
69  	 */
70  	private static final transient Logger LOG = LoggerFactory
71  			.getLogger(Jbi4CorbaConsumerExceptionInterceptor.class);
72  
73      /** The messages. */
74      @SuppressWarnings("unused")
75  	private static final Messages MESSAGES = 
76      	Messages.getMessages(Jbi4CorbaConsumerExceptionInterceptor.class);
77  
78  	public Jbi4CorbaConsumerExceptionInterceptor() {
79  		super(Phase.UNMARSHAL);
80  	}
81  
82  	public Jbi4CorbaConsumerExceptionInterceptor(String phase) {
83  		super(phase);
84  	}
85  
86  	public void handleMessage(Message msg) {
87  		Fault fault = (Fault) msg.getContent(Exception.class);
88  
89  		if (fault.getDetail() != null) {
90  			processFaultDetail(fault, msg);
91  			setStackTrace(fault, msg);
92  		}
93  	}
94  
95  	protected void processFaultDetail(Fault fault, Message msg) {
96  		Element exDetail = (Element) DOMUtils.getChild(fault.getDetail(),
97  				Node.ELEMENT_NODE);
98  		if (exDetail == null) {
99  			return;
100 		}
101 		QName qname = new QName(exDetail.getNamespaceURI(), exDetail
102 				.getLocalName());
103 		FaultInfo faultWanted = null;
104 		MessagePartInfo part = null;
105 		BindingOperationInfo boi = msg.getExchange().get(
106 				BindingOperationInfo.class);
107 		if (boi == null) {
108 			return;
109 		}
110 		if (boi.isUnwrapped()) {
111 			boi = boi.getWrappedOperation();
112 		}
113 		for (FaultInfo faultInfo : boi.getOperationInfo().getFaults()) {
114 			for (MessagePartInfo mpi : faultInfo.getMessageParts()) {
115 				if (qname.equals(mpi.getConcreteName())) {
116 					faultWanted = faultInfo;
117 					part = mpi;
118 					break;
119 				}
120 			}
121 			if (faultWanted != null) {
122 				break;
123 			}
124 		}
125 		if (faultWanted == null) {
126 			// did not find it using the proper qualified names, we'll try again
127 			// with just the localpart
128 			for (FaultInfo faultInfo : boi.getOperationInfo().getFaults()) {
129 				for (MessagePartInfo mpi : faultInfo.getMessageParts()) {
130 					if (qname.getLocalPart().equals(
131 							mpi.getConcreteName().getLocalPart())) {
132 						faultWanted = faultInfo;
133 						part = mpi;
134 						break;
135 					}
136 				}
137 				if (faultWanted != null) {
138 					break;
139 				}
140 			}
141 		}
142 		if (faultWanted == null) {
143 			return;
144 		}
145 		Service s = msg.getExchange().get(Service.class);
146 		DataBinding dataBinding = s.getDataBinding();
147 
148 		Object e = null;
149 		if (isDOMSupported(dataBinding)) {
150 			DataReader<Node> reader = dataBinding.createReader(Node.class);
151 			reader.setProperty(DataReader.FAULT, fault);
152 			JAXBContext context = ((JAXBDataBinding) dataBinding).getContext();
153 			try {
154 				Unmarshaller unmarshaller = context.createUnmarshaller();
155 				e = unmarshallException(unmarshaller, exDetail, part);
156 				
157 			} catch (Exception ex) {
158 				if (ex instanceof javax.xml.bind.UnmarshalException) {
159 					javax.xml.bind.UnmarshalException unmarshalEx = (javax.xml.bind.UnmarshalException) ex;
160 					throw new Fault(unmarshalEx);
161 				} else {
162 					throw new Fault(ex);
163 				}
164 			}
165 		}
166 
167 		LOG.debug("Returned object of class:" + e.getClass().getName());
168 		if (!(e instanceof Exception)) {
169 
170 			try {
171 				Class<?> exClass = faultWanted.getProperty(Class.class
172 						.getName(), Class.class);
173 				LOG.debug("ExClass exClass:" + exClass.getName());				
174 				if (e == null) {
175 					Constructor constructor = exClass
176 							.getConstructor(new Class[] { String.class });
177 					e = constructor.newInstance(new Object[] { fault
178 							.getMessage() });
179 				} else {				    
180 					Constructor constructor = getConstructor(exClass, e);
181 					e = constructor.newInstance(new Object[] {
182 							fault.getMessage(), e });
183 					LOG.debug("After newInstance");  
184 				}
185 				msg.setContent(Exception.class, e);
186 			} catch (Throwable e1) {
187 			    e1.printStackTrace();
188 				String errmsg = MESSAGES.getString("CRB000901_Exception_while_creating_exception");
189 				LOG.debug(errmsg, e1.getMessage());
190 			}
191 		} else if (e != null) {
192 			if (fault.getMessage() != null) {
193 				Field f;
194 				try {
195 					f = Throwable.class.getDeclaredField("detailMessage");
196 					f.setAccessible(true);
197 					f.set(e, fault.getMessage());
198 				} catch (Exception e1) {
199 					String errmsg = MESSAGES.getString("CRB000902_Exception_in_fault_processing");
200 					LOG.debug(errmsg, e1.getMessage());
201 				}
202 			}
203 			msg.setContent(Exception.class, e);
204 		}
205 	}
206 
207 	private Constructor getConstructor(Class<?> faultClass, Object e)
208 			throws NoSuchMethodException {
209 		Class<?> beanClass = e.getClass();
210 		Constructor cons[] = faultClass.getConstructors();
211 		for (Constructor c : cons) {
212 			if (c.getParameterTypes().length == 2
213 					&& String.class.equals(c.getParameterTypes()[0])
214 					&& c.getParameterTypes()[1].isInstance(e)) {
215 			    LOG.debug("Found constructor: " + c);
216 			    LOG.debug("The second parameter type is: " + c.getParameterTypes()[1]);			    
217 				return c;
218 			}
219 		}
220 		try {
221 		    LOG.debug("Found constructor directly");
222 			return faultClass.getConstructor(new Class[] { String.class,
223 					beanClass });
224 		} catch (NoSuchMethodException ex) {
225 		    ex.printStackTrace();
226 			Class<?> cls = getPrimitiveClass(beanClass);
227 			if (cls != null) {
228 			    LOG.debug("Uses the primitive class:  " + cls);
229 				return faultClass.getConstructor(new Class[] { String.class,
230 						cls });
231 			} else {
232 				throw ex;
233 			}
234 		}
235 
236 	}
237 
238 	private boolean isDOMSupported(DataBinding db) {
239 		boolean supportsDOM = false;
240 		for (Class c : db.getSupportedReaderFormats()) {
241 			if (c.equals(Node.class)) {
242 				supportsDOM = true;
243 			}
244 		}
245 		return supportsDOM;
246 	}
247 
248 	private void setStackTrace(Fault fault, Message msg) {
249 		Map<String, String> ns = new HashMap<String, String>();
250 		XPathUtils xu = new XPathUtils(ns);
251 		String ss = (String) xu.getValue("/" + Fault.STACKTRACE + "/text()",
252 				fault.getDetail(), XPathConstants.STRING);
253 		List<StackTraceElement> stackTraceList = new ArrayList<StackTraceElement>();
254 		if (!StringUtils.isEmpty(ss)) {
255 			StringTokenizer st = new StringTokenizer(ss, "\n");
256 			while (st.hasMoreTokens()) {
257 				String oneLine = st.nextToken();
258 				StringTokenizer stInner = new StringTokenizer(oneLine, "!");
259 				StackTraceElement ste = new StackTraceElement(stInner
260 						.nextToken(), stInner.nextToken(), stInner.nextToken(),
261 						Integer.parseInt(stInner.nextToken()));
262 				stackTraceList.add(ste);
263 			}
264 			if (stackTraceList.size() > 0) {
265 				StackTraceElement[] stackTraceElement = new StackTraceElement[stackTraceList
266 						.size()];
267 				Exception e = msg.getContent(Exception.class);
268 				e.setStackTrace(stackTraceList.toArray(stackTraceElement));
269 			}
270 		}
271 
272 	}
273 
274 	private Class<?> getPrimitiveClass(Class<?> cls) {
275 		if (cls.isPrimitive()) {
276 			return cls;
277 		}
278 		try {
279 			Field field = cls.getField("TYPE");
280 			Object obj = (Object) cls;
281 			Object type = field.get(obj);
282 			if (type instanceof Class) {
283 				return (Class) type;
284 			}
285 		} catch (Exception e) {
286 			String errmsg = MESSAGES.getString("CRB000902_Exception_in_fault_processing");
287 			LOG.error(errmsg, e.getMessage());
288 		}
289 		return null;
290 	}
291 
292 	/**
293 	 * Unmarshall the exception (from XML to Java).
294 	 * @param u
295 	 * @param source
296 	 * @param part
297 	 * @return
298 	 */
299 	public static Object unmarshallException(Unmarshaller u, Object source,
300 			MessagePartInfo part) {
301 		XMLStreamReader reader = null;
302 		Object obj = null;
303 		try {
304 			if (source instanceof XMLStreamReader) {
305 				reader = (XMLStreamReader) source;
306 			} else if (source instanceof Element) {
307 				reader = StaxUtils.createXMLStreamReader((Element) source);
308 				try {
309 					// advance into the node
310 					reader.nextTag();
311 				} catch (XMLStreamException e) {
312 					String errmsg = MESSAGES.getString("CRB000903_Exception_in_exception_unmarshalling");
313 					LOG.error(errmsg, e.getMessage());
314 				}
315 			} else {
316 				String errmsg = MESSAGES.getString("CRB000904_Wrong_source_type_in_Exception_unmarshalling");
317 				LOG.error(errmsg);
318 			}
319 
320 			QName qn = part.getElementQName();
321 			if (!qn.equals(reader.getName())) {
322 				String errmsg = MESSAGES.getString("CRB000905_Element_name_mismatch_in_Exception_unmarshalling");
323 				LOG.error(errmsg);
324 			}
325 
326 			Class<?> cls = part.getTypeClass();
327 
328 			try {
329 				Constructor cons = cls.getConstructor();
330 				obj = cons.newInstance();
331 			} catch (NoSuchMethodException nse) {
332 				Constructor cons = cls
333 						.getConstructor(new Class[] { String.class });
334 				obj = cons.newInstance(new Object[1]);
335 			}
336 
337 			XmlAccessorType accessorType = cls
338 					.getAnnotation(XmlAccessorType.class);
339 			if (accessorType == null && cls.getPackage() != null) {
340 				accessorType = cls.getPackage().getAnnotation(
341 						XmlAccessorType.class);
342 			}
343 			//XmlAccessType accessType = accessorType != null ? accessorType
344 			//		.value() : XmlAccessType.PUBLIC_MEMBER;
345 			reader.nextTag();
346 			while (reader.getEventType() == XMLStreamReader.START_ELEMENT) {
347 				QName q = reader.getName();
348 
349 				String s = Character.toUpperCase(q.getLocalPart().charAt(0))
350 						+ q.getLocalPart().substring(1);
351 				Method m = null;
352 				try {
353 					m = cls.getMethod("get" + s);
354 				} catch (NoSuchMethodException mex) {
355 					m = cls.getMethod("is" + s);
356 				}
357 				Type type = m.getGenericReturnType();
358 				Method m2 = cls.getMethod("set" + s, m.getReturnType());
359 				if (isArray(type)) {
360 					Class<?> compType = getArrayComponentType(type);
361 					List<Object> ret = unmarshallArray(u, reader, q, compType,
362 							createList(type));
363 					Object o = ret;
364 					if (!isList(type)) {
365 						if (compType.isPrimitive()) {
366 							o = java.lang.reflect.Array.newInstance(compType,
367 									ret.size());
368 							for (int x = 0; x < ret.size(); x++) {
369 								Array.set(o, x, ret.get(x));
370 							}
371 						} else {
372 							o = ret.toArray((Object[]) Array.newInstance(
373 									compType, ret.size()));
374 						}
375 					}
376 
377 					m2.invoke(obj, o);
378 				} else {
379 					Object o = getElementValue(u.unmarshal(reader, m
380 							.getReturnType()));
381 					m2.invoke(obj, o);
382 				}
383 			}
384 		} catch (Exception ex) {
385 			ex.printStackTrace();
386 		}
387 		return obj;
388 
389 	}
390 
391 	static boolean isArray(Type cls) {
392 		if (cls instanceof Class) {
393 			return ((Class) cls).isArray();
394 		} else if (cls instanceof ParameterizedType) {
395 			return true;
396 		} else if (cls instanceof GenericArrayType) {
397 			return true;
398 		}
399 		return false;
400 	}
401 
402 	static Class<?> getArrayComponentType(Type cls) {
403 		if (cls instanceof Class) {
404 			if (((Class) cls).isArray()) {
405 				return ((Class) cls).getComponentType();
406 			} else {
407 				return (Class) cls;
408 			}
409 		} else if (cls instanceof ParameterizedType) {
410 			for (Type t2 : ((ParameterizedType) cls).getActualTypeArguments()) {
411 				return getArrayComponentType(t2);
412 			}
413 		} else if (cls instanceof GenericArrayType) {
414 			GenericArrayType gt = (GenericArrayType) cls;
415 			Class ct = (Class) gt.getGenericComponentType();
416 			return Array.newInstance(ct, 0).getClass();
417 		}
418 		return null;
419 	}
420 
421 	private static List<Object> createList(Type genericType) {
422 		if (genericType instanceof ParameterizedType) {
423 			Type tp2 = ((ParameterizedType) genericType).getRawType();
424 			if (tp2 instanceof Class) {
425 				Class<?> cls = (Class) tp2;
426 				if (!cls.isInterface()
427 						&& List.class.isAssignableFrom((Class<?>) cls)) {
428 					try {
429 						return CastUtils.cast((List) cls.newInstance());
430 					} catch (Exception e) {
431 					    // ignore, just return an ArrayList
432 	    				String errmsg = MESSAGES.getString("CRB000906_Exception_in_list_exception_unmarshalling");
433 	    				LOG.warn(errmsg, e.getMessage());
434 					}
435 				}
436 			}
437 		}
438 		return new ArrayList<Object>();
439 	}
440 
441 	public static List<Object> unmarshallArray(Unmarshaller u, Object source,
442 			QName elName, Class<?> clazz, List<Object> ret) throws Exception {
443 		try {
444 			XMLStreamReader reader = null;
445 			if (source instanceof XMLStreamReader) {
446 				reader = (XMLStreamReader) source;
447 			} else if (source instanceof Element) {
448 				reader = StaxUtils.createXMLStreamReader((Element) source);
449 			} else {
450 				// empty...
451 				String errmsg = MESSAGES.getString("CRB000907_Should_be_XMLStreamReader_or_Element");
452 				LOG.error(errmsg);
453 			}
454 			while (reader.getName().equals(elName)) {
455 				Object obj = u.unmarshal(reader, clazz);
456 				if (obj instanceof JAXBElement) {
457 					obj = ((JAXBElement) obj).getValue();
458 				}
459 				ret.add(obj);
460 				while (reader.getEventType() != XMLStreamConstants.START_ELEMENT
461 						&& reader.getEventType() != XMLStreamConstants.END_ELEMENT) {
462 					reader.nextTag();
463 				}
464 			}
465 			return ret;
466 		} catch (Fault ex) {
467 			ex.fillInStackTrace();
468 			throw ex;
469 		} catch (Exception ex) {
470 			if (ex instanceof javax.xml.bind.UnmarshalException) {
471 				javax.xml.bind.UnmarshalException unmarshalEx = (javax.xml.bind.UnmarshalException) ex;
472 				throw new Fault(unmarshalEx);
473 
474 			} else {
475 				throw new Fault(ex);
476 			}
477 		}
478 	}
479 
480 	private static boolean isList(Type cls) {
481 		if (cls instanceof ParameterizedType) {
482 			return true;
483 		}
484 		return false;
485 	}
486 
487 	public static Object getElementValue(Object obj) {
488 		if (null == obj) {
489 			return null;
490 		}
491 
492 		if (obj instanceof JAXBElement) {
493 			return ((JAXBElement<?>) obj).getValue();
494 		}
495 		return obj;
496 	}
497 
498 }