Java Code Examples for org.springframework.util.ReflectionUtils#rethrowException()

The following examples show how to use org.springframework.util.ReflectionUtils#rethrowException() . These examples are extracted from open source projects. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
/**
 * Handles a fatal error thrown while asynchronously invoking the specified
 * {@link Method}.
 * <p>If the return type of the method is a {@link Future} object, the original
 * exception can be propagated by just throwing it at the higher level. However,
 * for all other cases, the exception will not be transmitted back to the client.
 * In that later case, the current {@link AsyncUncaughtExceptionHandler} will be
 * used to manage such exception.
 * @param ex the exception to handle
 * @param method the method that was invoked
 * @param params the parameters used to invoke the method
 */
protected void handleError(Throwable ex, Method method, Object... params) throws Exception {
	if (Future.class.isAssignableFrom(method.getReturnType())) {
		ReflectionUtils.rethrowException(ex);
	}
	else {
		// Could not transmit the exception to the caller with default executor
		try {
			this.exceptionHandler.obtain().handleUncaughtException(ex, method, params);
		}
		catch (Throwable ex2) {
			logger.warn("Exception handler for async method '" + method.toGenericString() +
					"' threw unexpected exception itself", ex2);
		}
	}
}
 
Example 2
/**
 * Run all {@link BeforeTransaction @BeforeTransaction} methods for the
 * specified {@linkplain TestContext test context}. If one of the methods
 * fails, however, the caught exception will be rethrown in a wrapped
 * {@link RuntimeException}, and the remaining methods will <strong>not</strong>
 * be given a chance to execute.
 * @param testContext the current test context
 */
protected void runBeforeTransactionMethods(TestContext testContext) throws Exception {
	try {
		List<Method> methods = getAnnotatedMethods(testContext.getTestClass(), BeforeTransaction.class);
		Collections.reverse(methods);
		for (Method method : methods) {
			if (logger.isDebugEnabled()) {
				logger.debug("Executing @BeforeTransaction method [" + method + "] for test context " + testContext);
			}
			ReflectionUtils.makeAccessible(method);
			method.invoke(testContext.getTestInstance());
		}
	}
	catch (InvocationTargetException ex) {
		if (logger.isErrorEnabled()) {
			logger.error("Exception encountered while executing @BeforeTransaction methods for test context " +
					testContext + ".", ex.getTargetException());
		}
		ReflectionUtils.rethrowException(ex.getTargetException());
	}
}
 
Example 3
/**
 * Hook for preparing a test instance prior to execution of any individual
 * test methods, for example for injecting dependencies, etc. Should be
 * called immediately after instantiation of the test instance.
 * <p>The managed {@link TestContext} will be updated with the supplied
 * {@code testInstance}.
 * <p>An attempt will be made to give each registered
 * {@link TestExecutionListener} a chance to prepare the test instance. If a
 * listener throws an exception, however, the remaining registered listeners
 * will <strong>not</strong> be called.
 * @param testInstance the test instance to prepare (never {@code null})
 * @throws Exception if a registered TestExecutionListener throws an exception
 * @see #getTestExecutionListeners()
 */
public void prepareTestInstance(Object testInstance) throws Exception {
	if (logger.isTraceEnabled()) {
		logger.trace("prepareTestInstance(): instance [" + testInstance + "]");
	}
	getTestContext().updateState(testInstance, null, null);

	for (TestExecutionListener testExecutionListener : getTestExecutionListeners()) {
		try {
			testExecutionListener.prepareTestInstance(getTestContext());
		}
		catch (Throwable ex) {
			if (logger.isErrorEnabled()) {
				logger.error("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
						"] to prepare test instance [" + testInstance + "]", ex);
			}
			ReflectionUtils.rethrowException(ex);
		}
	}
}
 
Example 4
/**
 * Handles a fatal error thrown while asynchronously invoking the specified
 * {@link Method}.
 * <p>If the return type of the method is a {@link Future} object, the original
 * exception can be propagated by just throwing it at the higher level. However,
 * for all other cases, the exception will not be transmitted back to the client.
 * In that later case, the current {@link AsyncUncaughtExceptionHandler} will be
 * used to manage such exception.
 * @param ex the exception to handle
 * @param method the method that was invoked
 * @param params the parameters used to invoke the method
 */
protected void handleError(Throwable ex, Method method, Object... params) throws Exception {
	if (Future.class.isAssignableFrom(method.getReturnType())) {
		ReflectionUtils.rethrowException(ex);
	}
	else {
		// Could not transmit the exception to the caller with default executor
		try {
			this.exceptionHandler.obtain().handleUncaughtException(ex, method, params);
		}
		catch (Throwable ex2) {
			logger.warn("Exception handler for async method '" + method.toGenericString() +
					"' threw unexpected exception itself", ex2);
		}
	}
}
 
Example 5
/**
 * Run all {@link BeforeTransaction @BeforeTransaction} methods for the
 * specified {@linkplain TestContext test context}. If one of the methods
 * fails, however, the caught exception will be rethrown in a wrapped
 * {@link RuntimeException}, and the remaining methods will <strong>not</strong>
 * be given a chance to execute.
 * @param testContext the current test context
 */
protected void runBeforeTransactionMethods(TestContext testContext) throws Exception {
	try {
		List<Method> methods = getAnnotatedMethods(testContext.getTestClass(), BeforeTransaction.class);
		Collections.reverse(methods);
		for (Method method : methods) {
			if (logger.isDebugEnabled()) {
				logger.debug("Executing @BeforeTransaction method [" + method + "] for test context " + testContext);
			}
			ReflectionUtils.makeAccessible(method);
			method.invoke(testContext.getTestInstance());
		}
	}
	catch (InvocationTargetException ex) {
		if (logger.isErrorEnabled()) {
			logger.error("Exception encountered while executing @BeforeTransaction methods for test context " +
					testContext + ".", ex.getTargetException());
		}
		ReflectionUtils.rethrowException(ex.getTargetException());
	}
}
 
Example 6
/**
 * Hook for pre-processing a test class <em>before</em> execution of any
 * tests within the class. Should be called prior to any framework-specific
 * <em>before class methods</em> (e.g., methods annotated with JUnit 4's
 * {@link org.junit.BeforeClass @BeforeClass}).
 * <p>An attempt will be made to give each registered
 * {@link TestExecutionListener} a chance to pre-process the test class
 * execution. If a listener throws an exception, however, the remaining
 * registered listeners will <strong>not</strong> be called.
 * @throws Exception if a registered TestExecutionListener throws an
 * exception
 * @since 3.0
 * @see #getTestExecutionListeners()
 */
public void beforeTestClass() throws Exception {
	Class<?> testClass = getTestContext().getTestClass();
	if (logger.isTraceEnabled()) {
		logger.trace("beforeTestClass(): class [" + testClass.getName() + "]");
	}
	getTestContext().updateState(null, null, null);

	for (TestExecutionListener testExecutionListener : getTestExecutionListeners()) {
		try {
			testExecutionListener.beforeTestClass(getTestContext());
		}
		catch (Throwable ex) {
			logException(ex, "beforeTestClass", testExecutionListener, testClass);
			ReflectionUtils.rethrowException(ex);
		}
	}
}
 
Example 7
/**
 * Hook for preparing a test instance prior to execution of any individual
 * test methods, for example for injecting dependencies, etc. Should be
 * called immediately after instantiation of the test instance.
 * <p>The managed {@link TestContext} will be updated with the supplied
 * {@code testInstance}.
 * <p>An attempt will be made to give each registered
 * {@link TestExecutionListener} a chance to prepare the test instance. If a
 * listener throws an exception, however, the remaining registered listeners
 * will <strong>not</strong> be called.
 * @param testInstance the test instance to prepare (never {@code null})
 * @throws Exception if a registered TestExecutionListener throws an exception
 * @see #getTestExecutionListeners()
 */
public void prepareTestInstance(Object testInstance) throws Exception {
	if (logger.isTraceEnabled()) {
		logger.trace("prepareTestInstance(): instance [" + testInstance + "]");
	}
	getTestContext().updateState(testInstance, null, null);

	for (TestExecutionListener testExecutionListener : getTestExecutionListeners()) {
		try {
			testExecutionListener.prepareTestInstance(getTestContext());
		}
		catch (Throwable ex) {
			if (logger.isErrorEnabled()) {
				logger.error("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
						"] to prepare test instance [" + testInstance + "]", ex);
			}
			ReflectionUtils.rethrowException(ex);
		}
	}
}
 
Example 8
/**
 * Handles a fatal error thrown while asynchronously invoking the specified
 * {@link Method}.
 * <p>If the return type of the method is a {@link Future} object, the original
 * exception can be propagated by just throwing it at the higher level. However,
 * for all other cases, the exception will not be transmitted back to the client.
 * In that later case, the current {@link AsyncUncaughtExceptionHandler} will be
 * used to manage such exception.
 * @param ex the exception to handle
 * @param method the method that was invoked
 * @param params the parameters used to invoke the method
 */
protected void handleError(Throwable ex, Method method, Object... params) throws Exception {
	if (Future.class.isAssignableFrom(method.getReturnType())) {
		ReflectionUtils.rethrowException(ex);
	}
	else {
		// Could not transmit the exception to the caller with default executor
		try {
			this.exceptionHandler.handleUncaughtException(ex, method, params);
		}
		catch (Throwable ex2) {
			logger.error("Exception handler for async method '" + method.toGenericString() +
					"' threw unexpected exception itself", ex2);
		}
	}
}
 
Example 9
/**
 * Handles a fatal error thrown while asynchronously invoking the specified
 * {@link Method}.
 * <p>If the return type of the method is a {@link Future} object, the original
 * exception can be propagated by just throwing it at the higher level. However,
 * for all other cases, the exception will not be transmitted back to the client.
 * In that later case, the current {@link AsyncUncaughtExceptionHandler} will be
 * used to manage such exception.
 * @param ex the exception to handle
 * @param method the method that was invoked
 * @param params the parameters used to invoke the method
 */
protected void handleError(Throwable ex, Method method, Object... params) throws Exception {
	if (Future.class.isAssignableFrom(method.getReturnType())) {
		ReflectionUtils.rethrowException(ex);
	}
	else {
		// Could not transmit the exception to the caller with default executor
		try {
			this.exceptionHandler.handleUncaughtException(ex, method, params);
		}
		catch (Throwable ex2) {
			logger.error("Exception handler for async method '" + method.toGenericString() +
					"' threw unexpected exception itself", ex2);
		}
	}
}
 
Example 10
private Object doInvokeMethod(Method method, Object target, Object[] args) throws Exception {
	ReflectionUtils.makeAccessible(method);
	try {
		return method.invoke(target, args);
	}
	catch (InvocationTargetException ex) {
		ReflectionUtils.rethrowException(ex.getTargetException());
	}
	throw new IllegalStateException("Should never get here");
}
 
Example 11
/**
 * Hook for post-processing a test class <em>after</em> execution of all
 * tests within the class. Should be called after any framework-specific
 * <em>after class methods</em> (e.g., methods annotated with JUnit 4's
 * {@link org.junit.AfterClass @AfterClass}).
 * <p>Each registered {@link TestExecutionListener} will be given a chance
 * to perform its post-processing. If a listener throws an exception, the
 * remaining registered listeners will still be called. After all listeners
 * have executed, the first caught exception will be rethrown with any
 * subsequent exceptions {@linkplain Throwable#addSuppressed suppressed} in
 * the first exception.
 * <p>Note that registered listeners will be executed in the opposite
 * @throws Exception if a registered TestExecutionListener throws an exception
 * @since 3.0
 * @see #getTestExecutionListeners()
 * @see Throwable#addSuppressed(Throwable)
 */
public void afterTestClass() throws Exception {
	Class<?> testClass = getTestContext().getTestClass();
	if (logger.isTraceEnabled()) {
		logger.trace("afterTestClass(): class [" + testClass.getName() + "]");
	}
	getTestContext().updateState(null, null, null);

	Throwable afterTestClassException = null;
	// Traverse the TestExecutionListeners in reverse order to ensure proper
	// "wrapper"-style execution of listeners.
	for (TestExecutionListener testExecutionListener : getReversedTestExecutionListeners()) {
		try {
			testExecutionListener.afterTestClass(getTestContext());
		}
		catch (Throwable ex) {
			logException(ex, "afterTestClass", testExecutionListener, testClass);
			if (afterTestClassException == null) {
				afterTestClassException = ex;
			}
			else {
				afterTestClassException.addSuppressed(ex);
			}
		}
	}

	this.testContextHolder.remove();

	if (afterTestClassException != null) {
		ReflectionUtils.rethrowException(afterTestClassException);
	}
}
 
Example 12
/**
 * Hook for post-processing a test class <em>after</em> execution of all
 * tests within the class. Should be called after any framework-specific
 * <em>after class methods</em> (e.g., methods annotated with JUnit 4's
 * {@link org.junit.AfterClass @AfterClass}).
 * <p>Each registered {@link TestExecutionListener} will be given a chance
 * to perform its post-processing. If a listener throws an exception, the
 * remaining registered listeners will still be called. After all listeners
 * have executed, the first caught exception will be rethrown with any
 * subsequent exceptions {@linkplain Throwable#addSuppressed suppressed} in
 * the first exception.
 * <p>Note that registered listeners will be executed in the opposite
 * @throws Exception if a registered TestExecutionListener throws an exception
 * @since 3.0
 * @see #getTestExecutionListeners()
 * @see Throwable#addSuppressed(Throwable)
 */
public void afterTestClass() throws Exception {
	Class<?> testClass = getTestContext().getTestClass();
	if (logger.isTraceEnabled()) {
		logger.trace("afterTestClass(): class [" + testClass.getName() + "]");
	}
	getTestContext().updateState(null, null, null);

	Throwable afterTestClassException = null;
	// Traverse the TestExecutionListeners in reverse order to ensure proper
	// "wrapper"-style execution of listeners.
	for (TestExecutionListener testExecutionListener : getReversedTestExecutionListeners()) {
		try {
			testExecutionListener.afterTestClass(getTestContext());
		}
		catch (Throwable ex) {
			logException(ex, "afterTestClass", testExecutionListener, testClass);
			if (afterTestClassException == null) {
				afterTestClassException = ex;
			}
			else {
				afterTestClassException.addSuppressed(ex);
			}
		}
	}

	this.testContextHolder.remove();

	if (afterTestClassException != null) {
		ReflectionUtils.rethrowException(afterTestClassException);
	}
}
 
Example 13
private Object doInvokeMethod(Method method, Object target, Object[] args) throws Exception {
	ReflectionUtils.makeAccessible(method);
	try {
		return method.invoke(target, args);
	}
	catch (InvocationTargetException ex) {
		ReflectionUtils.rethrowException(ex.getTargetException());
	}
	throw new IllegalStateException("Should never get here");
}
 
Example 14
private Object doInvokeMethod(Method method, Object target, Object[] args) throws Exception {
	ReflectionUtils.makeAccessible(method);
	try {
		return method.invoke(target, args);
	}
	catch (InvocationTargetException ex) {
		ReflectionUtils.rethrowException(ex.getTargetException());
	}
	throw new IllegalStateException("Should never get here");
}
 
Example 15
private void handleBeforeException(Throwable ex, String callbackName, TestExecutionListener testExecutionListener,
		Object testInstance, Method testMethod) throws Exception {

	logException(ex, callbackName, testExecutionListener, testInstance, testMethod);
	ReflectionUtils.rethrowException(ex);
}
 
Example 16
private void handleBeforeException(Throwable ex, String callbackName, TestExecutionListener testExecutionListener,
		Object testInstance, Method testMethod) throws Exception {

	logException(ex, callbackName, testExecutionListener, testInstance, testMethod);
	ReflectionUtils.rethrowException(ex);
}
 
Example 17
/**
 * Hook for post-processing a test <em>immediately after</em> execution of
 * the {@linkplain java.lang.reflect.Method test method} in the supplied
 * {@linkplain TestContext test context} &mdash; for example, for timing
 * or logging purposes.
 * <p>This method <strong>must</strong> be called before framework-specific
 * <em>after</em> lifecycle callbacks (e.g., methods annotated with JUnit 4's
 * {@link org.junit.After @After}).
 * <p>The managed {@link TestContext} will be updated with the supplied
 * {@code testInstance}, {@code testMethod}, and {@code exception}.
 * <p>Each registered {@link TestExecutionListener} will be given a chance
 * to perform its post-processing. If a listener throws an exception, the
 * remaining registered listeners will still be called. After all listeners
 * have executed, the first caught exception will be rethrown with any
 * subsequent exceptions {@linkplain Throwable#addSuppressed suppressed} in
 * the first exception.
 * <p>Note that registered listeners will be executed in the opposite
 * order in which they were registered.
 * @param testInstance the current test instance (never {@code null})
 * @param testMethod the test method which has just been executed on the
 * test instance
 * @param exception the exception that was thrown during execution of the
 * test method or by a TestExecutionListener, or {@code null} if none
 * was thrown
 * @throws Exception if a registered TestExecutionListener throws an exception
 * @since 5.0
 * @see #beforeTestMethod
 * @see #afterTestMethod
 * @see #beforeTestExecution
 * @see #getTestExecutionListeners()
 * @see Throwable#addSuppressed(Throwable)
 */
public void afterTestExecution(Object testInstance, Method testMethod, @Nullable Throwable exception)
		throws Exception {

	String callbackName = "afterTestExecution";
	prepareForAfterCallback(callbackName, testInstance, testMethod, exception);
	Throwable afterTestExecutionException = null;

	// Traverse the TestExecutionListeners in reverse order to ensure proper
	// "wrapper"-style execution of listeners.
	for (TestExecutionListener testExecutionListener : getReversedTestExecutionListeners()) {
		try {
			testExecutionListener.afterTestExecution(getTestContext());
		}
		catch (Throwable ex) {
			logException(ex, callbackName, testExecutionListener, testInstance, testMethod);
			if (afterTestExecutionException == null) {
				afterTestExecutionException = ex;
			}
			else {
				afterTestExecutionException.addSuppressed(ex);
			}
		}
	}

	if (afterTestExecutionException != null) {
		ReflectionUtils.rethrowException(afterTestExecutionException);
	}
}
 
Example 18
/**
 * Hook for post-processing a test <em>after</em> execution of <em>after</em>
 * lifecycle callbacks of the underlying test framework &mdash; for example,
 * tearing down test fixtures, ending a transaction, etc.
 * <p>This method <strong>must</strong> be called immediately after
 * framework-specific <em>after</em> lifecycle callbacks (e.g., methods
 * annotated with JUnit 4's {@link org.junit.After @After}). For historical
 * reasons, this method is named {@code afterTestMethod}. Since the
 * introduction of {@link #afterTestExecution}, a more suitable name for
 * this method might be something like {@code afterTestTearDown} or
 * {@code afterEach}; however, it is unfortunately impossible to rename
 * this method due to backward compatibility concerns.
 * <p>The managed {@link TestContext} will be updated with the supplied
 * {@code testInstance}, {@code testMethod}, and {@code exception}.
 * <p>Each registered {@link TestExecutionListener} will be given a chance
 * to perform its post-processing. If a listener throws an exception, the
 * remaining registered listeners will still be called. After all listeners
 * have executed, the first caught exception will be rethrown with any
 * subsequent exceptions {@linkplain Throwable#addSuppressed suppressed} in
 * the first exception.
 * <p>Note that registered listeners will be executed in the opposite
 * @param testInstance the current test instance (never {@code null})
 * @param testMethod the test method which has just been executed on the
 * test instance
 * @param exception the exception that was thrown during execution of the test
 * method or by a TestExecutionListener, or {@code null} if none was thrown
 * @throws Exception if a registered TestExecutionListener throws an exception
 * @see #beforeTestMethod
 * @see #beforeTestExecution
 * @see #afterTestExecution
 * @see #getTestExecutionListeners()
 * @see Throwable#addSuppressed(Throwable)
 */
public void afterTestMethod(Object testInstance, Method testMethod, @Nullable Throwable exception)
		throws Exception {

	String callbackName = "afterTestMethod";
	prepareForAfterCallback(callbackName, testInstance, testMethod, exception);
	Throwable afterTestMethodException = null;

	// Traverse the TestExecutionListeners in reverse order to ensure proper
	// "wrapper"-style execution of listeners.
	for (TestExecutionListener testExecutionListener : getReversedTestExecutionListeners()) {
		try {
			testExecutionListener.afterTestMethod(getTestContext());
		}
		catch (Throwable ex) {
			logException(ex, callbackName, testExecutionListener, testInstance, testMethod);
			if (afterTestMethodException == null) {
				afterTestMethodException = ex;
			}
			else {
				afterTestMethodException.addSuppressed(ex);
			}
		}
	}

	if (afterTestMethodException != null) {
		ReflectionUtils.rethrowException(afterTestMethodException);
	}
}
 
Example 19
/**
 * Hook for post-processing a test <em>immediately after</em> execution of
 * the {@linkplain java.lang.reflect.Method test method} in the supplied
 * {@linkplain TestContext test context} &mdash; for example, for timing
 * or logging purposes.
 * <p>This method <strong>must</strong> be called before framework-specific
 * <em>after</em> lifecycle callbacks (e.g., methods annotated with JUnit 4's
 * {@link org.junit.After @After}).
 * <p>The managed {@link TestContext} will be updated with the supplied
 * {@code testInstance}, {@code testMethod}, and {@code exception}.
 * <p>Each registered {@link TestExecutionListener} will be given a chance
 * to perform its post-processing. If a listener throws an exception, the
 * remaining registered listeners will still be called. After all listeners
 * have executed, the first caught exception will be rethrown with any
 * subsequent exceptions {@linkplain Throwable#addSuppressed suppressed} in
 * the first exception.
 * <p>Note that registered listeners will be executed in the opposite
 * order in which they were registered.
 * @param testInstance the current test instance (never {@code null})
 * @param testMethod the test method which has just been executed on the
 * test instance
 * @param exception the exception that was thrown during execution of the
 * test method or by a TestExecutionListener, or {@code null} if none
 * was thrown
 * @throws Exception if a registered TestExecutionListener throws an exception
 * @since 5.0
 * @see #beforeTestMethod
 * @see #afterTestMethod
 * @see #beforeTestExecution
 * @see #getTestExecutionListeners()
 * @see Throwable#addSuppressed(Throwable)
 */
public void afterTestExecution(Object testInstance, Method testMethod, @Nullable Throwable exception)
		throws Exception {

	String callbackName = "afterTestExecution";
	prepareForAfterCallback(callbackName, testInstance, testMethod, exception);
	Throwable afterTestExecutionException = null;

	// Traverse the TestExecutionListeners in reverse order to ensure proper
	// "wrapper"-style execution of listeners.
	for (TestExecutionListener testExecutionListener : getReversedTestExecutionListeners()) {
		try {
			testExecutionListener.afterTestExecution(getTestContext());
		}
		catch (Throwable ex) {
			logException(ex, callbackName, testExecutionListener, testInstance, testMethod);
			if (afterTestExecutionException == null) {
				afterTestExecutionException = ex;
			}
			else {
				afterTestExecutionException.addSuppressed(ex);
			}
		}
	}

	if (afterTestExecutionException != null) {
		ReflectionUtils.rethrowException(afterTestExecutionException);
	}
}
 
Example 20
/**
 * Hook for post-processing a test <em>after</em> execution of <em>after</em>
 * lifecycle callbacks of the underlying test framework &mdash; for example,
 * tearing down test fixtures, ending a transaction, etc.
 * <p>This method <strong>must</strong> be called immediately after
 * framework-specific <em>after</em> lifecycle callbacks (e.g., methods
 * annotated with JUnit 4's {@link org.junit.After @After}). For historical
 * reasons, this method is named {@code afterTestMethod}. Since the
 * introduction of {@link #afterTestExecution}, a more suitable name for
 * this method might be something like {@code afterTestTearDown} or
 * {@code afterEach}; however, it is unfortunately impossible to rename
 * this method due to backward compatibility concerns.
 * <p>The managed {@link TestContext} will be updated with the supplied
 * {@code testInstance}, {@code testMethod}, and {@code exception}.
 * <p>Each registered {@link TestExecutionListener} will be given a chance
 * to perform its post-processing. If a listener throws an exception, the
 * remaining registered listeners will still be called. After all listeners
 * have executed, the first caught exception will be rethrown with any
 * subsequent exceptions {@linkplain Throwable#addSuppressed suppressed} in
 * the first exception.
 * <p>Note that registered listeners will be executed in the opposite
 * @param testInstance the current test instance (never {@code null})
 * @param testMethod the test method which has just been executed on the
 * test instance
 * @param exception the exception that was thrown during execution of the test
 * method or by a TestExecutionListener, or {@code null} if none was thrown
 * @throws Exception if a registered TestExecutionListener throws an exception
 * @see #beforeTestMethod
 * @see #beforeTestExecution
 * @see #afterTestExecution
 * @see #getTestExecutionListeners()
 * @see Throwable#addSuppressed(Throwable)
 */
public void afterTestMethod(Object testInstance, Method testMethod, @Nullable Throwable exception)
		throws Exception {

	String callbackName = "afterTestMethod";
	prepareForAfterCallback(callbackName, testInstance, testMethod, exception);
	Throwable afterTestMethodException = null;

	// Traverse the TestExecutionListeners in reverse order to ensure proper
	// "wrapper"-style execution of listeners.
	for (TestExecutionListener testExecutionListener : getReversedTestExecutionListeners()) {
		try {
			testExecutionListener.afterTestMethod(getTestContext());
		}
		catch (Throwable ex) {
			logException(ex, callbackName, testExecutionListener, testInstance, testMethod);
			if (afterTestMethodException == null) {
				afterTestMethodException = ex;
			}
			else {
				afterTestMethodException.addSuppressed(ex);
			}
		}
	}

	if (afterTestMethodException != null) {
		ReflectionUtils.rethrowException(afterTestMethodException);
	}
}