001/* 002 * Copyright 2002-2019 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.context.event; 018 019import java.util.concurrent.Executor; 020 021import org.apache.commons.logging.Log; 022import org.apache.commons.logging.LogFactory; 023 024import org.springframework.beans.factory.BeanFactory; 025import org.springframework.context.ApplicationEvent; 026import org.springframework.context.ApplicationListener; 027import org.springframework.core.ResolvableType; 028import org.springframework.lang.Nullable; 029import org.springframework.util.ErrorHandler; 030 031/** 032 * Simple implementation of the {@link ApplicationEventMulticaster} interface. 033 * 034 * <p>Multicasts all events to all registered listeners, leaving it up to 035 * the listeners to ignore events that they are not interested in. 036 * Listeners will usually perform corresponding {@code instanceof} 037 * checks on the passed-in event object. 038 * 039 * <p>By default, all listeners are invoked in the calling thread. 040 * This allows the danger of a rogue listener blocking the entire application, 041 * but adds minimal overhead. Specify an alternative task executor to have 042 * listeners executed in different threads, for example from a thread pool. 043 * 044 * @author Rod Johnson 045 * @author Juergen Hoeller 046 * @author Stephane Nicoll 047 * @see #setTaskExecutor 048 */ 049public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster { 050 051 @Nullable 052 private Executor taskExecutor; 053 054 @Nullable 055 private ErrorHandler errorHandler; 056 057 058 /** 059 * Create a new SimpleApplicationEventMulticaster. 060 */ 061 public SimpleApplicationEventMulticaster() { 062 } 063 064 /** 065 * Create a new SimpleApplicationEventMulticaster for the given BeanFactory. 066 */ 067 public SimpleApplicationEventMulticaster(BeanFactory beanFactory) { 068 setBeanFactory(beanFactory); 069 } 070 071 072 /** 073 * Set a custom executor (typically a {@link org.springframework.core.task.TaskExecutor}) 074 * to invoke each listener with. 075 * <p>Default is equivalent to {@link org.springframework.core.task.SyncTaskExecutor}, 076 * executing all listeners synchronously in the calling thread. 077 * <p>Consider specifying an asynchronous task executor here to not block the 078 * caller until all listeners have been executed. However, note that asynchronous 079 * execution will not participate in the caller's thread context (class loader, 080 * transaction association) unless the TaskExecutor explicitly supports this. 081 * @see org.springframework.core.task.SyncTaskExecutor 082 * @see org.springframework.core.task.SimpleAsyncTaskExecutor 083 */ 084 public void setTaskExecutor(@Nullable Executor taskExecutor) { 085 this.taskExecutor = taskExecutor; 086 } 087 088 /** 089 * Return the current task executor for this multicaster. 090 */ 091 @Nullable 092 protected Executor getTaskExecutor() { 093 return this.taskExecutor; 094 } 095 096 /** 097 * Set the {@link ErrorHandler} to invoke in case an exception is thrown 098 * from a listener. 099 * <p>Default is none, with a listener exception stopping the current 100 * multicast and getting propagated to the publisher of the current event. 101 * If a {@linkplain #setTaskExecutor task executor} is specified, each 102 * individual listener exception will get propagated to the executor but 103 * won't necessarily stop execution of other listeners. 104 * <p>Consider setting an {@link ErrorHandler} implementation that catches 105 * and logs exceptions (a la 106 * {@link org.springframework.scheduling.support.TaskUtils#LOG_AND_SUPPRESS_ERROR_HANDLER}) 107 * or an implementation that logs exceptions while nevertheless propagating them 108 * (e.g. {@link org.springframework.scheduling.support.TaskUtils#LOG_AND_PROPAGATE_ERROR_HANDLER}). 109 * @since 4.1 110 */ 111 public void setErrorHandler(@Nullable ErrorHandler errorHandler) { 112 this.errorHandler = errorHandler; 113 } 114 115 /** 116 * Return the current error handler for this multicaster. 117 * @since 4.1 118 */ 119 @Nullable 120 protected ErrorHandler getErrorHandler() { 121 return this.errorHandler; 122 } 123 124 125 @Override 126 public void multicastEvent(ApplicationEvent event) { 127 multicastEvent(event, resolveDefaultEventType(event)); 128 } 129 130 @Override 131 public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { 132 ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); 133 Executor executor = getTaskExecutor(); 134 for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { 135 if (executor != null) { 136 executor.execute(() -> invokeListener(listener, event)); 137 } 138 else { 139 invokeListener(listener, event); 140 } 141 } 142 } 143 144 private ResolvableType resolveDefaultEventType(ApplicationEvent event) { 145 return ResolvableType.forInstance(event); 146 } 147 148 /** 149 * Invoke the given listener with the given event. 150 * @param listener the ApplicationListener to invoke 151 * @param event the current event to propagate 152 * @since 4.1 153 */ 154 protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) { 155 ErrorHandler errorHandler = getErrorHandler(); 156 if (errorHandler != null) { 157 try { 158 doInvokeListener(listener, event); 159 } 160 catch (Throwable err) { 161 errorHandler.handleError(err); 162 } 163 } 164 else { 165 doInvokeListener(listener, event); 166 } 167 } 168 169 @SuppressWarnings({"rawtypes", "unchecked"}) 170 private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { 171 try { 172 listener.onApplicationEvent(event); 173 } 174 catch (ClassCastException ex) { 175 String msg = ex.getMessage(); 176 if (msg == null || matchesClassCastMessage(msg, event.getClass())) { 177 // Possibly a lambda-defined listener which we could not resolve the generic event type for 178 // -> let's suppress the exception and just log a debug message. 179 Log logger = LogFactory.getLog(getClass()); 180 if (logger.isTraceEnabled()) { 181 logger.trace("Non-matching event type for listener: " + listener, ex); 182 } 183 } 184 else { 185 throw ex; 186 } 187 } 188 } 189 190 private boolean matchesClassCastMessage(String classCastMessage, Class<?> eventClass) { 191 // On Java 8, the message starts with the class name: "java.lang.String cannot be cast..." 192 if (classCastMessage.startsWith(eventClass.getName())) { 193 return true; 194 } 195 // On Java 11, the message starts with "class ..." a.k.a. Class.toString() 196 if (classCastMessage.startsWith(eventClass.toString())) { 197 return true; 198 } 199 // On Java 9, the message used to contain the module name: "java.base/java.lang.String cannot be cast..." 200 int moduleSeparatorIndex = classCastMessage.indexOf('/'); 201 if (moduleSeparatorIndex != -1 && classCastMessage.startsWith(eventClass.getName(), moduleSeparatorIndex + 1)) { 202 return true; 203 } 204 // Assuming an unrelated class cast failure... 205 return false; 206 } 207 208}