All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.healthmarketscience.common.util.PassthroughException Maven / Gradle / Ivy

There is a newer version: 1.1.1
Show newest version
/*
Copyright (c) 2007 Health Market Science, Inc.

Licensed 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 com.healthmarketscience.common.util;

import java.io.Serializable;
import org.apache.commons.lang.exception.ExceptionUtils;

/**
 * This exception is useful for copying internal exceptions within one
 * classloader so that they can be inspected within another classloader (after
 * serialization/deserialization) without throwing ClassNotFoundException (or
 * related exceptions).  This is assuming, of course, that this class is
 * available in both classloaders.  An example for this would be an exception
 * generated within a remote server, where the client code would not have the
 * actual class of the exception.  By "copying" the exception as a
 * PassthroughException, the exception details can be "passed through" the
 * classloader barrier.  The receiver will get a complete stacktrace which
 * looks identical to the server-side stacktrace without any of the class
 * definition issues.
 * 

* Example usage: *

 *
 * public class MyBean {
 *   public int computeSomething() throws MyPublicBeanException {
 *      try {
 *        // use some internal library to do something
 *      } catch(InternalImplException e) {
 *        throw new MyPublicBeanException("Failed doing something",
 *                                        PassthroughException.create(e));
 *      }
 *   }
 * }
 *
 * 
* * @author James Ahlborn */ public final class PassthroughException extends RuntimeException implements Serializable { private static final long serialVersionUID = 20071230; private static final ReflectionFactory THROWABLE_FACTORY = new ReflectionFactory( Throwable.class, IllegalArgumentException.class, String.class, Throwable.class); private final String _originalException; /** * Constructs a PassthroughException by copying information from the given * original exception. * @param original the original non-portable exception * @param cause the cause for this exception, (most likely also a * PassthroughException) * @see #create for simple instantiation of a PassthroughException */ public PassthroughException(Throwable original, PassthroughException cause) { this(getOriginalExceptionName(original), original.getMessage(), original.getStackTrace(), cause); } /** * Constructs a PassthroughException with the given information. * @param originalException class name of the original exception * @param originalMessage message of the original exception * @param originalStackTrace stacktrace of the original exception * @param cause the cause for this exception, (most likely also a * PassthroughException) * @see #create for simple instantiation of a PassthroughException */ public PassthroughException(String originalException, String originalMessage, StackTraceElement[] originalStackTrace, PassthroughException cause) { super(originalMessage, cause); _originalException = originalException; setStackTrace(originalStackTrace); } @Override public String toString() { String msg = getMessage(); return ((msg == null) ? _originalException : (_originalException + ": " + msg)); } /** * @return the class name of the original exception wrapped by this * PassthroughException */ public String getOriginalExceptionName() { return _originalException; } /** * @return the class name of the given exception, with special handling for * PassthroughExceptions */ static String getOriginalExceptionName(Throwable t) { if(t instanceof PassthroughException) { return ((PassthroughException)t).getOriginalExceptionName(); } return t.getClass().getName(); } /** * Constructs a PassthroughException which will print out a virtually * identical stacktrace to the given Throwable. This method will deeply * "copy" all causes within the given Throwable, returning a cause chain of * instances of PassthroughException. Thus, the returned object will have * no class dependencies outside of the JDK and this class. *

* Note, if the given throwable is already a PassthroughException, it will * be returned as-is. * * @param t the throwable to deeply "copy". * @return a deep "copy" of the given Throwable, where all of the exception * classes are actually PassthroughException instances. Will return * {@code null} if given {@code null}. */ public static PassthroughException create(Throwable t) { if(t == null) { // nothing to do return null; } if(t.getClass() == PassthroughException.class) { // no need to re-wrap everything return (PassthroughException)t; } // reconstruct the chain of exceptions from top to bottom using // PassthroughExceptions Throwable[] ts = ExceptionUtils.getThrowables(t); PassthroughException last = null; for(int i = ts.length - 1; i >= 0; --i) { Throwable next = ts[i]; last = new PassthroughException(next, last); } return last; } /** * Creates an exception of the given type, with the given throwable * translated to a PassthroughException as the cause. If the top level * exception of the given cause chain is an instance of the given exception * type, it will be copied (and the result exception will be of this type), * otherwise a new exception of the given type will be created. *

* This method will throw Errors immediately, as-is. * * @param t throwable cause to wrap as a PassthroughException * @param exType the returned exception will be a subclass of this type. * This type (and it's subclasses) must have a public * constructor like {@code ExType(String, Throwable)}. * @param defaultMsg if the top exception is not of exType and this message * is non-{@code null}, this will be the message of the * resulting exception, otherwise the message of the top * PassthroughException will be used (or {@code ""} if * none). * @return an exception of the given type (possibly a subclass) where all * nested causes have been translated to PassthroughExceptions. * Will return {@code null} if given {@code null}. */ public static ExType create( Throwable t, Class exType, String defaultMsg) { return create(t, exType, defaultMsg, true); } /** * Creates an exception of the given type, with the given throwable * translated to a PassthroughException as the cause. If the top level * exception of the given cause chain is an instance of the given exception * type, it will be copied (and the result exception will be of this type), * otherwise a new exception of the given type will be created. *

* Errors may optionally be thrown immediately, as-is, or wrapped as any * other cause. * * @param t throwable cause to wrap as a PassthroughException * @param exType the returned exception will be a subclass of this type. * This type (and it's subclasses) must have a public * constructor like {@code ExType(String, Throwable)}. * @param defaultMsg if the top exception is not of exType and this message * is non-{@code null}, this will be the message of the * resulting exception, otherwise the message of the top * PassthroughException will be used (or {@code ""} if * none). * @param throwError iff {@code true}, Errors will be thrown immediately, * as-is, otherwise they will be treated like any other * cause. * @return an exception of the given type (possibly a subclass) where all * nested causes have been translated to PassthroughExceptions. * Will return {@code null} if given {@code null}. */ public static ExType create( Throwable t, Class exType, String defaultMsg, boolean throwError) { if(t == null) { // nothing to do return null; } if(throwError && (t instanceof Error)) { throw (Error)t; } // if the top-level exception is already an instance of exType, we want to // clone it, otherwise we will wrap it like everything else Throwable topT = t; ExType origT = findException(t, exType); if(t.equals(origT)) { // the top exception is the given type, so we will replace that one // instead of nesting it t = ExceptionUtils.getCause(t); } // wrap all the leftovers PassthroughException passthroughCause = create(t); // check to see if this exception has already been wrapped if((origT != null) && (t.equals(passthroughCause))) { // no need to repeat this process return origT; } // now, create the top-level exception String className = null; String msg = null; if(origT != null) { // copy the original exception msg = origT.getMessage(); className = origT.getClass().getName(); } else { // create new top exception msg = ((defaultMsg != null) ? defaultMsg : ((passthroughCause != null) ? passthroughCause.getMessage() : "")); className = exType.getName(); } ExType rtn = exType.cast( THROWABLE_FACTORY.create(className, msg, passthroughCause)); // always keep the top stack trace (the current stack trace is never // interesting) rtn.setStackTrace(topT.getStackTrace()); return rtn; } /** * @return the first Throwable of the given type in the given Throwable * or its causes, or null if none found. */ private static ThrowType findException( Throwable t, Class throwClass) { int idx = ExceptionUtils.indexOfType(t, throwClass); if(idx >= 0) { return throwClass.cast(ExceptionUtils.getThrowables(t)[idx]); } return null; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy