1 package com.atlassian.plugin.event.impl;
2
3 import com.atlassian.plugin.event.PluginEventManager;
4 import com.atlassian.plugin.util.ClassUtils;
5
6 import java.lang.reflect.Method;
7 import java.lang.reflect.InvocationTargetException;
8 import java.util.*;
9 import java.util.concurrent.atomic.AtomicBoolean;
10
11 import org.apache.commons.collections.map.LazyMap;
12 import org.apache.commons.collections.Factory;
13 import org.apache.commons.logging.Log;
14 import org.apache.commons.logging.LogFactory;
15 import org.apache.commons.lang.Validate;
16
17
18
19
20
21 public class DefaultPluginEventManager implements PluginEventManager
22 {
23 private final Map<Class,Set<Listener>> eventsToListener;
24 private static final Log log = LogFactory.getLog(DefaultPluginEventManager.class);
25 private final ListenerMethodSelector[] listenerMethodSelectors;
26
27
28
29
30 public DefaultPluginEventManager()
31 {
32 this(new ListenerMethodSelector[]{new MethodNameListenerMethodSelector(), new AnnotationListenerMethodSelector()});
33 }
34
35
36
37
38
39 public DefaultPluginEventManager(ListenerMethodSelector[] selectors)
40 {
41 this.listenerMethodSelectors = selectors;
42 eventsToListener = LazyMap.decorate(new HashMap<Class,Set<Listener>>(), new Factory() {
43 public Set<Listener> create() { return new HashSet<Listener>(); }
44 });
45 }
46
47
48
49
50
51
52 public synchronized void broadcast(Object event)
53 {
54 Validate.notNull(event, "The event to broadcast must not be null");
55 final Set<Listener> calledListeners = new HashSet<Listener>();
56 for (Class type : ClassUtils.findAllTypes(event.getClass()))
57 {
58 Set<Listener> registrations = eventsToListener.get(type);
59 for (Listener reg : registrations)
60 {
61 if (calledListeners.contains(reg))
62 continue;
63 calledListeners.add(reg);
64 reg.notify(event);
65 }
66 }
67 }
68
69
70
71
72
73
74
75
76 public synchronized void register(Object listener) throws IllegalArgumentException
77 {
78 if (listener == null)
79 throw new IllegalArgumentException("Listener cannot be null");
80
81 final AtomicBoolean listenerFound = new AtomicBoolean(false);
82 forEveryListenerMethod(listener, new ListenerMethodHandler()
83 {
84 public void handle(Object listener, Method m)
85 {
86 if (m.getParameterTypes().length != 1)
87 throw new IllegalArgumentException("Listener methods must only have one argument");
88 Set<Listener> listeners = eventsToListener.get(m.getParameterTypes()[0]);
89 listeners.add(new Listener(listener, m));
90 listenerFound.set(true);
91 }
92 });
93 if (!listenerFound.get())
94 {
95 throw new IllegalArgumentException("At least one listener method must be specified. Most likely, a listener " +
96 "method is missing the @PluginEventListener annotation.");
97 }
98 }
99
100
101
102
103
104 public synchronized void unregister(Object listener)
105 {
106 forEveryListenerMethod(listener, new ListenerMethodHandler()
107 {
108 public void handle(Object listener, Method m)
109 {
110 Set<Listener> listeners = eventsToListener.get(m.getParameterTypes()[0]);
111 listeners.remove(new Listener(listener, m));
112 }
113 });
114 }
115
116
117
118
119
120
121 void forEveryListenerMethod(Object listener, ListenerMethodHandler handler)
122 {
123 Method[] methods = listener.getClass().getMethods();
124 for (int x=0; x<methods.length; x++)
125 {
126 Method m = methods[x];
127 for (int s = 0; s<listenerMethodSelectors.length; s++)
128 {
129 ListenerMethodSelector selector = listenerMethodSelectors[s];
130 if (selector.isListenerMethod(m))
131 {
132 handler.handle(listener, m);
133 }
134 }
135 }
136 }
137
138
139
140
141
142
143
144 private static interface ListenerMethodHandler
145 {
146 void handle(Object listener, Method m);
147 }
148
149 private static class Listener
150 {
151
152 public final Object listener;
153
154 public final Method method;
155
156 public Listener(Object listener, Method method)
157 {
158 Validate.notNull(listener);
159 Validate.notNull(method);
160 this.listener = listener;
161 this.method = method;
162 }
163
164 public void notify(Object event)
165 {
166 Validate.notNull(event);
167 try
168 {
169 method.invoke(listener, event);
170 }
171 catch (IllegalAccessException e)
172 {
173 log.error("Unable to access listener method: "+method, e);
174 }
175 catch (InvocationTargetException e)
176 {
177 log.error("Exception calling listener method", e.getCause());
178 }
179 }
180
181 public boolean equals(Object o)
182 {
183 if (this == o) return true;
184 if (o == null || getClass() != o.getClass()) return false;
185
186 Listener that = (Listener) o;
187
188 if (!listener.equals(that.listener)) return false;
189 if (!method.equals(that.method)) return false;
190
191 return true;
192 }
193
194 public int hashCode()
195 {
196 int result;
197 result = listener.hashCode();
198 result = 31 * result + method.hashCode();
199 return result;
200 }
201 }
202 }