1 |
| |
2 |
| |
3 |
| |
4 |
| |
5 |
| |
6 |
| |
7 |
| |
8 |
| |
9 |
| |
10 |
| |
11 |
| |
12 |
| |
13 |
| |
14 |
| |
15 |
| |
16 |
| |
17 |
| |
18 |
| |
19 |
| |
20 |
| |
21 |
| |
22 |
| |
23 |
| package org.jboss.cache.pojo.impl; |
24 |
| |
25 |
| import java.lang.annotation.Annotation; |
26 |
| import java.lang.reflect.Method; |
27 |
| import java.lang.reflect.Modifier; |
28 |
| import java.util.ArrayList; |
29 |
| import java.util.Collections; |
30 |
| import java.util.HashMap; |
31 |
| import java.util.HashSet; |
32 |
| import java.util.List; |
33 |
| import java.util.Map; |
34 |
| import java.util.Set; |
35 |
| import java.util.concurrent.CopyOnWriteArraySet; |
36 |
| import java.util.regex.Pattern; |
37 |
| |
38 |
| import org.jboss.cache.Fqn; |
39 |
| import org.jboss.cache.pojo.PojoCacheException; |
40 |
| import org.jboss.cache.pojo.notification.annotation.ArrayModified; |
41 |
| import org.jboss.cache.pojo.notification.annotation.Attached; |
42 |
| import org.jboss.cache.pojo.notification.annotation.Detached; |
43 |
| import org.jboss.cache.pojo.notification.annotation.FieldModified; |
44 |
| import org.jboss.cache.pojo.notification.annotation.ListModified; |
45 |
| import org.jboss.cache.pojo.notification.annotation.MapModified; |
46 |
| import org.jboss.cache.pojo.notification.annotation.PojoCacheListener; |
47 |
| import org.jboss.cache.pojo.notification.annotation.SetModified; |
48 |
| import org.jboss.cache.pojo.notification.annotation.TransactionCompleted; |
49 |
| import org.jboss.cache.pojo.notification.annotation.TransactionRegistered; |
50 |
| import org.jboss.cache.pojo.notification.event.ArrayModifiedEvent; |
51 |
| import org.jboss.cache.pojo.notification.event.AttachedEvent; |
52 |
| import org.jboss.cache.pojo.notification.event.DetachedEvent; |
53 |
| import org.jboss.cache.pojo.notification.event.Event; |
54 |
| import org.jboss.cache.pojo.notification.event.FieldModifiedEvent; |
55 |
| import org.jboss.cache.pojo.notification.event.ListModifiedEvent; |
56 |
| import org.jboss.cache.pojo.notification.event.MapModifiedEvent; |
57 |
| import org.jboss.cache.pojo.notification.event.SetModifiedEvent; |
58 |
| import org.jboss.cache.pojo.notification.event.TransactionCompletedEvent; |
59 |
| import org.jboss.cache.pojo.notification.event.TransactionRegisteredEvent; |
60 |
| |
61 |
| |
62 |
| |
63 |
| |
64 |
| |
65 |
| |
66 |
| |
67 |
| |
68 |
| |
69 |
| class NotificationDispatcher |
70 |
| { |
71 |
| private final static Map<Class<? extends Annotation>, Class<? extends Event>> annotations = new HashMap<Class<? extends Annotation>, Class<? extends Event>>(); |
72 |
| private final Set<Entry> listeners = new CopyOnWriteArraySet<Entry>(); |
73 |
| private Set<Object> filteredListeners = new HashSet<Object>(); |
74 |
| private volatile boolean hasFilters; |
75 |
| |
76 |
| static |
77 |
| { |
78 |
77
| annotations.put(Attached.class, AttachedEvent.class);
|
79 |
77
| annotations.put(Detached.class, DetachedEvent.class);
|
80 |
77
| annotations.put(FieldModified.class, FieldModifiedEvent.class);
|
81 |
77
| annotations.put(ListModified.class, ListModifiedEvent.class);
|
82 |
77
| annotations.put(MapModified.class, MapModifiedEvent.class);
|
83 |
77
| annotations.put(SetModified.class, SetModifiedEvent.class);
|
84 |
77
| annotations.put(ArrayModified.class, ArrayModifiedEvent.class);
|
85 |
77
| annotations.put(TransactionRegistered.class, TransactionRegisteredEvent.class);
|
86 |
77
| annotations.put(TransactionCompleted.class, TransactionCompletedEvent.class);
|
87 |
| } |
88 |
| |
89 |
| final static class Entry |
90 |
| { |
91 |
| private final Object listener; |
92 |
| private final Pattern pattern; |
93 |
| private final Map<Class<?>, List<Method>> notifiers; |
94 |
| |
95 |
50
| private Entry(Object listener, boolean build)
|
96 |
| { |
97 |
50
| this(listener, build, null);
|
98 |
| } |
99 |
| |
100 |
50
| private Entry(Object listener, boolean build, Pattern pattern)
|
101 |
| { |
102 |
50
| if (listener == null)
|
103 |
0
| throw new IllegalArgumentException("Listener can't be null");
|
104 |
| |
105 |
50
| this.listener = listener;
|
106 |
50
| this.pattern = pattern;
|
107 |
| |
108 |
50
| this.notifiers = build ? buildNotifiers(listener.getClass()) : null;
|
109 |
| } |
110 |
| |
111 |
| |
112 |
0
| public int hashCode()
|
113 |
| { |
114 |
0
| return listener.hashCode();
|
115 |
| } |
116 |
| |
117 |
| |
118 |
0
| public boolean equals(Object o)
|
119 |
| { |
120 |
0
| if (o == this)
|
121 |
0
| return true;
|
122 |
0
| if (! (o instanceof Entry))
|
123 |
0
| return false;
|
124 |
| |
125 |
0
| return ((Entry)o).equals(listener);
|
126 |
| } |
127 |
| |
128 |
50
| private static Map<Class<?>, List<Method>> buildNotifiers(Class clazz)
|
129 |
| { |
130 |
50
| if (! Modifier.isPublic(clazz.getModifiers()))
|
131 |
0
| throw new IllegalArgumentException("Listener must be public! Class:" + clazz.getName());
|
132 |
| |
133 |
50
| if (! clazz.isAnnotationPresent(PojoCacheListener.class))
|
134 |
0
| throw new IllegalArgumentException("Not a listener, class did not contain @PojoCacheListener. Class: " + clazz.getName());
|
135 |
| |
136 |
| |
137 |
50
| Map<Class<?>, List<Method>> notifiers = new HashMap<Class<?>, List<Method>>();
|
138 |
50
| for (Method method : clazz.getMethods())
|
139 |
| { |
140 |
600
| for (Annotation annotation : method.getAnnotations())
|
141 |
| { |
142 |
304
| Class<? extends Event> event = annotations.get(annotation.annotationType());
|
143 |
304
| if (event == null)
|
144 |
0
| continue;
|
145 |
| |
146 |
304
| Class<?>[] types = method.getParameterTypes();
|
147 |
304
| if (types.length != 1 || !types[0].isAssignableFrom(event))
|
148 |
| { |
149 |
0
| throw new IllegalArgumentException("Listener has invlaid method signature for annotation. " +
|
150 |
| "Method: \"" + method.getName() + "\" " + |
151 |
| "Annotation: \"" + annotation.annotationType().getSimpleName() + "\" " + |
152 |
| "Expected Parameter: \"" + event.getSimpleName() + "\""); |
153 |
| } |
154 |
| |
155 |
304
| List<Method> list = notifiers.get(event);
|
156 |
304
| if (list == null)
|
157 |
| { |
158 |
304
| list = new ArrayList<Method>();
|
159 |
304
| notifiers.put(event, list);
|
160 |
| } |
161 |
| |
162 |
304
| list.add(method);
|
163 |
| } |
164 |
| } |
165 |
| |
166 |
50
| return notifiers;
|
167 |
| } |
168 |
| } |
169 |
| |
170 |
50
| void add(Object listener)
|
171 |
| { |
172 |
50
| listeners.add(new Entry(listener, true));
|
173 |
| } |
174 |
| |
175 |
| |
176 |
0
| synchronized void add(Object listener, Pattern pattern)
|
177 |
| { |
178 |
0
| listeners.add(new Entry(listener, true, pattern));
|
179 |
0
| filteredListeners.add(listener);
|
180 |
0
| hasFilters = true;
|
181 |
| } |
182 |
| |
183 |
| |
184 |
0
| synchronized void remove(Object listener)
|
185 |
| { |
186 |
0
| filteredListeners.remove(listener);
|
187 |
0
| if (filteredListeners.size() == 0)
|
188 |
0
| hasFilters = false;
|
189 |
0
| listeners.remove(new Entry(listener, false));
|
190 |
| } |
191 |
| |
192 |
598
| boolean hasFilters()
|
193 |
| { |
194 |
598
| return hasFilters;
|
195 |
| } |
196 |
| |
197 |
0
| Set<Object> getListeners()
|
198 |
| { |
199 |
0
| Set<Object> set = new HashSet<Object>();
|
200 |
0
| for (Entry entry : listeners)
|
201 |
0
| set.add(entry.listener);
|
202 |
| |
203 |
0
| return Collections.unmodifiableSet(set);
|
204 |
| } |
205 |
| |
206 |
0
| Set<Entry> getListenerEntries(List<Fqn> fqns)
|
207 |
| { |
208 |
0
| Set<Entry> set = new HashSet<Entry>();
|
209 |
0
| for (Entry entry : listeners)
|
210 |
| { |
211 |
0
| if (entry.pattern == null)
|
212 |
| { |
213 |
0
| set.add(entry);
|
214 |
0
| continue;
|
215 |
| } |
216 |
| |
217 |
0
| for (Fqn fqn : fqns)
|
218 |
| { |
219 |
0
| if (entry.pattern.matcher(fqn.toString()).matches())
|
220 |
| { |
221 |
0
| set.add(entry);
|
222 |
0
| break;
|
223 |
| } |
224 |
| } |
225 |
| } |
226 |
| |
227 |
0
| return set;
|
228 |
| } |
229 |
| |
230 |
0
| boolean isEmpty()
|
231 |
| { |
232 |
0
| return listeners.size() == 0;
|
233 |
| } |
234 |
| |
235 |
272
| void dispatch(Event notification)
|
236 |
| { |
237 |
272
| for (Entry entry : listeners)
|
238 |
| { |
239 |
| |
240 |
272
| if (entry.pattern == null)
|
241 |
272
| dispatch(notification, entry);
|
242 |
| } |
243 |
| } |
244 |
| |
245 |
0
| void dispatch(Event notification, Set<Entry> listeners)
|
246 |
| { |
247 |
0
| for (Entry listener : listeners)
|
248 |
0
| dispatch(notification, listener);
|
249 |
| } |
250 |
| |
251 |
272
| private void dispatch(Event notification, Entry entry)
|
252 |
| { |
253 |
272
| List<Method> methods = entry.notifiers.get(notification.getClass());
|
254 |
272
| if (methods == null)
|
255 |
93
| return;
|
256 |
| |
257 |
179
| try
|
258 |
| { |
259 |
179
| for (Method method : methods)
|
260 |
179
| method.invoke(entry.listener, notification);
|
261 |
| } |
262 |
| catch (Exception e) |
263 |
| { |
264 |
0
| throw new PojoCacheException(e);
|
265 |
| } |
266 |
| } |
267 |
| } |