1 package com.atlassian.event.internal;
2
3 import com.atlassian.event.api.EventPublisher;
4 import com.atlassian.event.config.ListenerHandlersConfiguration;
5 import com.atlassian.event.spi.EventDispatcher;
6 import com.atlassian.event.spi.ListenerHandler;
7 import com.atlassian.event.spi.ListenerInvoker;
8 import com.atlassian.plugin.eventlistener.descriptors.EventListenerModuleDescriptor;
9 import com.atlassian.plugin.scope.EverythingIsActiveScopeManager;
10 import com.atlassian.plugin.scope.ScopeManager;
11 import com.google.common.collect.Lists;
12 import com.google.common.collect.Maps;
13 import com.google.common.collect.Multimap;
14 import com.google.common.collect.Multimaps;
15 import com.google.common.collect.Sets;
16 import org.apache.commons.lang.builder.EqualsBuilder;
17 import org.apache.commons.lang.builder.HashCodeBuilder;
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
20
21 import java.util.Collection;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Optional;
26 import java.util.Set;
27 import java.util.stream.Collectors;
28
29 import static com.google.common.base.Preconditions.checkArgument;
30 import static com.google.common.base.Preconditions.checkNotNull;
31 import static com.google.common.collect.Sets.newLinkedHashSet;
32 import static org.apache.commons.lang.ObjectUtils.identityToString;
33 import static org.apache.commons.lang.StringUtils.isNotEmpty;
34
35
36
37
38
39
40
41
42
43
44
45
46
47 public final class EventPublisherImpl implements EventPublisher {
48 private static final Logger log = LoggerFactory.getLogger(EventPublisherImpl.class);
49
50 private static final String PROPERTY_PREFIX = EventPublisherImpl.class.getName();
51 private static final Optional<String> debugRegistration =
52 Optional.ofNullable(System.getProperty(PROPERTY_PREFIX + ".debugRegistration"));
53 private static final boolean debugRegistrationLocation =
54 Boolean.getBoolean(PROPERTY_PREFIX + ".debugRegistrationLocation");
55 private static final Optional<String> debugInvocation =
56 Optional.ofNullable(System.getProperty(PROPERTY_PREFIX + ".debugInvocation"));
57 private static final boolean debugInvocationLocation =
58 Boolean.getBoolean(PROPERTY_PREFIX + ".debugInvocationLocation");
59
60 private final EventDispatcher eventDispatcher;
61 private final List<ListenerHandler> listenerHandlers;
62 private final ScopeManager scopeManager;
63
64
65
66
67 private final Multimap<Class<?>, KeyedListenerInvoker> listenerInvokers;
68
69
70
71
72
73
74
75
76
77
78
79
80 public EventPublisherImpl(EventDispatcher eventDispatcher, ListenerHandlersConfiguration listenerHandlersConfiguration) {
81 this(eventDispatcher, listenerHandlersConfiguration, new EverythingIsActiveScopeManager());
82 }
83
84
85
86
87
88
89
90
91
92
93
94
95 public EventPublisherImpl(EventDispatcher eventDispatcher,
96 ListenerHandlersConfiguration listenerHandlersConfiguration,
97 ScopeManager scopeManager) {
98 this.eventDispatcher = checkNotNull(eventDispatcher);
99 this.listenerHandlers = checkNotNull(checkNotNull(listenerHandlersConfiguration).getListenerHandlers());
100 this.listenerInvokers = newMultimap();
101 this.scopeManager = checkNotNull(scopeManager);
102 }
103
104
105
106
107
108
109
110
111
112
113 public void publish(Object event) {
114 invokeListeners(findListenerInvokersForEvent(checkNotNull(event)), event);
115 }
116
117 public void register(final Object listener) {
118 registerListener(identityToString(checkNotNull(listener)), listener);
119 }
120
121 public void unregister(Object listener) {
122 unregisterListener(identityToString(checkNotNull(listener)));
123 }
124
125 public void unregisterAll() {
126 synchronized (listenerInvokers) {
127 listenerInvokers.clear();
128 }
129 }
130
131 private void unregisterListener(String listenerKey) {
132 checkArgument(isNotEmpty(listenerKey), "Key for the listener must not be empty");
133
134
135 synchronized (listenerInvokers) {
136 for (Iterator<Map.Entry<Class<?>, KeyedListenerInvoker>> invokerIterator = listenerInvokers.entries().iterator(); invokerIterator.hasNext(); ) {
137 if (invokerIterator.next().getValue().getKey().equals(listenerKey)) {
138 invokerIterator.remove();
139 }
140 }
141 }
142 }
143
144 private void registerListener(String listenerKey, Object listener) {
145 final Object listenerImpl;
146 final Optional<String> parentScope;
147 if (listener instanceof EventListenerModuleDescriptor) {
148 final EventListenerModuleDescriptor descriptor = (EventListenerModuleDescriptor) listener;
149 listenerImpl = descriptor.getModule();
150 parentScope = descriptor.getScopeKey();
151 } else {
152 listenerImpl = listener;
153 parentScope = Optional.empty();
154 }
155
156 synchronized (listenerInvokers) {
157 unregisterListener(listenerKey);
158
159 final List<ListenerInvoker> invokers = Lists.newArrayList();
160 for (ListenerHandler listenerHandler : listenerHandlers) {
161 invokers.addAll(listenerHandler.getInvokers(listenerImpl));
162 }
163 if (!invokers.isEmpty()) {
164 registerListenerInvokers(listenerKey, parentScope, invokers);
165 } else {
166 throw new IllegalArgumentException("No listener invokers were found for listener <" + listenerImpl + ">");
167 }
168 }
169 }
170
171 private Set<KeyedListenerInvoker> findListenerInvokersForEvent(Object event) {
172 final Set<KeyedListenerInvoker> invokers = newLinkedHashSet();
173
174 synchronized (listenerInvokers) {
175 for (Class<?> eventClass : ClassUtils.findAllTypes(checkNotNull(event).getClass())) {
176 invokers.addAll(listenerInvokers.get(eventClass));
177 }
178 }
179
180 final List<KeyedListenerInvoker> activeInvokers = invokers
181 .stream()
182 .filter(i -> i.getScope()
183 .map(s -> scopeManager.isScopeActive(s))
184 .orElse(true))
185 .collect(Collectors.toList());
186
187 return newLinkedHashSet(activeInvokers);
188 }
189
190 private void invokeListeners(Collection<KeyedListenerInvoker> listenerInvokers, Object event) {
191 final String eventClass = event.getClass().getName();
192 final boolean debugThisInvocation = debugInvocation.map(eventClass::startsWith).orElse(false);
193 for (KeyedListenerInvoker keyedInvoker : listenerInvokers) {
194 final ListenerInvoker invoker = keyedInvoker.getInvoker();
195 if (debugThisInvocation) {
196 log.warn("Listener invoked event with class '{}' -> invoker {}", eventClass, invoker);
197 if (debugInvocationLocation) {
198 log.warn("Invoked from", new Exception());
199 }
200 }
201
202 try {
203 eventDispatcher.dispatch(invoker, event);
204 } catch (Exception e) {
205 log.error("There was an exception thrown trying to dispatch event '{}' from the invoker '{}'.",
206 event, invoker, e);
207 }
208 }
209 }
210
211 private void registerListenerInvokers(String listenerKey, Optional<String> parentScope, List<? extends ListenerInvoker> invokers) {
212 for (ListenerInvoker invoker : invokers) {
213 registerListenerInvoker(listenerKey, parentScope, invoker);
214 }
215 }
216
217 private void registerListenerInvoker(String listenerKey, Optional<String> parentScope, ListenerInvoker invoker) {
218
219 if (invoker.getSupportedEventTypes().isEmpty()) {
220 putEventListenerInvoker(Object.class, listenerKey, parentScope, invoker);
221 }
222
223
224 for (Class<?> eventClass : invoker.getSupportedEventTypes()) {
225 putEventListenerInvoker(eventClass, listenerKey, parentScope, invoker);
226 }
227 }
228
229 private void putEventListenerInvoker(final Class<?> eventClass, String listenerKey, Optional<String> parentScope, ListenerInvoker invoker) {
230 debugRegistration.ifPresent(classPrefix -> {
231 if (eventClass.getName().startsWith(classPrefix)) {
232 log.warn("Listener registered event '{}' -> invoker {}", eventClass, invoker);
233 if (debugRegistrationLocation) {
234 log.warn("Registered from", new Exception());
235 }
236 }
237 });
238
239 log.debug("Registering {} with scope {}", listenerKey, parentScope);
240
241 if(parentScope.isPresent() && invoker.getScope().isPresent() && !parentScope.equals(invoker.getScope()))
242 {
243 throw new IllegalArgumentException("Listener <" + listenerKey + "> tries to override parent scope <" + parentScope.get() + "> with <" + invoker.getScope() + ">");
244 }
245
246 final Optional<String> invokerScope = parentScope.isPresent()
247 ?parentScope
248 :invoker.getScope();
249
250 listenerInvokers.put(eventClass, new KeyedListenerInvoker(listenerKey, invoker, invokerScope));
251 }
252
253 private Multimap<Class<?>, KeyedListenerInvoker> newMultimap() {
254 return Multimaps.synchronizedMultimap(
255 Multimaps.newMultimap(Maps.newHashMap(), () -> Sets.newHashSet()));
256 }
257
258 private static final class KeyedListenerInvoker {
259 private final String key;
260 private final ListenerInvoker invoker;
261 private final Optional<String> scope;
262
263 KeyedListenerInvoker(String key, ListenerInvoker invoker, Optional<String> scope) {
264 this.invoker = invoker;
265 this.key = key;
266 this.scope = scope;
267 }
268
269 String getKey() {
270 return key;
271 }
272
273 ListenerInvoker getInvoker() {
274 return invoker;
275 }
276
277 Optional<String> getScope() {
278 return scope;
279 }
280
281 @Override
282 public int hashCode() {
283 return new HashCodeBuilder(5, 23).append(key).append(invoker).toHashCode();
284 }
285
286 @Override
287 public boolean equals(Object obj) {
288 if (obj == this) {
289 return true;
290 }
291 if (obj == null || obj.getClass() != getClass()) {
292 return false;
293 }
294 final KeyedListenerInvoker kli = (KeyedListenerInvoker) obj;
295 return new EqualsBuilder()
296 .append(key, kli.key)
297 .append(invoker, kli.invoker)
298 .append(scope, kli.scope)
299 .isEquals();
300 }
301 }
302 }