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 java
.lang
.reflect
.InvocationTargetException
;
17 import java
.lang
.reflect
.Method
;
18 import java
.util
.ArrayList
;
19 import java
.util
.HashMap
;
20 import java
.util
.List
;
22 import java
.util
.concurrent
.ExecutorService
;
23 import java
.util
.concurrent
.Executors
;
25 import org
.eclipse
.tracecompass
.internal
.tmf
.core
.Activator
;
26 import org
.eclipse
.tracecompass
.internal
.tmf
.core
.TmfCoreTracer
;
29 * This class manages the set of signal listeners and the signals they are
30 * interested in. When a signal is broadcasted, the appropriate listeners
31 * signal handlers are invoked.
34 * @author Francois Chouinard
36 public class TmfSignalManager
{
38 // The set of event listeners and their corresponding handler methods.
39 // Note: listeners could be restricted to ITmfComponents but there is no
40 // harm in letting anyone use this since it is not tied to anything but
41 // the signal data type.
42 private static Map
<Object
, Method
[]> fListeners
= new HashMap
<>();
43 private static Map
<Object
, Method
[]> fVIPListeners
= new HashMap
<>();
45 // The signal executor for asynchronous signals
46 private static final ExecutorService fExecutor
= Executors
.newSingleThreadExecutor();
48 // If requested, add universal signal tracer
49 // TODO: Temporary solution: should be enabled/disabled dynamically
50 private static boolean fTraceIsActive
= false;
51 private static TmfSignalTracer fSignalTracer
;
55 fSignalTracer
= TmfSignalTracer
.getInstance();
56 register(fSignalTracer
);
61 * Register an object to the signal manager. This object can then implement
62 * handler methods, marked with @TmfSignalHandler and with the expected
63 * signal type as parameter.
66 * The object that will be notified of new signals
68 public static synchronized void register(Object listener
) {
69 deregister(listener
); // make sure that listener is only registered once
70 Method
[] methods
= getSignalHandlerMethods(listener
);
71 if (methods
.length
> 0) {
72 fListeners
.put(listener
, methods
);
77 * Register an object to the signal manager as a "VIP" listener. All VIP
78 * listeners will all receive the signal before the manager moves on to the
79 * lowly, non-VIP listeners.
82 * The object that will be notified of new signals
84 public static synchronized void registerVIP(Object listener
) {
85 deregister(listener
); // make sure that listener is only registered once
86 Method
[] methods
= getSignalHandlerMethods(listener
);
87 if (methods
.length
> 0) {
88 fVIPListeners
.put(listener
, methods
);
93 * De-register a listener object from the signal manager. This means that
94 * its @TmfSignalHandler methods will no longer be called.
97 * The object to de-register
99 public static synchronized void deregister(Object listener
) {
100 fVIPListeners
.remove(listener
);
101 fListeners
.remove(listener
);
105 * Returns the list of signal handlers in the listener. Signal handler name
106 * is irrelevant; only the annotation (@TmfSignalHandler) is important.
111 private static Method
[] getSignalHandlerMethods(Object listener
) {
112 List
<Method
> handlers
= new ArrayList
<>();
113 Method
[] methods
= listener
.getClass().getMethods();
114 for (Method method
: methods
) {
115 if (method
.isAnnotationPresent(TmfSignalHandler
.class)) {
116 handlers
.add(method
);
119 return handlers
.toArray(new Method
[handlers
.size()]);
122 static int fSignalId
= 0;
125 * Invokes the handling methods that listens to signals of a given type in
126 * the current thread.
128 * The list of handlers is built on-the-fly to allow for the dynamic
129 * creation/deletion of signal handlers. Since the number of signal handlers
130 * shouldn't be too high, this is not a big performance issue to pay for the
133 * For synchronization purposes, the signal is bracketed by two synch
137 * the signal to dispatch
139 public static synchronized void dispatchSignal(TmfSignal signal
) {
140 int signalId
= fSignalId
++;
141 sendSignal(new TmfStartSynchSignal(signalId
));
142 signal
.setReference(signalId
);
144 sendSignal(new TmfEndSynchSignal(signalId
));
148 * Invokes the handling methods that listens to signals of a given type
149 * in a separate thread which will call
150 * {@link TmfSignalManager#dispatchSignal(TmfSignal)}.
152 * If a signal is already processed the signal will be queued and
153 * dispatched after the ongoing signal finishes.
156 * the signal to dispatch
158 public static void dispatchSignalAsync(final TmfSignal signal
) {
159 if (!fExecutor
.isShutdown()) {
160 fExecutor
.execute(new Runnable() {
163 dispatchSignal(signal
);
170 * Disposes the signal manager
172 public static void dispose() {
173 fExecutor
.shutdown();
176 private static void sendSignal(TmfSignal signal
) {
177 sendSignal(fVIPListeners
, signal
);
178 sendSignal(fListeners
, signal
);
181 private static void sendSignal(Map
<Object
, Method
[]> listeners
, TmfSignal signal
) {
183 if (TmfCoreTracer
.isSignalTraced()) {
184 TmfCoreTracer
.traceSignal(signal
, "(start)"); //$NON-NLS-1$
187 // Build the list of listener methods that are registered for this signal
188 Class
<?
> signalClass
= signal
.getClass();
189 Map
<Object
, List
<Method
>> targets
= new HashMap
<>();
191 for (Map
.Entry
<Object
, Method
[]> entry
: listeners
.entrySet()) {
192 List
<Method
> matchingMethods
= new ArrayList
<>();
193 for (Method method
: entry
.getValue()) {
194 if (method
.getParameterTypes()[0].isAssignableFrom(signalClass
)) {
195 matchingMethods
.add(method
);
198 if (!matchingMethods
.isEmpty()) {
199 targets
.put(entry
.getKey(), matchingMethods
);
203 // Call the signal handlers
204 for (Map
.Entry
<Object
, List
<Method
>> entry
: targets
.entrySet()) {
205 for (Method method
: entry
.getValue()) {
207 method
.invoke(entry
.getKey(), new Object
[] { signal
});
208 if (TmfCoreTracer
.isSignalTraced()) {
209 Object key
= entry
.getKey();
210 String hash
= String
.format("%1$08X", entry
.getKey().hashCode()); //$NON-NLS-1$
211 String target
= "[" + hash
+ "] " + key
.getClass().getSimpleName() + ":" + method
.getName(); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
212 TmfCoreTracer
.traceSignal(signal
, target
);
214 } catch (IllegalArgumentException e
) {
215 Activator
.logError("Exception handling signal " + signal
+ " in method " + method
, e
); //$NON-NLS-1$ //$NON-NLS-2$
216 } catch (IllegalAccessException e
) {
217 Activator
.logError("Exception handling signal " + signal
+ " in method " + method
, e
); //$NON-NLS-1$ //$NON-NLS-2$
218 } catch (InvocationTargetException e
) {
219 Activator
.logError("Exception handling signal " + signal
+ " in method " + method
, e
); //$NON-NLS-1$ //$NON-NLS-2$
224 if (TmfCoreTracer
.isSignalTraced()) {
225 TmfCoreTracer
.traceSignal(signal
, "(end)"); //$NON-NLS-1$