SimpleApplicationEventMulticaster supports a configurable ErrorHandler strategy

Issue: SPR-11551
This commit is contained in:
Juergen Hoeller
2014-04-10 23:06:25 +02:00
parent 14f9dbf70c
commit 49916bb7eb
2 changed files with 119 additions and 11 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import java.util.concurrent.Executor;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.util.ErrorHandler;
/**
* Simple implementation of the {@link ApplicationEventMulticaster} interface.
@@ -32,7 +33,7 @@ import org.springframework.context.ApplicationListener;
*
* <p>By default, all listeners are invoked in the calling thread.
* This allows the danger of a rogue listener blocking the entire application,
* but adds minimal overhead. Specify an alternative TaskExecutor to have
* but adds minimal overhead. Specify an alternative task executor to have
* listeners executed in different threads, for example from a thread pool.
*
* @author Rod Johnson
@@ -43,6 +44,8 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
private Executor taskExecutor;
private ErrorHandler errorHandler;
/**
* Create a new SimpleApplicationEventMulticaster.
@@ -59,10 +62,11 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
/**
* Set the TaskExecutor to execute application listeners with.
* <p>Default is a SyncTaskExecutor, executing the listeners synchronously
* in the calling thread.
* <p>Consider specifying an asynchronous TaskExecutor here to not block the
* Set a custom executor (typically a {@link org.springframework.core.task.TaskExecutor})
* to invoke each listener with.
* <p>Default is equivalent to {@link org.springframework.core.task.SyncTaskExecutor},
* executing all listeners synchronously in the calling thread.
* <p>Consider specifying an asynchronous task executor here to not block the
* caller until all listeners have been executed. However, note that asynchronous
* execution will not participate in the caller's thread context (class loader,
* transaction association) unless the TaskExecutor explicitly supports this.
@@ -74,30 +78,78 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
}
/**
* Return the current TaskExecutor for this multicaster.
* Return the current task executor for this multicaster.
*/
protected Executor getTaskExecutor() {
return this.taskExecutor;
}
/**
* Set the {@link ErrorHandler} to invoke in case of an exception thrown
* from a listener.
* <p>Default is none, with a listener exception stopping the current
* multicast and getting propagated to the publisher of the current event.
* In case of a {@link #setTaskExecutor task executor} specified, each
* individual listener exception will get propagated to the executor but
* won't necessarily stop execution of other listeners.
* <p>Consider setting an {@link ErrorHandler} implementation that catches
* and logs exceptions (a la
* {@link org.springframework.scheduling.support.TaskUtils#LOG_AND_SUPPRESS_ERROR_HANDLER})
* or an implementation that logs exceptions while nevertheless propagating them
* ({@link org.springframework.scheduling.support.TaskUtils#LOG_AND_PROPAGATE_ERROR_HANDLER}).
* @since 4.1
*/
public void setErrorHandler(ErrorHandler errorHandler) {
this.errorHandler = errorHandler;
}
/**
* Return the current error handler for this multicaster.
* @since 4.1
*/
protected ErrorHandler getErrorHandler() {
return this.errorHandler;
}
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public void multicastEvent(final ApplicationEvent event) {
for (final ApplicationListener listener : getApplicationListeners(event)) {
for (final ApplicationListener<?> listener : getApplicationListeners(event)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(new Runnable() {
@Override
public void run() {
listener.onApplicationEvent(event);
invokeListener(listener, event);
}
});
}
else {
listener.onApplicationEvent(event);
invokeListener(listener, event);
}
}
}
/**
* Invoke the given listener with the given event.
* @param listener the ApplicationListener to invoke
* @param event the current event to propagate
* @since 4.1
*/
@SuppressWarnings({"unchecked", "rawtypes"})
protected void invokeListener(ApplicationListener listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
listener.onApplicationEvent(event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
listener.onApplicationEvent(event);
}
}
}