1 /*******************************************************************************
2 * Copyright (c) 2009, 2014 Ericsson
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
10 * Francois Chouinard - Initial API and implementation
11 * Bernd Hufmann - Update register methods
12 *******************************************************************************/
14 package org
.eclipse
.tracecompass
.tmf
.core
.signal
;
16 import static org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
.checkNotNull
;
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
;
24 import java
.util
.concurrent
.ExecutorService
;
25 import java
.util
.concurrent
.Executors
;
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
;
32 import com
.google
.common
.collect
.HashMultimap
;
33 import com
.google
.common
.collect
.Multimap
;
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.
41 * @author Francois Chouinard
43 public class TmfSignalManager
{
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
<>();
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();
57 // The signal executor for asynchronous signals
58 private static final ExecutorService fExecutor
= Executors
.newSingleThreadExecutor();
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
;
67 fSignalTracer
= TmfSignalTracer
.getInstance();
68 register(fSignalTracer
);
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.
78 * The object that will be notified of new signals
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
);
89 * Ignore the outbound signal type from the specified source.
90 * One can ignore all signals by passing TmfSignal.class as the signal class.
95 * The signal class to ignore
99 public static synchronized void addIgnoredOutboundSignal(Object source
, Class
<?
extends TmfSignal
> signal
) {
100 fOutboundSignalBlacklist
.put(source
, signal
);
104 * Ignore the inbound signal type for the specified listener.
105 * All signals can be ignored by passing TmfSignal.class.
108 * The listener object for which the signal must be ignored
110 * The signal class to ignore
114 public static synchronized void addIgnoredInboundSignal(Object listener
, Class
<?
extends TmfSignal
> signal
) {
115 fInboundSignalBlacklist
.put(listener
, signal
);
119 * Remove the signal from the list of ignored outbound signal for the
120 * specified source if present.
125 * The signal class to remove from the ignore list
128 public static synchronized void removeIgnoredOutboundSignal(Object source
, Class
<?
extends TmfSignal
> signal
) {
129 fOutboundSignalBlacklist
.remove(source
, signal
);
133 * Remove the signal from the list of inbound ignored signals for the
134 * specified listener if present.
137 * The listener object
139 * The signal class to remove from the ignore list
142 public static synchronized void removeIgnoredInboundSignal(Object listener
, Class
<?
extends TmfSignal
> signal
) {
143 fInboundSignalBlacklist
.remove(listener
, signal
);
148 * Clear the list of ignored outbound signals for the source.
154 public static synchronized void clearIgnoredOutboundSignalList(Object source
) {
155 fOutboundSignalBlacklist
.removeAll(source
);
159 * Clear the list of ignored inbound signals for the listener.
162 * The listener object
165 public static synchronized void clearIgnoredInboundSignalList(Object listener
) {
166 fInboundSignalBlacklist
.removeAll(listener
);
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.
175 * The object that will be notified of new signals
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
);
186 * De-register a listener object from the signal manager. This means that
187 * its @TmfSignalHandler methods will no longer be called.
190 * The object to de-register
192 public static synchronized void deregister(Object listener
) {
193 fVIPListeners
.remove(listener
);
194 fListeners
.remove(listener
);
195 fInboundSignalBlacklist
.removeAll(listener
);
196 fOutboundSignalBlacklist
.removeAll(listener
);
200 * Returns the list of signal handlers in the listener. Signal handler name
201 * is irrelevant; only the annotation (@TmfSignalHandler) is important.
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
);
214 return handlers
.toArray(new Method
[handlers
.size()]);
217 static int fSignalId
= 0;
220 * Invokes the handling methods that listens to signals of a given type in
221 * the current thread.
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
228 * For synchronization purposes, the signal is bracketed by two synch
232 * the signal to dispatch
234 public static synchronized void dispatchSignal(TmfSignal signal
) {
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()));
246 int signalId
= fSignalId
++;
247 sendSignal(new TmfStartSynchSignal(signalId
));
248 signal
.setReference(signalId
);
250 sendSignal(new TmfEndSynchSignal(signalId
));
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)}.
258 * If a signal is already processed the signal will be queued and
259 * dispatched after the ongoing signal finishes.
262 * the signal to dispatch
264 public static void dispatchSignalAsync(final TmfSignal signal
) {
265 if (!fExecutor
.isShutdown()) {
266 fExecutor
.execute(new Runnable() {
269 dispatchSignal(signal
);
276 * Disposes the signal manager
278 public static void dispose() {
279 fExecutor
.shutdown();
282 private static void sendSignal(TmfSignal signal
) {
283 sendSignal(fVIPListeners
, signal
);
284 sendSignal(fListeners
, signal
);
287 private static void sendSignal(Map
<Object
, Method
[]> listeners
, TmfSignal signal
) {
289 if (TmfCoreTracer
.isSignalTraced()) {
290 TmfCoreTracer
.traceSignal(signal
, "(start)"); //$NON-NLS-1$
293 // Build the list of listener methods that are registered for this signal
294 Class
<?
> signalClass
= signal
.getClass();
296 Map
<Object
, List
<Method
>> targets
= new HashMap
<>();
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
));
311 /* No rules apply add it */
312 matchingMethods
.add(method
);
315 if (!matchingMethods
.isEmpty()) {
316 targets
.put(entry
.getKey(), matchingMethods
);
320 // Call the signal handlers
321 for (Map
.Entry
<Object
, List
<Method
>> entry
: targets
.entrySet()) {
322 for (Method method
: entry
.getValue()) {
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
);
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$
341 if (TmfCoreTracer
.isSignalTraced()) {
342 TmfCoreTracer
.traceSignal(signal
, "(end)"); //$NON-NLS-1$