| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.apache.axis2.jaxws; |
| |
| import org.apache.axiom.om.OMException; |
| import org.apache.axis2.AxisFault; |
| import org.apache.axis2.jaxws.i18n.Messages; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| |
| import javax.xml.ws.ProtocolException; |
| import javax.xml.ws.WebServiceException; |
| import java.io.ByteArrayOutputStream; |
| import java.io.PrintStream; |
| import java.lang.reflect.InvocationTargetException; |
| |
| /** |
| * ExceptionFactory is used to create exceptions within the JAX-WS implementation. There are several |
| * reasons for using a factory to create exceptions. 1. We can intercept all exception creation and |
| * add the appropriate logging/serviceability. 2. Exceptions are chained. ExceptionFactory can |
| * lengthen or reduce the cause chains as necessary to support the JAX-WS programming model. 3. |
| * Prevents construction of the same exception. Uses similar principles as |
| * AxisFault.makeException. |
| * <p/> |
| * Example Usage: // Example usage |
| * <p/> |
| * public fooMethod() throws WebServiceException { try{ ... } catch(Exception e){ throw |
| * ExceptionFactory.makeWebServiceException(e); } } |
| */ |
| public class ExceptionFactory { |
| |
| protected static Log log = |
| LogFactory.getLog(ExceptionFactory.class.getName()); |
| |
| /** Private Constructor All methods are static. The private constructor prevents instantiation. */ |
| private ExceptionFactory() { |
| } |
| |
| /** |
| * Create a WebServiceException using the information from a given Throwable instance and message |
| * |
| * @param message |
| * @param throwable |
| * @return WebServiceException |
| */ |
| public static WebServiceException makeWebServiceException(String message, Throwable throwable) { |
| try { |
| // See if there is already a WebServiceException (Note that the returned exception could be a ProtocolException or |
| // other kind of exception) |
| WebServiceException e = |
| (WebServiceException)findException(throwable, WebServiceException.class); |
| if (e == null) { |
| e = createWebServiceException(message, throwable); |
| } |
| return e; |
| } catch (RuntimeException re) { |
| // TODO |
| // This is not a good situation, an exception occurred while building the exception. |
| // This should never occur! For now log the problem and rethrow...we may revisit this later |
| if (log.isDebugEnabled()) { |
| log.debug(Messages.getMessage("exceptionDuringExceptionFlow"), re); |
| } |
| throw re; |
| } |
| } |
| |
| /** |
| * Create a ProtocolException using the information from a Throwable and message |
| * |
| * @param message |
| * @param throwable |
| * @return ProtocolException |
| */ |
| public static ProtocolException makeProtocolException(String message, Throwable throwable) { |
| try { |
| // See if there is already a ProtocolException |
| ProtocolException e = |
| (ProtocolException)findException(throwable, ProtocolException.class); |
| if (e == null) { |
| e = createProtocolException(message, throwable); |
| } |
| return e; |
| } catch (RuntimeException re) { |
| // TODO |
| // This is not a good situation, an exception occurred while building the exception. |
| // This should never occur! For now log the problem and rethrow...we may revisit this later |
| if (log.isDebugEnabled()) { |
| log.debug(Messages.getMessage("exceptionDuringExceptionFlow"), re); |
| } |
| throw re; |
| } |
| } |
| |
| /** |
| * Make a WebServiceException with a given message |
| * |
| * @param message |
| * @return WebServiceException |
| */ |
| public static WebServiceException makeWebServiceException(String message) { |
| return makeWebServiceException(message, null); |
| } |
| |
| /** |
| * Create a WebServiceException using the information from a given Throwable instance |
| * |
| * @param throwable |
| * @return WebServiceException |
| */ |
| public static WebServiceException makeWebServiceException(Throwable throwable) { |
| return makeWebServiceException(null, throwable); |
| } |
| |
| |
| /** |
| * Create a WebServiceException |
| * |
| * @param message |
| * @param t Throwable |
| * @return WebServiceException |
| */ |
| private static WebServiceException createWebServiceException(String message, Throwable t) { |
| |
| // We might have an embedded WebServiceException that has a good message on it |
| WebServiceException me = (WebServiceException)findException(t, WebServiceException.class); |
| if (me != null) { |
| String meMessage = me.getMessage(); |
| if (meMessage != null) { |
| if (message == null) { |
| message = meMessage; |
| } else { |
| message = message + ": " + meMessage; |
| } |
| } |
| } |
| |
| // Get the root cause. We want to strip out the intermediate exceptions (like AxisFault) because |
| // these won't make sense to the user. |
| Throwable rootCause = null; |
| if (t != null) { |
| rootCause = getRootCause(t); |
| } |
| rootCause = rootCause == null ? t : rootCause; |
| WebServiceException e = null; |
| |
| // The root cause may not have a good message. We might want to enhance it |
| String enhancedMessage = enhanceMessage(rootCause); |
| if (enhancedMessage != null) { |
| if (message != null) |
| message = message + ": " + enhancedMessage; |
| else |
| message = enhancedMessage; |
| } |
| |
| if (message != null) { |
| e = new WebServiceException(message, rootCause); |
| } else { |
| e = new WebServiceException(rootCause); |
| } |
| |
| if (log.isDebugEnabled()) { |
| log.debug("Create Exception:", e); |
| } |
| return e; |
| } |
| |
| /** |
| * Create a ProtocolException |
| * |
| * @param message |
| * @param t Throwable |
| * @return ProtocolException |
| */ |
| private static ProtocolException createProtocolException(String message, Throwable t) { |
| Throwable rootCause = null; |
| if (t != null) { |
| rootCause = getRootCause(t); |
| } |
| rootCause = rootCause == null ? t : rootCause; |
| ProtocolException e = null; |
| if (message != null) { |
| e = new ProtocolException(message, rootCause); |
| } else { |
| e = new ProtocolException(rootCause); |
| } |
| if (log.isDebugEnabled()) { |
| log.debug("create Exception:", e); |
| } |
| return e; |
| } |
| |
| /** |
| * Return the exception or nested cause that is assignable from the specified class |
| * |
| * @param t Throwable |
| * @param cls |
| * @return Exception or null |
| */ |
| private static Exception findException(Throwable t, Class cls) { |
| while (t != null) { |
| if (cls.isAssignableFrom(t.getClass())) { |
| return (Exception)t; |
| } |
| t = getCause(t); |
| } |
| return null; |
| } |
| |
| /** |
| * Gets the Throwable cause of the Exception. Some exceptions store the cause in a different |
| * field, which is why this method should be used when walking the causes. |
| * |
| * @param t Throwable |
| * @return Throwable or null |
| */ |
| private static Throwable getCause(Throwable t) { |
| Throwable cause = null; |
| |
| // Look for a specific cause for this kind of exception |
| if (t instanceof InvocationTargetException) { |
| cause = ((InvocationTargetException)t).getTargetException(); |
| } |
| |
| // If no specific cause, fall back to the general cause. |
| if (cause == null) { |
| cause = t.getCause(); |
| } |
| return cause; |
| } |
| |
| /** |
| * This method searches the causes of the specified throwable until it finds one that is |
| * acceptable as a "root" cause. |
| * <p/> |
| * Example: If t is an AxisFault, the code traverses to the next cause. |
| * |
| * @param t Throwable |
| * @return Throwable root cause |
| */ |
| private static Throwable getRootCause(Throwable t) { |
| while (t != null) { |
| Throwable nextCause = null; |
| if (t instanceof InvocationTargetException || |
| t instanceof OMException || |
| t instanceof AxisFault) { |
| // Skip over this cause |
| nextCause = getCause(t); |
| if (nextCause == null) { |
| logRootCause(t); |
| return t; |
| } |
| t = nextCause; |
| } else { |
| // This is the root cause |
| logRootCause(t); |
| return t; |
| } |
| } |
| logRootCause(t); |
| return t; |
| } |
| |
| /** |
| * Other developers may add additional criteria to give better error messages back to the user. |
| * |
| * @param t Throwable |
| * @return String a message that helps the user understand what caused the exception |
| */ |
| private static String enhanceMessage(Throwable t) { |
| if (t == null) |
| return null; |
| |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| PrintStream ps = new PrintStream(baos, true); |
| t.printStackTrace(ps); |
| String stackTrace = baos.toString(); |
| |
| // TODO better criteria |
| if ((t instanceof StackOverflowError) && (stackTrace.contains("JAXB"))) |
| return Messages.getMessage("JABGraphProblem"); |
| |
| return null; |
| } |
| /** |
| * Log the root cause |
| * @param t |
| */ |
| private static void logRootCause(Throwable t) { |
| // TODO Should certain throwables be logged elsewhere |
| if (log.isDebugEnabled()) { |
| log.debug("Root Cause:" + t.toString()); |
| log.debug("stack:" + stackToString(t)); |
| } |
| } |
| |
| /** |
| * Get a string containing the stack of the specified exception |
| * @param e |
| * @return |
| */ |
| public static String stackToString(Throwable e) { |
| java.io.StringWriter sw= new java.io.StringWriter(); |
| java.io.BufferedWriter bw = new java.io.BufferedWriter(sw); |
| java.io.PrintWriter pw= new java.io.PrintWriter(bw); |
| e.printStackTrace(pw); |
| pw.close(); |
| return sw.getBuffer().toString(); |
| } |
| |
| /** |
| * Give a target Throwable, set the initialCause Throwable as the initial cause on the target. |
| * @param target The throwable on which to set the initial cause |
| * @param initialCause The initial cause to set on the target Throwable. |
| */ |
| public static void setInitialCause(Throwable target, Throwable initialCause) { |
| if (target != null && initialCause != null) { |
| // Create a WebServiceException from the initialCause throwable. This skips over things like |
| // AxisFault as a cause. Set the result on the target throwable. |
| try { |
| WebServiceException localException = ExceptionFactory.makeWebServiceException(initialCause); |
| target.initCause(localException.getCause()); |
| } catch (Throwable t) { |
| // If the cause had already been set, then it can't be set again; it throws an exception. |
| if (log.isDebugEnabled()) { |
| log.debug("Caught exception trying to set initial cause on: " + target +". Initial cause: " + |
| initialCause + ". Caught: " + t); |
| } |
| } |
| } |
| } |
| } |
| |