diff --git a/spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java b/spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java index 6b73c9cfa4..c53475223a 100644 --- a/spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java +++ b/spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java @@ -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; * *
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. - *
Default is a SyncTaskExecutor, executing the listeners synchronously - * in the calling thread. - *
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. + *
Default is equivalent to {@link org.springframework.core.task.SyncTaskExecutor}, + * executing all listeners synchronously in the calling thread. + *
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. + *
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. + *
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);
+ }
+ }
+
}
diff --git a/spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java b/spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java
index a882ba8e9f..92e8b1cffd 100644
--- a/spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java
+++ b/spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java
@@ -18,6 +18,7 @@ package org.springframework.context.event;
import java.util.HashSet;
import java.util.Set;
+import java.util.concurrent.Executor;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.Test;
@@ -33,6 +34,7 @@ import org.springframework.context.BeanThatListens;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.core.Ordered;
+import org.springframework.scheduling.support.TaskUtils;
import org.springframework.tests.sample.beans.TestBean;
import static org.junit.Assert.*;
@@ -59,6 +61,60 @@ public class ApplicationContextEventTests {
verify(listener).onApplicationEvent(evt);
}
+ @Test
+ public void simpleApplicationEventMulticasterWithTaskExecutor() {
+ @SuppressWarnings("unchecked")
+ ApplicationListener