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: */
019:
020: package org.apache.axis2.jaxws.marshaller.impl.alt;
021:
022: import org.apache.axis2.jaxws.ExceptionFactory;
023: import org.apache.axis2.jaxws.description.FaultDescription;
024: import org.apache.axis2.jaxws.i18n.Messages;
025: import org.apache.axis2.jaxws.runtime.description.marshal.FaultBeanDesc;
026: import org.apache.axis2.jaxws.runtime.description.marshal.MarshalServiceRuntimeDescription;
027: import org.apache.axis2.jaxws.utility.PropertyDescriptorPlus;
028: import org.apache.axis2.jaxws.wrapper.JAXBWrapperTool;
029: import org.apache.axis2.jaxws.wrapper.impl.JAXBWrapperToolImpl;
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032:
033: import javax.xml.ws.WebServiceException;
034: import java.beans.IntrospectionException;
035: import java.lang.reflect.Constructor;
036: import java.lang.reflect.InvocationTargetException;
037: import java.util.ArrayList;
038: import java.util.HashMap;
039: import java.util.HashSet;
040: import java.util.Iterator;
041: import java.util.List;
042: import java.util.Map;
043: import java.util.Map.Entry;
044: import java.util.Set;
045:
046: /**
047: * The JAX-WS Specification (chapter 3.7) indicates that JAX-WS
048: * supports exceptions that do not match the normal pattern (the normal
049: * pattern is defined in chapter 2.5.
050: *
051: * These non-matching exceptions are the result of running WSGen
052: * on a pre-existing webservice. I am going to use the term, legacy exception,
053: * to describe these non-matching exceptions.
054: *
055: * The JAX-WS marshaller (server) must marshal a legacy exception thrown from
056: * the web service impl. The marshalling/mapping algorithm is defined in chapter 3.7.
057: *
058: * On the client, the JAX-WS engine will need to demarshal exceptions. However
059: * the specification assumes that the client is always created via wsimport; therefore
060: * the assumption is that all exceptions on the client are compliant (never legacy exceptions).
061: * I have included some code here in case we have to deal with legacy exceptions on the client...this is non-spec.
062: *
063: *
064: */
065:
066: /** @author scheu */
067: class LegacyExceptionUtil {
068:
069: private static Log log = LogFactory
070: .getLog(LegacyExceptionUtil.class);
071:
072: private static Set<String> ignore = new HashSet<String>();
073:
074: static {
075: // Per Chap 3.7 rule 3, ignore these properties on the exception
076: ignore.add("localizedMessage");
077: ignore.add("stackTrace");
078: ignore.add("class");
079: ignore.add("cause");
080: }
081:
082: /** Static class. Constructor is intentionally private */
083: private LegacyExceptionUtil() {
084: }
085:
086: /**
087: * Create a FaultBean populated with the data from the Exception t The algorithm used to
088: * populate the FaultBean is described in JAX-WS 3.7
089: *
090: * @param t
091: * @param fd
092: * @param marshalDesc
093: * @return faultBean
094: */
095: static Object createFaultBean(Throwable t, FaultDescription fd,
096: MarshalServiceRuntimeDescription marshalDesc)
097: throws WebServiceException {
098:
099: Object faultBean = null;
100: try {
101: // Get the fault bean name from the fault description.
102: // REVIEW The default name should be:
103: // Package = <SEI package> or <SEI package>.jaxws
104: // Name = <exception name> + Bean
105: FaultBeanDesc faultBeanDesc = marshalDesc
106: .getFaultBeanDesc(fd);
107: String faultBeanName = faultBeanDesc
108: .getFaultBeanClassName();
109:
110: // TODO Add check that faultBeanName is correct
111: if (log.isDebugEnabled()) {
112: log.debug("Legacy Exception FaultBean name is = "
113: + faultBeanName);
114: }
115:
116: // Load the FaultBean Class
117: Class faultBeanClass = null;
118: if (faultBeanName != null && faultBeanName.length() > 0) {
119: try {
120: faultBeanClass = MethodMarshallerUtils
121: .loadClass(faultBeanName);
122: } catch (Throwable throwable) {
123: if (log.isDebugEnabled()) {
124: log
125: .debug("Cannot load fault bean class = "
126: + faultBeanName
127: + ". Fallback to using the exception object");
128: }
129: }
130: }
131:
132: if (faultBeanClass != null) {
133:
134: // Get the properties names from the exception class
135: Map<String, PropertyDescriptorPlus> pdMap = marshalDesc
136: .getPropertyDescriptorMap(t.getClass());
137:
138: // We need to assign the legacy exception data to the java bean class.
139: // We will use the JAXBWrapperTool.wrap utility to do this.
140:
141: // Get the map of child objects
142: Map<String, Object> childObjects = getChildObjectsMap(
143: t, pdMap);
144:
145: List<String> childNames = new ArrayList<String>(
146: childObjects.keySet());
147:
148: if (log.isErrorEnabled()) {
149: log
150: .debug("List of properties on the Legacy Exception is "
151: + childNames);
152: }
153: // Use the wrapper tool to get the child objects.
154: JAXBWrapperTool wrapperTool = new JAXBWrapperToolImpl();
155: Map<String, PropertyDescriptorPlus> pdMapForBean = marshalDesc
156: .getPropertyDescriptorMap(faultBeanClass);
157: faultBean = wrapperTool.wrap(faultBeanClass,
158: childNames, childObjects, pdMapForBean);
159: if (log.isErrorEnabled()) {
160: log.debug("Completed creation of the fault bean.");
161: }
162: } else {
163: //if (log.isErrorEnabled()) {
164: // log.debug("The fault bean could not be loaded...Fallback to using the fault exception: " + t.getClass());
165: //}
166: //return t;
167: throw ExceptionFactory.makeWebServiceException(Messages
168: .getMessage("faultProcessingNotSupported",
169: "the @WebFault faultbean is missing for "
170: + t.getClass()));
171: }
172:
173: } catch (Exception e) {
174: throw ExceptionFactory.makeWebServiceException(e);
175: }
176: return faultBean;
177: }
178:
179: /**
180: * Create an Exception using the data in the JAXB object. The specification is silent on this
181: * issue.
182: *
183: * @param exceptionClass
184: * @param jaxb
185: * @param marshalDesc
186: * @return
187: */
188: static Exception createFaultException(Class exceptionClass,
189: Object jaxb, MarshalServiceRuntimeDescription marshalDesc) {
190: Exception e = null;
191: try {
192: if (log.isErrorEnabled()) {
193: log.debug("Create Legacy Exception for "
194: + exceptionClass.getName());
195: }
196: // Get the properties names from the exception class
197: Map<String, PropertyDescriptorPlus> pdMap = marshalDesc
198: .getPropertyDescriptorMap(exceptionClass);
199:
200: // Now get a list of PropertyDescriptorPlus objects that map to the jaxb bean properties
201: Iterator<Entry<String, PropertyDescriptorPlus>> it = pdMap
202: .entrySet().iterator();
203: List<PropertyDescriptorPlus> piList = new ArrayList<PropertyDescriptorPlus>();
204: while (it.hasNext()) {
205: Entry<String, PropertyDescriptorPlus> entry = it.next();
206: String propertyName = entry.getValue()
207: .getPropertyName();
208: // Some propertyNames should be ignored.
209: if (!ignore.contains(propertyName)) {
210: piList.add(entry.getValue());
211: }
212: }
213:
214: // Find a matching constructor
215: List<String> childNames = new ArrayList<String>();
216: if (log.isErrorEnabled()) {
217: log.debug("List of childNames on legacy exception is "
218: + childNames);
219: }
220: Constructor constructor = findConstructor(exceptionClass,
221: piList, childNames);
222:
223: if (log.isErrorEnabled()) {
224: log
225: .debug("The constructor used to create the exception is "
226: + constructor);
227: }
228: // Use the wrapper tool to unwrap the jaxb object
229: JAXBWrapperTool wrapperTool = new JAXBWrapperToolImpl();
230: Map<String, PropertyDescriptorPlus> pdMapForBean = marshalDesc
231: .getPropertyDescriptorMap(jaxb.getClass());
232: Object[] childObjects = wrapperTool.unWrap(jaxb,
233: childNames, pdMapForBean);
234:
235: if (log.isErrorEnabled()) {
236: log.debug("Calling newInstance on the constructor "
237: + constructor);
238: }
239: e = (Exception) constructor.newInstance(childObjects);
240: } catch (Exception ex) {
241: throw ExceptionFactory.makeWebServiceException(ex);
242: }
243: return e;
244: }
245:
246: /**
247: * Find a construcor that matches this set of properties
248: *
249: * @param cls
250: * @param pdList
251: * @param childNames returned in the order that they occur in the constructor
252: * @return Constructor or null
253: */
254: private static Constructor findConstructor(Class cls,
255: List<PropertyDescriptorPlus> pdList, List<String> childNames) {
256: Constructor[] constructors = cls.getConstructors();
257: Constructor constructor = null;
258: if (constructors != null) {
259: for (int i = 0; i < constructors.length
260: && constructor == null; i++) {
261: Constructor tryConstructor = constructors[i];
262: if (tryConstructor.getParameterTypes().length == pdList
263: .size()) {
264: // Try and find the best match using the property types
265: List<PropertyDescriptorPlus> list = new ArrayList<PropertyDescriptorPlus>(
266: pdList);
267: List<PropertyDescriptorPlus> args = new ArrayList<PropertyDescriptorPlus>();
268:
269: Class[] parms = tryConstructor.getParameterTypes();
270: boolean valid = true;
271:
272: // Assume the message is first in the constructor
273: for (int j = 0; j < list.size(); j++) {
274: if ("message".equals(list.get(j)
275: .getPropertyName())) {
276: args.add(list.remove(j));
277: }
278: }
279: if (args.size() != 1
280: || !parms[0].isAssignableFrom(args.get(0)
281: .getPropertyType())) {
282: valid = false;
283: }
284:
285: // Now process the rest of the args
286: for (int j = 1; j < parms.length && valid; j++) {
287: // Find a compatible argument
288: Class parm = parms[j];
289: boolean found = false;
290: for (int k = 0; k < list.size() && !found; k++) {
291: Class arg = list.get(k).getPropertyType();
292: if (parm.isAssignableFrom(arg)) {
293: found = true;
294: args.add(list.remove(k));
295: }
296: }
297: // If no compatible argument then this constructor is not valid
298: if (!found) {
299: valid = false;
300: }
301: }
302: // A constructor is found
303: if (valid) {
304: constructor = tryConstructor;
305: for (int index = 0; index < args.size(); index++) {
306: childNames.add(args.get(index)
307: .getPropertyName());
308: }
309: }
310: }
311: }
312: }
313:
314: return constructor;
315: }
316:
317: /**
318: * Get the child objects map that is required by the wrapper tool.
319: *
320: * @param t Exception
321: * @param ParameterDescriptorPlus map for Throwable t
322: * @return Map with key is bean property names and values are objects from the Exception
323: * @throws IntrospectionException
324: */
325: private static Map<String, Object> getChildObjectsMap(Throwable t,
326: Map<String, PropertyDescriptorPlus> pdMap)
327: throws IntrospectionException, InvocationTargetException,
328: IllegalAccessException {
329:
330: Map<String, Object> coMap = new HashMap<String, Object>();
331:
332: Iterator<Entry<String, PropertyDescriptorPlus>> it = pdMap
333: .entrySet().iterator();
334: while (it.hasNext()) {
335: Entry<String, PropertyDescriptorPlus> entry = it.next();
336: String propertyName = entry.getValue().getPropertyName();
337: String xmlName = entry.getKey();
338: // Some propertyNames should be ignored.
339: if (!ignore.contains(propertyName)) {
340: Object value = entry.getValue().get(t);
341: coMap.put(xmlName, value);
342: }
343: }
344: return coMap;
345: }
346:
347: }
|