Introduce inbound and outbound signal blacklisting
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.core / src / org / eclipse / tracecompass / tmf / core / signal / TmfSignalManager.java
1 /*******************************************************************************
2 * Copyright (c) 2009, 2014 Ericsson
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Francois Chouinard - Initial API and implementation
11 * Bernd Hufmann - Update register methods
12 *******************************************************************************/
13
14 package org.eclipse.tracecompass.tmf.core.signal;
15
16 import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
17
18 import java.lang.reflect.InvocationTargetException;
19 import java.lang.reflect.Method;
20 import java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.concurrent.ExecutorService;
25 import java.util.concurrent.Executors;
26
27 import org.eclipse.jdt.annotation.NonNull;
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.eclipse.tracecompass.internal.tmf.core.Activator;
30 import org.eclipse.tracecompass.internal.tmf.core.TmfCoreTracer;
31
32 import com.google.common.collect.HashMultimap;
33 import com.google.common.collect.Multimap;
34
35 /**
36 * This class manages the set of signal listeners and the signals they are
37 * interested in. When a signal is broadcasted, the appropriate listeners
38 * signal handlers are invoked.
39 *
40 * @version 1.0
41 * @author Francois Chouinard
42 */
43 public class TmfSignalManager {
44
45 // The set of event listeners and their corresponding handler methods.
46 // Note: listeners could be restricted to ITmfComponents but there is no
47 // harm in letting anyone use this since it is not tied to anything but
48 // the signal data type.
49 private static Map<Object, Method[]> fListeners = new HashMap<>();
50 private static Map<Object, Method[]> fVIPListeners = new HashMap<>();
51
52 /** The outbound blacklist of pair <source, signal> */
53 private static Multimap<@NonNull Object, @NonNull Class<? extends TmfSignal>> fOutboundSignalBlacklist = HashMultimap.create();
54 /** The inbound blacklist of pair <listener, signal> */
55 private static Multimap<@NonNull Object, @NonNull Class<? extends TmfSignal>> fInboundSignalBlacklist = HashMultimap.create();
56
57 // The signal executor for asynchronous signals
58 private static final ExecutorService fExecutor = Executors.newSingleThreadExecutor();
59
60 // If requested, add universal signal tracer
61 // TODO: Temporary solution: should be enabled/disabled dynamically
62 private static boolean fTraceIsActive = false;
63 private static TmfSignalTracer fSignalTracer;
64
65 static {
66 if (fTraceIsActive) {
67 fSignalTracer = TmfSignalTracer.getInstance();
68 register(fSignalTracer);
69 }
70 }
71
72 /**
73 * Register an object to the signal manager. This object can then implement
74 * handler methods, marked with @TmfSignalHandler and with the expected
75 * signal type as parameter.
76 *
77 * @param listener
78 * The object that will be notified of new signals
79 */
80 public static synchronized void register(Object listener) {
81 deregister(listener); // make sure that listener is only registered once
82 Method[] methods = getSignalHandlerMethods(listener);
83 if (methods.length > 0) {
84 fListeners.put(listener, methods);
85 }
86 }
87
88 /**
89 * Ignore the outbound signal type from the specified source.
90 * One can ignore all signals by passing TmfSignal.class as the signal class.
91 *
92 * @param source
93 * The source object
94 * @param signal
95 * The signal class to ignore
96 * @since 2.2
97 */
98 @NonNullByDefault
99 public static synchronized void addIgnoredOutboundSignal(Object source, Class<? extends TmfSignal> signal) {
100 fOutboundSignalBlacklist.put(source, signal);
101 }
102
103 /**
104 * Ignore the inbound signal type for the specified listener.
105 * All signals can be ignored by passing TmfSignal.class.
106 *
107 * @param listener
108 * The listener object for which the signal must be ignored
109 * @param signal
110 * The signal class to ignore
111 * @since 2.2
112 */
113 @NonNullByDefault
114 public static synchronized void addIgnoredInboundSignal(Object listener, Class<? extends TmfSignal> signal) {
115 fInboundSignalBlacklist.put(listener, signal);
116 }
117
118 /**
119 * Remove the signal from the list of ignored outbound signal for the
120 * specified source if present.
121 *
122 * @param source
123 * The source object
124 * @param signal
125 * The signal class to remove from the ignore list
126 * @since 2.2
127 */
128 public static synchronized void removeIgnoredOutboundSignal(Object source, Class<? extends TmfSignal> signal) {
129 fOutboundSignalBlacklist.remove(source, signal);
130 }
131
132 /**
133 * Remove the signal from the list of inbound ignored signals for the
134 * specified listener if present.
135 *
136 * @param listener
137 * The listener object
138 * @param signal
139 * The signal class to remove from the ignore list
140 * @since 2.2
141 */
142 public static synchronized void removeIgnoredInboundSignal(Object listener, Class<? extends TmfSignal> signal) {
143 fInboundSignalBlacklist.remove(listener, signal);
144 }
145
146
147 /**
148 * Clear the list of ignored outbound signals for the source.
149 *
150 * @param source
151 * The source object
152 * @since 2.2
153 */
154 public static synchronized void clearIgnoredOutboundSignalList(Object source) {
155 fOutboundSignalBlacklist.removeAll(source);
156 }
157
158 /**
159 * Clear the list of ignored inbound signals for the listener.
160 *
161 * @param listener
162 * The listener object
163 * @since 2.2
164 */
165 public static synchronized void clearIgnoredInboundSignalList(Object listener) {
166 fInboundSignalBlacklist.removeAll(listener);
167 }
168
169 /**
170 * Register an object to the signal manager as a "VIP" listener. All VIP
171 * listeners will all receive the signal before the manager moves on to the
172 * lowly, non-VIP listeners.
173 *
174 * @param listener
175 * The object that will be notified of new signals
176 */
177 public static synchronized void registerVIP(Object listener) {
178 deregister(listener); // make sure that listener is only registered once
179 Method[] methods = getSignalHandlerMethods(listener);
180 if (methods.length > 0) {
181 fVIPListeners.put(listener, methods);
182 }
183 }
184
185 /**
186 * De-register a listener object from the signal manager. This means that
187 * its @TmfSignalHandler methods will no longer be called.
188 *
189 * @param listener
190 * The object to de-register
191 */
192 public static synchronized void deregister(Object listener) {
193 fVIPListeners.remove(listener);
194 fListeners.remove(listener);
195 fInboundSignalBlacklist.removeAll(listener);
196 fOutboundSignalBlacklist.removeAll(listener);
197 }
198
199 /**
200 * Returns the list of signal handlers in the listener. Signal handler name
201 * is irrelevant; only the annotation (@TmfSignalHandler) is important.
202 *
203 * @param listener
204 * @return
205 */
206 private static Method[] getSignalHandlerMethods(Object listener) {
207 List<Method> handlers = new ArrayList<>();
208 Method[] methods = listener.getClass().getMethods();
209 for (Method method : methods) {
210 if (method.isAnnotationPresent(TmfSignalHandler.class)) {
211 handlers.add(method);
212 }
213 }
214 return handlers.toArray(new Method[handlers.size()]);
215 }
216
217 static int fSignalId = 0;
218
219 /**
220 * Invokes the handling methods that listens to signals of a given type in
221 * the current thread.
222 *
223 * The list of handlers is built on-the-fly to allow for the dynamic
224 * creation/deletion of signal handlers. Since the number of signal handlers
225 * shouldn't be too high, this is not a big performance issue to pay for the
226 * flexibility.
227 *
228 * For synchronization purposes, the signal is bracketed by two synch
229 * signals.
230 *
231 * @param signal
232 * the signal to dispatch
233 */
234 public static synchronized void dispatchSignal(TmfSignal signal) {
235
236 /* Check if the source,signal tuple is blacklisted */
237 Object source = signal.getSource();
238 if (source != null) {
239 boolean isBlackListed = fOutboundSignalBlacklist.get(source).stream()
240 .anyMatch(x -> x.isAssignableFrom(signal.getClass()));
241 if (isBlackListed) {
242 return;
243 }
244 }
245
246 int signalId = fSignalId++;
247 sendSignal(new TmfStartSynchSignal(signalId));
248 signal.setReference(signalId);
249 sendSignal(signal);
250 sendSignal(new TmfEndSynchSignal(signalId));
251 }
252
253 /**
254 * Invokes the handling methods that listens to signals of a given type
255 * in a separate thread which will call
256 * {@link TmfSignalManager#dispatchSignal(TmfSignal)}.
257 *
258 * If a signal is already processed the signal will be queued and
259 * dispatched after the ongoing signal finishes.
260 *
261 * @param signal
262 * the signal to dispatch
263 */
264 public static void dispatchSignalAsync(final TmfSignal signal) {
265 if (!fExecutor.isShutdown()) {
266 fExecutor.execute(new Runnable() {
267 @Override
268 public void run() {
269 dispatchSignal(signal);
270 }
271 });
272 }
273 }
274
275 /**
276 * Disposes the signal manager
277 */
278 public static void dispose() {
279 fExecutor.shutdown();
280 }
281
282 private static void sendSignal(TmfSignal signal) {
283 sendSignal(fVIPListeners, signal);
284 sendSignal(fListeners, signal);
285 }
286
287 private static void sendSignal(Map<Object, Method[]> listeners, TmfSignal signal) {
288
289 if (TmfCoreTracer.isSignalTraced()) {
290 TmfCoreTracer.traceSignal(signal, "(start)"); //$NON-NLS-1$
291 }
292
293 // Build the list of listener methods that are registered for this signal
294 Class<?> signalClass = signal.getClass();
295
296 Map<Object, List<Method>> targets = new HashMap<>();
297 targets.clear();
298 for (Map.Entry<Object, Method[]> entry : listeners.entrySet()) {
299 List<Method> matchingMethods = new ArrayList<>();
300 for (Method method : entry.getValue()) {
301 Class<?> classParam = method.getParameterTypes()[0];
302 if (classParam.isAssignableFrom(signalClass)) {
303 if (fInboundSignalBlacklist.containsKey(entry.getKey())) {
304 /* Check if any of the ignore rule apply to the signal */
305 boolean isBlackListed = fInboundSignalBlacklist.get(checkNotNull(entry.getKey())).stream()
306 .anyMatch( x -> x.isAssignableFrom(classParam));
307 if (isBlackListed) {
308 continue;
309 }
310 }
311 /* No rules apply add it */
312 matchingMethods.add(method);
313 }
314 }
315 if (!matchingMethods.isEmpty()) {
316 targets.put(entry.getKey(), matchingMethods);
317 }
318 }
319
320 // Call the signal handlers
321 for (Map.Entry<Object, List<Method>> entry : targets.entrySet()) {
322 for (Method method : entry.getValue()) {
323 try {
324 method.invoke(entry.getKey(), new Object[] { signal });
325 if (TmfCoreTracer.isSignalTraced()) {
326 Object key = entry.getKey();
327 String hash = String.format("%1$08X", entry.getKey().hashCode()); //$NON-NLS-1$
328 String target = "[" + hash + "] " + key.getClass().getSimpleName() + ":" + method.getName(); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
329 TmfCoreTracer.traceSignal(signal, target);
330 }
331 } catch (IllegalArgumentException e) {
332 Activator.logError("Exception handling signal " + signal + " in method " + method, e); //$NON-NLS-1$ //$NON-NLS-2$
333 } catch (IllegalAccessException e) {
334 Activator.logError("Exception handling signal " + signal + " in method " + method, e); //$NON-NLS-1$ //$NON-NLS-2$
335 } catch (InvocationTargetException e) {
336 Activator.logError("Exception handling signal " + signal + " in method " + method, e); //$NON-NLS-1$ //$NON-NLS-2$
337 }
338 }
339 }
340
341 if (TmfCoreTracer.isSignalTraced()) {
342 TmfCoreTracer.traceSignal(signal, "(end)"); //$NON-NLS-1$
343 }
344 }
345
346 }
This page took 0.045575 seconds and 6 git commands to generate.