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;
021:
022: import org.apache.axis2.AxisFault;
023: import org.apache.axis2.jaxws.i18n.Messages;
024: import org.apache.commons.logging.Log;
025: import org.apache.commons.logging.LogFactory;
026:
027: import javax.xml.ws.ProtocolException;
028: import javax.xml.ws.WebServiceException;
029: import java.io.ByteArrayOutputStream;
030: import java.io.PrintStream;
031: import java.lang.reflect.InvocationTargetException;
032:
033: /**
034: * ExceptionFactory is used to create exceptions within the JAX-WS implementation. There are several
035: * reasons for using a factory to create exceptions. 1. We can intercept all exception creation and
036: * add the appropriate logging/serviceability. 2. Exceptions are chained. ExceptionFactory can
037: * lengthen or reduce the cause chains as necessary to support the JAX-WS programming model. 3.
038: * Prevents construction of the same exception. Uses similar principles as
039: * AxisFault.makeException.
040: * <p/>
041: * Example Usage: // Example usage
042: * <p/>
043: * public fooMethod() throws WebServiceException { try{ ... } catch(Exception e){ throw
044: * ExceptionFactory.makeWebServiceException(e); } }
045: */
046: public class ExceptionFactory {
047:
048: protected static Log log = LogFactory.getLog(ExceptionFactory.class
049: .getName());
050:
051: /** Private Constructor All methods are static. The private constructor prevents instantiation. */
052: private ExceptionFactory() {
053: }
054:
055: /**
056: * Create a WebServiceException using the information from a given Throwable instance and message
057: *
058: * @param message
059: * @param throwable
060: * @return WebServiceException
061: */
062: public static WebServiceException makeWebServiceException(
063: String message, Throwable throwable) {
064: try {
065: // See if there is already a WebServiceException (Note that the returned exception could be a ProtocolException or
066: // other kind of exception)
067: WebServiceException e = (WebServiceException) findException(
068: throwable, WebServiceException.class);
069: if (e == null) {
070: e = createWebServiceException(message, throwable);
071: }
072: return e;
073: } catch (RuntimeException re) {
074: // TODO
075: // This is not a good situation, an exception occurred while building the exception.
076: // This should never occur! For now log the problem and rethrow...we may revisit this later
077: if (log.isDebugEnabled()) {
078: log
079: .debug(
080: Messages
081: .getMessage("exceptionDuringExceptionFlow"),
082: re);
083: }
084: throw re;
085: }
086: }
087:
088: /**
089: * Create a ProtocolException using the information from a Throwable and message
090: *
091: * @param message
092: * @param throwable
093: * @return ProtocolException
094: */
095: public static ProtocolException makeProtocolException(
096: String message, Throwable throwable) {
097: try {
098: // See if there is already a ProtocolException
099: ProtocolException e = (ProtocolException) findException(
100: throwable, ProtocolException.class);
101: if (e == null) {
102: e = createProtocolException(message, throwable);
103: }
104: return e;
105: } catch (RuntimeException re) {
106: // TODO
107: // This is not a good situation, an exception occurred while building the exception.
108: // This should never occur! For now log the problem and rethrow...we may revisit this later
109: if (log.isDebugEnabled()) {
110: log
111: .debug(
112: Messages
113: .getMessage("exceptionDuringExceptionFlow"),
114: re);
115: }
116: throw re;
117: }
118: }
119:
120: /**
121: * Make a WebServiceException with a given message
122: *
123: * @param message
124: * @return WebServiceException
125: */
126: public static WebServiceException makeWebServiceException(
127: String message) {
128: return makeWebServiceException(message, null);
129: }
130:
131: /**
132: * Create a WebServiceException using the information from a given Throwable instance
133: *
134: * @param throwable
135: * @return WebServiceException
136: */
137: public static WebServiceException makeWebServiceException(
138: Throwable throwable) {
139: return makeWebServiceException(null, throwable);
140: }
141:
142: /**
143: * Create a WebServiceException
144: *
145: * @param message
146: * @param t Throwable
147: * @return WebServiceException
148: */
149: private static WebServiceException createWebServiceException(
150: String message, Throwable t) {
151:
152: // We might have an embedded WebServiceException that has a good message on it
153: WebServiceException me = (WebServiceException) findException(t,
154: WebServiceException.class);
155: if (me != null) {
156: String meMessage = me.getMessage();
157: if (meMessage != null) {
158: if (message == null) {
159: message = meMessage;
160: } else {
161: message = message + ": " + meMessage;
162: }
163: }
164: }
165:
166: // Get the root cause. We want to strip out the intermediate exceptions (like AxisFault) because
167: // these won't make sense to the user.
168: Throwable rootCause = null;
169: if (t != null) {
170: rootCause = getRootCause(t);
171: }
172: rootCause = rootCause == null ? t : rootCause;
173: WebServiceException e = null;
174:
175: // The root cause may not have a good message. We might want to enhance it
176: String enhancedMessage = enhanceMessage(rootCause);
177: if (enhancedMessage != null) {
178: if (message != null)
179: message = message + ": " + enhancedMessage;
180: else
181: message = enhancedMessage;
182: }
183:
184: if (message != null) {
185: e = new WebServiceException(message, rootCause);
186: } else {
187: e = new WebServiceException(rootCause);
188: }
189:
190: if (log.isDebugEnabled()) {
191: log.debug("Create Exception:", e);
192: }
193: return e;
194: }
195:
196: /**
197: * Create a ProtocolException
198: *
199: * @param message
200: * @param t Throwable
201: * @return ProtocolException
202: */
203: private static ProtocolException createProtocolException(
204: String message, Throwable t) {
205: Throwable rootCause = null;
206: if (t != null) {
207: rootCause = getRootCause(t);
208: }
209: rootCause = rootCause == null ? t : rootCause;
210: ProtocolException e = null;
211: if (message != null) {
212: e = new ProtocolException(message, rootCause);
213: } else {
214: e = new ProtocolException(rootCause);
215: }
216: if (log.isDebugEnabled()) {
217: log.debug("create Exception:", e);
218: }
219: return e;
220: }
221:
222: /**
223: * Return the exception or nested cause that is assignable from the specified class
224: *
225: * @param t Throwable
226: * @param cls
227: * @return Exception or null
228: */
229: private static Exception findException(Throwable t, Class cls) {
230: while (t != null) {
231: if (cls.isAssignableFrom(t.getClass())) {
232: return (Exception) t;
233: }
234: t = getCause(t);
235: }
236: return null;
237: }
238:
239: /**
240: * Gets the Throwable cause of the Exception. Some exceptions store the cause in a different
241: * field, which is why this method should be used when walking the causes.
242: *
243: * @param t Throwable
244: * @return Throwable or null
245: */
246: private static Throwable getCause(Throwable t) {
247: Throwable cause = null;
248:
249: // Look for a specific cause for this kind of exception
250: if (t instanceof InvocationTargetException) {
251: cause = ((InvocationTargetException) t)
252: .getTargetException();
253: }
254:
255: // If no specific cause, fall back to the general cause.
256: if (cause == null) {
257: cause = t.getCause();
258: }
259: return cause;
260: }
261:
262: /**
263: * This method searches the causes of the specified throwable until it finds one that is
264: * acceptable as a "root" cause.
265: * <p/>
266: * Example: If t is an AxisFault, the code traverses to the next cause.
267: *
268: * @param t Throwable
269: * @return Throwable root cause
270: */
271: private static Throwable getRootCause(Throwable t) {
272: while (t != null) {
273: Throwable nextCause = null;
274: if (t instanceof InvocationTargetException
275: || t instanceof AxisFault) {
276: // Skip over this cause
277: nextCause = getCause(t);
278: if (nextCause == null) {
279: logRootCause(t);
280: return t;
281: }
282: t = nextCause;
283: } else {
284: // This is the root cause
285: logRootCause(t);
286: return t;
287: }
288: }
289: logRootCause(t);
290: return t;
291: }
292:
293: /**
294: * Other developers may add additional criteria to give better error messages back to the user.
295: *
296: * @param t Throwable
297: * @return String a message that helps the user understand what caused the exception
298: */
299: private static String enhanceMessage(Throwable t) {
300: if (t == null)
301: return null;
302:
303: ByteArrayOutputStream baos = new ByteArrayOutputStream();
304: PrintStream ps = new PrintStream(baos, true);
305: t.printStackTrace(ps);
306: String stackTrace = baos.toString();
307:
308: // TODO better criteria
309: if ((t instanceof StackOverflowError)
310: && (stackTrace.contains("JAXB")))
311: return Messages.getMessage("JABGraphProblem");
312:
313: return null;
314: }
315:
316: /**
317: * Log the root cause
318: * @param t
319: */
320: private static void logRootCause(Throwable t) {
321: // TODO Should certain throwables be logged elsewhere
322: if (log.isDebugEnabled()) {
323: log.debug("Root Cause:" + t.toString());
324: log.debug("stack:" + stackToString(t));
325: }
326: }
327:
328: /**
329: * Get a string containing the stack of the specified exception
330: * @param e
331: * @return
332: */
333: public static String stackToString(Throwable e) {
334: java.io.StringWriter sw = new java.io.StringWriter();
335: java.io.BufferedWriter bw = new java.io.BufferedWriter(sw);
336: java.io.PrintWriter pw = new java.io.PrintWriter(bw);
337: e.printStackTrace(pw);
338: pw.close();
339: return sw.getBuffer().toString();
340: }
341:
342: }
|