SEC-300: Applied Andreas Senft's patch for unwrapping exceptions in ExceptionTranslationFilter to obtain the cause.
This commit is contained in:
@@ -28,7 +28,7 @@ import org.springframework.core.NestedRuntimeException;
|
||||
public abstract class SpringSecurityException extends NestedRuntimeException {
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
/**
|
||||
/**
|
||||
* Constructs an <code>SpringSecurityException</code> with the specified
|
||||
* message and root cause.
|
||||
*
|
||||
@@ -39,7 +39,7 @@ public abstract class SpringSecurityException extends NestedRuntimeException {
|
||||
super(msg, t);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Constructs an <code>SpringSecurityException</code> with the specified
|
||||
* message and no root cause.
|
||||
*
|
||||
|
||||
+57
-20
@@ -25,6 +25,8 @@ import org.springframework.security.context.SecurityContextHolder;
|
||||
import org.springframework.security.ui.savedrequest.SavedRequest;
|
||||
import org.springframework.security.util.PortResolver;
|
||||
import org.springframework.security.util.PortResolverImpl;
|
||||
import org.springframework.security.util.ThrowableAnalyzer;
|
||||
import org.springframework.security.util.ThrowableCauseExtractor;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -92,7 +94,8 @@ public class ExceptionTranslationFilter extends SpringSecurityFilter implements
|
||||
private AuthenticationEntryPoint authenticationEntryPoint;
|
||||
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
|
||||
private PortResolver portResolver = new PortResolverImpl();
|
||||
private boolean createSessionAllowed = true;
|
||||
private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();
|
||||
private boolean createSessionAllowed = true;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
@@ -100,7 +103,8 @@ public class ExceptionTranslationFilter extends SpringSecurityFilter implements
|
||||
Assert.notNull(authenticationEntryPoint, "authenticationEntryPoint must be specified");
|
||||
Assert.notNull(portResolver, "portResolver must be specified");
|
||||
Assert.notNull(authenticationTrustResolver, "authenticationTrustResolver must be specified");
|
||||
}
|
||||
Assert.notNull(throwableAnalyzer, "throwableAnalyzer must be specified");
|
||||
}
|
||||
|
||||
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException,
|
||||
ServletException {
|
||||
@@ -112,24 +116,31 @@ public class ExceptionTranslationFilter extends SpringSecurityFilter implements
|
||||
logger.debug("Chain processed normally");
|
||||
}
|
||||
}
|
||||
catch (AuthenticationException ex) {
|
||||
handleException(request, response, chain, ex);
|
||||
}
|
||||
catch (AccessDeniedException ex) {
|
||||
handleException(request, response, chain, ex);
|
||||
}
|
||||
catch (ServletException ex) {
|
||||
if (ex.getRootCause() instanceof AuthenticationException
|
||||
|| ex.getRootCause() instanceof AccessDeniedException) {
|
||||
handleException(request, response, chain, (SpringSecurityException) ex.getRootCause());
|
||||
}
|
||||
else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Try to extract a SpringSecurityException from the stacktrace
|
||||
Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);
|
||||
SpringSecurityException ase = (SpringSecurityException)
|
||||
this.throwableAnalyzer.getFirstThrowableOfType(SpringSecurityException.class, causeChain);
|
||||
|
||||
if (ase != null) {
|
||||
handleException(request, response, chain, ase);
|
||||
}
|
||||
else {
|
||||
// Rethrow ServletExceptions and RuntimeExceptions as-is
|
||||
if (ex instanceof ServletException) {
|
||||
throw (ServletException) ex;
|
||||
}
|
||||
else if (ex instanceof RuntimeException) {
|
||||
throw (RuntimeException) ex;
|
||||
}
|
||||
|
||||
// Wrap other Exceptions. These are not expected to happen
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AuthenticationEntryPoint getAuthenticationEntryPoint() {
|
||||
@@ -235,7 +246,33 @@ public class ExceptionTranslationFilter extends SpringSecurityFilter implements
|
||||
this.portResolver = portResolver;
|
||||
}
|
||||
|
||||
public void setThrowableAnalyzer(ThrowableAnalyzer throwableAnalyzer) {
|
||||
this.throwableAnalyzer = throwableAnalyzer;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return FilterChainOrder.EXCEPTION_TRANSLATION_FILTER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of <code>ThrowableAnalyzer</code> which is capable of also unwrapping
|
||||
* <code>ServletException</code>s.
|
||||
*/
|
||||
private static final class DefaultThrowableAnalyzer extends ThrowableAnalyzer {
|
||||
/**
|
||||
* @see org.springframework.security.util.ThrowableAnalyzer#initExtractorMap()
|
||||
*/
|
||||
protected void initExtractorMap() {
|
||||
super.initExtractorMap();
|
||||
|
||||
registerExtractor(ServletException.class, new ThrowableCauseExtractor() {
|
||||
public Throwable extractCause(Throwable throwable) {
|
||||
ThrowableAnalyzer.verifyThrowableHierarchy(throwable, ServletException.class);
|
||||
return ((ServletException) throwable).getRootCause();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+270
@@ -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() + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package org.springframework.security.util;
|
||||
|
||||
|
||||
/**
|
||||
* Interface for handlers extracting the cause out of a specific {@link Throwable} type.
|
||||
*
|
||||
* @author Andreas Senft
|
||||
* @since 2.0
|
||||
* @version $Id$
|
||||
*
|
||||
* @see ThrowableAnalyzer
|
||||
*/
|
||||
public interface ThrowableCauseExtractor {
|
||||
|
||||
/**
|
||||
* Extracts the cause from the provided <code>Throwable</code>.
|
||||
*
|
||||
* @param throwable the <code>Throwable</code>
|
||||
* @return the extracted cause (maybe <code>null</code>)
|
||||
*
|
||||
* @throws IllegalArgumentException if <code>throwable</code> is <code>null</code>
|
||||
* or otherwise considered invalid for the implementation
|
||||
*/
|
||||
Throwable extractCause(Throwable throwable);
|
||||
}
|
||||
+1
-13
@@ -46,19 +46,8 @@ import javax.servlet.ServletResponse;
|
||||
* benalex $
|
||||
*/
|
||||
public class ExceptionTranslationFilterTests extends TestCase {
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
public ExceptionTranslationFilterTests() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ExceptionTranslationFilterTests(String arg0) {
|
||||
super(arg0);
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
SecurityContextHolder.clearContext();
|
||||
@@ -312,8 +301,7 @@ public class ExceptionTranslationFilterTests extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
// ~ Inner Classes
|
||||
// ==================================================================================================
|
||||
// ~ Inner Classes =================================================================================================
|
||||
|
||||
private class MockFilterChain implements FilterChain {
|
||||
private boolean throwAccessDenied;
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
package org.springframework.security.util;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Testcases for {@link ThrowableAnalyzer}.
|
||||
*
|
||||
* @author Andreas Senft
|
||||
*/
|
||||
public class ThrowableAnalyzerTests extends TestCase {
|
||||
|
||||
/**
|
||||
* Exception for testing purposes. The cause is not retrievable by {@link #getCause()}.
|
||||
*/
|
||||
public static final class NonStandardException extends Exception {
|
||||
|
||||
private Throwable cause;
|
||||
|
||||
public NonStandardException(String message, Throwable cause) {
|
||||
super(message);
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
public Throwable resolveCause() {
|
||||
return this.cause;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>ThrowableCauseExtractor</code> for handling <code>NonStandardException</code> instances.
|
||||
*/
|
||||
public static final class NonStandardExceptionCauseExtractor implements ThrowableCauseExtractor {
|
||||
|
||||
public Throwable extractCause(Throwable throwable) {
|
||||
ThrowableAnalyzer.verifyThrowableHierarchy(throwable, NonStandardException.class);
|
||||
return ((NonStandardException) throwable).resolveCause();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An array of nested throwables for testing.
|
||||
* The cause of element 0 is element 1, the cause of element 1 is element 2 and so on.
|
||||
*/
|
||||
private Throwable[] testTrace;
|
||||
|
||||
/**
|
||||
* Plain <code>ThrowableAnalyzer</code>.
|
||||
*/
|
||||
private ThrowableAnalyzer standardAnalyzer;
|
||||
|
||||
/**
|
||||
* Enhanced <code>ThrowableAnalyzer</code> capable to process
|
||||
* <code>NonStandardException</code>s.
|
||||
*/
|
||||
private ThrowableAnalyzer nonstandardAnalyzer;
|
||||
|
||||
/**
|
||||
* @see junit.framework.TestCase#setUp()
|
||||
*/
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
// Set up test trace
|
||||
this.testTrace = new Throwable[7];
|
||||
this.testTrace[6] = new IllegalArgumentException("Test_6");
|
||||
this.testTrace[5] = new Throwable("Test_5",this.testTrace[6]);
|
||||
this.testTrace[4] = new InvocationTargetException(this.testTrace[5], "Test_4");
|
||||
this.testTrace[3] = new Exception("Test_3", this.testTrace[4]);
|
||||
this.testTrace[2] = new NonStandardException("Test_2", this.testTrace[3]);
|
||||
this.testTrace[1] = new RuntimeException("Test_1", this.testTrace[2]);
|
||||
this.testTrace[0] = new Exception("Test_0", this.testTrace[1]);
|
||||
|
||||
// Set up standard analyzer
|
||||
this.standardAnalyzer = new ThrowableAnalyzer();
|
||||
|
||||
// Set up nonstandard analyzer
|
||||
this.nonstandardAnalyzer = new ThrowableAnalyzer() {
|
||||
/**
|
||||
* @see org.springframework.security.util.ThrowableAnalyzer#initExtractorMap()
|
||||
*/
|
||||
@Override
|
||||
protected void initExtractorMap() {
|
||||
super.initExtractorMap();
|
||||
// register extractor for NonStandardException
|
||||
registerExtractor(NonStandardException.class, new NonStandardExceptionCauseExtractor());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see junit.framework.TestCase#tearDown()
|
||||
*/
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
|
||||
public void testRegisterExtractorWithInvalidClass() {
|
||||
try {
|
||||
new ThrowableAnalyzer() {
|
||||
|
||||
/**
|
||||
* @see org.springframework.security.util.ThrowableAnalyzer#initExtractorMap()
|
||||
*/
|
||||
@Override
|
||||
protected void initExtractorMap() {
|
||||
// Object is no subclass of Throwable
|
||||
super.registerExtractor(Object.class, DEFAULT_EXTRACTOR);
|
||||
}
|
||||
};
|
||||
|
||||
fail("IllegalArgumentExpected");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
public void testRegisterExtractorWithInvalidExtractor() {
|
||||
try {
|
||||
new ThrowableAnalyzer() {
|
||||
|
||||
/**
|
||||
* @see org.springframework.security.util.ThrowableAnalyzer#initExtractorMap()
|
||||
*/
|
||||
@Override
|
||||
protected void initExtractorMap() {
|
||||
// null is no valid extractor
|
||||
super.registerExtractor(Exception.class, null);
|
||||
}
|
||||
};
|
||||
|
||||
fail("IllegalArgumentExpected");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetRegisteredTypes() {
|
||||
|
||||
Class[] registeredTypes = this.nonstandardAnalyzer.getRegisteredTypes();
|
||||
|
||||
for (int i = 0; i < registeredTypes.length; ++i) {
|
||||
Class clazz = registeredTypes[i];
|
||||
|
||||
// The most specific types have to occur first.
|
||||
for (int j = 0; j < i; ++j) {
|
||||
Class prevClazz = registeredTypes[j];
|
||||
|
||||
assertFalse("Unexpected order of registered classes: "
|
||||
+ prevClazz + " is assignable from " + clazz, prevClazz.isAssignableFrom(clazz));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testDetermineCauseChainWithNoExtractors() {
|
||||
ThrowableAnalyzer analyzer = new ThrowableAnalyzer() {
|
||||
|
||||
/**
|
||||
* @see org.springframework.security.util.ThrowableAnalyzer#initExtractorMap()
|
||||
*/
|
||||
@Override
|
||||
protected void initExtractorMap() {
|
||||
// skip default initialization
|
||||
}
|
||||
};
|
||||
|
||||
assertEquals("Unexpected number of registered types", 0, analyzer.getRegisteredTypes().length);
|
||||
|
||||
Throwable t = this.testTrace[0];
|
||||
Throwable[] chain = analyzer.determineCauseChain(t);
|
||||
// Without extractors only the root throwable is available
|
||||
assertEquals("Unexpected chain size", 1, chain.length);
|
||||
assertEquals("Unexpected chain entry", t, chain[0]);
|
||||
}
|
||||
|
||||
public void testDetermineCauseChainWithDefaultExtractors() {
|
||||
ThrowableAnalyzer analyzer = this.standardAnalyzer;
|
||||
|
||||
assertEquals("Unexpected number of registered types", 2, analyzer.getRegisteredTypes().length);
|
||||
|
||||
Throwable[] chain = analyzer.determineCauseChain(this.testTrace[0]);
|
||||
|
||||
// Element at index 2 is a NonStandardException which cannot be analyzed further by default
|
||||
assertEquals("Unexpected chain size", 3, chain.length);
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
assertEquals("Unexpected chain entry: " + i, this.testTrace[i], chain[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void testDetermineCauseChainWithCustomExtractors() {
|
||||
ThrowableAnalyzer analyzer = this.nonstandardAnalyzer;
|
||||
|
||||
Throwable[] chain = analyzer.determineCauseChain(this.testTrace[0]);
|
||||
|
||||
assertEquals("Unexpected chain size", this.testTrace.length, chain.length);
|
||||
for (int i = 0; i < chain.length; ++i) {
|
||||
assertEquals("Unexpected chain entry: " + i, this.testTrace[i], chain[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetFirstThrowableOfTypeWithSuccess1() {
|
||||
ThrowableAnalyzer analyzer = this.nonstandardAnalyzer;
|
||||
|
||||
Throwable[] chain = analyzer.determineCauseChain(this.testTrace[0]);
|
||||
|
||||
Throwable result = analyzer.getFirstThrowableOfType(Exception.class, chain);
|
||||
|
||||
assertNotNull("null not expected", result);
|
||||
assertEquals("Unexpected throwable found", this.testTrace[0], result);
|
||||
}
|
||||
|
||||
public void testGetFirstThrowableOfTypeWithSuccess2() {
|
||||
ThrowableAnalyzer analyzer = this.nonstandardAnalyzer;
|
||||
|
||||
Throwable[] chain = analyzer.determineCauseChain(this.testTrace[0]);
|
||||
|
||||
Throwable result = analyzer.getFirstThrowableOfType(NonStandardException.class, chain);
|
||||
|
||||
assertNotNull("null not expected", result);
|
||||
assertEquals("Unexpected throwable found", this.testTrace[2], result);
|
||||
}
|
||||
|
||||
public void testGetFirstThrowableOfTypeWithFailure() {
|
||||
ThrowableAnalyzer analyzer = this.nonstandardAnalyzer;
|
||||
|
||||
Throwable[] chain = analyzer.determineCauseChain(this.testTrace[0]);
|
||||
|
||||
// IllegalStateException not in trace
|
||||
Throwable result = analyzer.getFirstThrowableOfType(IllegalStateException.class, chain);
|
||||
|
||||
assertNull("null expected", result);
|
||||
}
|
||||
|
||||
public void testVerifyThrowableHierarchyWithExactType() {
|
||||
|
||||
Throwable throwable = new IllegalStateException("Test");
|
||||
ThrowableAnalyzer.verifyThrowableHierarchy(throwable, IllegalStateException.class);
|
||||
// No exception expected
|
||||
}
|
||||
|
||||
public void testVerifyThrowableHierarchyWithCompatibleType() {
|
||||
|
||||
Throwable throwable = new IllegalStateException("Test");
|
||||
ThrowableAnalyzer.verifyThrowableHierarchy(throwable, Exception.class);
|
||||
// No exception expected
|
||||
}
|
||||
|
||||
public void testVerifyThrowableHierarchyWithNull() {
|
||||
try {
|
||||
ThrowableAnalyzer.verifyThrowableHierarchy(null, Throwable.class);
|
||||
fail("IllegalArgumentException expected");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
public void testVerifyThrowableHierarchyWithNonmatchingType() {
|
||||
|
||||
Throwable throwable = new IllegalStateException("Test");
|
||||
try {
|
||||
ThrowableAnalyzer.verifyThrowableHierarchy(throwable, InvocationTargetException.class);
|
||||
fail("IllegalArgumentException expected");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user