| 
					
				 | 
			
			
				@@ -0,0 +1,270 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+package org.springframework.security.util; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.lang.reflect.InvocationTargetException; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.ArrayList; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.Comparator; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.Iterator; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.List; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.Map; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.TreeMap; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * Handler for analyzing {@link Throwable} instances. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * Can be subclassed to customize its behavior. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * @author Andreas Senft 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * @since 2.0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * @version $Id$ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+public class ThrowableAnalyzer { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Default extractor for {@link Throwable} instances. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @see Throwable#getCause() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public static final ThrowableCauseExtractor DEFAULT_EXTRACTOR 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        = new ThrowableCauseExtractor() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            public Throwable extractCause(Throwable throwable) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return throwable.getCause(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Default extractor for {@link InvocationTargetException} instances. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @see InvocationTargetException#getTargetException() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public static final ThrowableCauseExtractor INVOCATIONTARGET_EXTRACTOR  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        = new ThrowableCauseExtractor() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            public Throwable extractCause(Throwable throwable) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                verifyThrowableHierarchy(throwable, InvocationTargetException.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return ((InvocationTargetException) throwable).getTargetException(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Comparator to order classes ascending according to their hierarchy relation. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * If two classes have a hierarchical relation, the "higher" class is considered  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * to be greater by this comparator.<br> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * For hierarchically unrelated classes their fully qualified name will be compared.  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static final Comparator CLASS_HIERARCHY_COMPARATOR = new Comparator() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public int compare(Object o1, Object o2) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Class class1 = (Class) o1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Class class2 = (Class) o2; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+             
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (class1.isAssignableFrom(class2)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } else if (class2.isAssignableFrom(class1)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return class1.getName().compareTo(class2.getName()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Map of registered cause extractors. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * key: Class<Throwable>; value: ThrowableCauseExctractor 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private final Map extractorMap; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Creates a new <code>ThrowableAnalyzer</code> instance. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public ThrowableAnalyzer() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        this.extractorMap = new TreeMap(CLASS_HIERARCHY_COMPARATOR); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        initExtractorMap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Registers a <code>ThrowableCauseExtractor</code> for the specified type. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * <i>Can be used in subclasses overriding {@link #initExtractorMap()}.</i> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param throwableType the type (has to be a subclass of <code>Throwable</code>) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param extractor the associated <code>ThrowableCauseExtractor</code> (not <code>null</code>) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @throws IllegalArgumentException if one of the arguments is invalid 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    protected final void registerExtractor(Class throwableType, ThrowableCauseExtractor extractor) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        verifyThrowableType(throwableType); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (extractor == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new IllegalArgumentException("Invalid extractor: null"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        this.extractorMap.put(throwableType, extractor); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Initializes associations between <code>Throwable</code>s and <code>ThrowableCauseExtractor</code>s. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * The default implementation performs the following registrations: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * <li>{@link #DEFAULT_EXTRACTOR} for {@link Throwable}</li> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * <li>{@link #INVOCATIONTARGET_EXTRACTOR} for {@link InvocationTargetException}</li> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * <br> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Subclasses overriding this method are encouraged to invoke the super method to perform the 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * default registrations. They can register additional extractors as required. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * <p> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Note: An extractor registered for a specific type is applicable for that type <i>and all subtypes thereof</i>. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * However, extractors registered to more specific types are guaranteed to be resolved first. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * So in the default case InvocationTargetExceptions will be handled by {@link #INVOCATIONTARGET_EXTRACTOR} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * while all other throwables are handled by {@link #DEFAULT_EXTRACTOR}. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @see #registerExtractor(Class, ThrowableCauseExtractor) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    protected void initExtractorMap() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        registerExtractor(InvocationTargetException.class, INVOCATIONTARGET_EXTRACTOR); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        registerExtractor(Throwable.class, DEFAULT_EXTRACTOR); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Returns an array containing the classes for which extractors are registered. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * The order of the classes is the order in which comparisons will occur for 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * resolving a matching extractor. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @return the types for which extractors are registered 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    final Class[] getRegisteredTypes() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        List typeList = new ArrayList(this.extractorMap.keySet()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return (Class[]) typeList.toArray(new Class[typeList.size()]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Determines the cause chain of the provided <code>Throwable</code>. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * The returned array contains all throwables extracted from the stacktrace, using the registered 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * {@link ThrowableCauseExtractor extractors}. The elements of the array are ordered: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * The first element is the passed in throwable itself. The following elements 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * appear in their order downward the stacktrace. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * <p> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Note: If no {@link ThrowableCauseExtractor} is registered for this instance  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * then the returned array will always only contain the passed in throwable. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param throwable the <code>Throwable</code> to analyze 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @return an array of all determined throwables from the stacktrace 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @throws IllegalArgumentException if the throwable is <code>null</code> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @see #initExtractorMap() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public final Throwable[] determineCauseChain(Throwable throwable) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (throwable == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new IllegalArgumentException("Invalid throwable: null"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        List chain = new ArrayList(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Throwable currentThrowable = throwable; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        while (currentThrowable != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            chain.add(currentThrowable); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            currentThrowable = extractCause(currentThrowable); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return (Throwable[]) chain.toArray(new Throwable[chain.size()]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Extracts the cause of the given throwable using an appropriate extractor. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param throwable the <code>Throwable</code> (not <code>null</code> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @return the cause, may be <code>null</code> if none could be resolved 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private Throwable extractCause(Throwable throwable) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for (Iterator iter = this.extractorMap.entrySet().iterator(); iter.hasNext(); ) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Map.Entry entry = (Map.Entry) iter.next(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+             
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Class throwableType = (Class) entry.getKey(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (throwableType.isInstance(throwable)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ThrowableCauseExtractor extractor = (ThrowableCauseExtractor) entry.getValue(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return extractor.extractCause(throwable); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Returns the first throwable from the passed in array that is assignable to the provided type. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * A returned instance is safe to be cast to the specified type. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * <p> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * If the passed in array is null or empty this method returns <code>null</code>. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param throwableType the type to look for 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param chain the array (will be processed in element order) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @return the found <code>Throwable</code>, <code>null</code> if not found 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @throws IllegalArgumentException if the provided type is <code>null</code>  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * or no subclass of <code>Throwable</code> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public final Throwable getFirstThrowableOfType(Class throwableType, Throwable[] chain) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        verifyThrowableType(throwableType); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (chain != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for (int i = 0; i < chain.length; ++i) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                Throwable t = chain[i]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if ((t != null) && throwableType.isInstance(t)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return t; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Convenience method for verifying that the passed in class refers to a valid  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * subclass of <code>Throwable</code>. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param throwableType the type to check 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @throws IllegalArgumentException if <code>typeToCheck</code> is either <code>null</code> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * or not assignable to <code>expectedBaseType</code> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private static void verifyThrowableType(Class throwableType) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (throwableType == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new IllegalArgumentException("Invalid type: null"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!Throwable.class.isAssignableFrom(throwableType)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new IllegalArgumentException("Invalid type: '"  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    + throwableType.getName()  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    + "'. Has to be a subclass of '" + Throwable.class.getName() + "'"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Verifies that the provided throwable is a valid subclass of the provided type (or of the type itself). 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * If <code>expectdBaseType</code> is <code>null</code>, no check will be performed. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * <p> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * Can be used for verification purposes in implementations  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * of {@link ThrowableCauseExtractor extractors}. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param throwable the <code>Throwable</code> to check 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param expectedBaseType the type to check against 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     *  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @throws IllegalArgumentException if <code>throwable</code> is either <code>null</code> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * or its type is not assignable to <code>expectedBaseType</code> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public static final void verifyThrowableHierarchy(Throwable throwable, Class expectedBaseType) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (expectedBaseType == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (throwable == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new IllegalArgumentException("Invalid throwable: null"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Class throwableType = throwable.getClass(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!expectedBaseType.isAssignableFrom(throwableType)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new IllegalArgumentException("Invalid type: '"  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    + throwableType.getName()  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    + "'. Has to be a subclass of '" + expectedBaseType.getName() + "'"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 |