Commit | Line | Data |
---|---|---|
8c8bf09f | 1 | /******************************************************************************* |
d91063d0 | 2 | * Copyright (c) 2009, 2014 Ericsson |
6256d8ad | 3 | * |
8c8bf09f ASL |
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 | |
6256d8ad | 8 | * |
8c8bf09f ASL |
9 | * Contributors: |
10 | * Francois Chouinard - Initial API and implementation | |
fec1ac0b | 11 | * Bernd Hufmann - Update register methods |
8c8bf09f ASL |
12 | *******************************************************************************/ |
13 | ||
6c13869b | 14 | package org.eclipse.linuxtools.tmf.core.signal; |
8c8bf09f ASL |
15 | |
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; | |
21 | import java.util.Map; | |
d91063d0 BH |
22 | import java.util.concurrent.ExecutorService; |
23 | import java.util.concurrent.Executors; | |
8c8bf09f | 24 | |
8555a0f3 | 25 | import org.eclipse.linuxtools.internal.tmf.core.Activator; |
5500a7f0 | 26 | import org.eclipse.linuxtools.internal.tmf.core.TmfCoreTracer; |
dc299841 | 27 | |
8c8bf09f | 28 | /** |
8d2e2848 FC |
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. | |
6256d8ad | 32 | * |
4b7b3670 FC |
33 | * @version 1.0 |
34 | * @author Francois Chouinard | |
4f8ca6a1 | 35 | */ |
8c8bf09f ASL |
36 | public class TmfSignalManager { |
37 | ||
d9ec9800 AM |
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. | |
a4524c1b AM |
42 | private static Map<Object, Method[]> fListeners = new HashMap<>(); |
43 | private static Map<Object, Method[]> fVIPListeners = new HashMap<>(); | |
d9ec9800 | 44 | |
d91063d0 BH |
45 | // The signal executor for asynchronous signals |
46 | private static final ExecutorService fExecutor = Executors.newSingleThreadExecutor(); | |
47 | ||
d9ec9800 AM |
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; | |
52 | ||
53 | static { | |
54 | if (fTraceIsActive) { | |
55 | fSignalTracer = TmfSignalTracer.getInstance(); | |
56 | register(fSignalTracer); | |
57 | } | |
58 | } | |
8c8bf09f | 59 | |
4f8ca6a1 AM |
60 | /** |
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. | |
64 | * | |
65 | * @param listener | |
66 | * The object that will be notified of new signals | |
67 | */ | |
68 | public static synchronized void register(Object listener) { | |
fec1ac0b | 69 | deregister(listener); // make sure that listener is only registered once |
4f8ca6a1 AM |
70 | Method[] methods = getSignalHandlerMethods(listener); |
71 | if (methods.length > 0) { | |
72 | fListeners.put(listener, methods); | |
73 | } | |
74 | } | |
8c8bf09f | 75 | |
4f8ca6a1 AM |
76 | /** |
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. | |
80 | * | |
81 | * @param listener | |
82 | * The object that will be notified of new signals | |
83 | */ | |
d5c13688 | 84 | public static synchronized void registerVIP(Object listener) { |
fec1ac0b | 85 | deregister(listener); // make sure that listener is only registered once |
d5c13688 | 86 | Method[] methods = getSignalHandlerMethods(listener); |
6256d8ad | 87 | if (methods.length > 0) { |
d5c13688 | 88 | fVIPListeners.put(listener, methods); |
6256d8ad | 89 | } |
d5c13688 FC |
90 | } |
91 | ||
4f8ca6a1 AM |
92 | /** |
93 | * De-register a listener object from the signal manager. This means that | |
94 | * its @TmfSignalHandler methods will no longer be called. | |
95 | * | |
96 | * @param listener | |
97 | * The object to de-register | |
98 | */ | |
99 | public static synchronized void deregister(Object listener) { | |
100 | fVIPListeners.remove(listener); | |
d5c13688 | 101 | fListeners.remove(listener); |
4f8ca6a1 | 102 | } |
8c8bf09f | 103 | |
d9ec9800 AM |
104 | /** |
105 | * Returns the list of signal handlers in the listener. Signal handler name | |
106 | * is irrelevant; only the annotation (@TmfSignalHandler) is important. | |
107 | * | |
108 | * @param listener | |
109 | * @return | |
110 | */ | |
111 | private static Method[] getSignalHandlerMethods(Object listener) { | |
a4524c1b | 112 | List<Method> handlers = new ArrayList<>(); |
d9ec9800 AM |
113 | Method[] methods = listener.getClass().getMethods(); |
114 | for (Method method : methods) { | |
115 | if (method.isAnnotationPresent(TmfSignalHandler.class)) { | |
116 | handlers.add(method); | |
117 | } | |
118 | } | |
119 | return handlers.toArray(new Method[handlers.size()]); | |
120 | } | |
121 | ||
122 | static int fSignalId = 0; | |
123 | ||
124 | /** | |
d91063d0 BH |
125 | * Invokes the handling methods that listens to signals of a given type in |
126 | * the current thread. | |
d9ec9800 AM |
127 | * |
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 | |
131 | * flexibility. | |
132 | * | |
133 | * For synchronization purposes, the signal is bracketed by two synch | |
134 | * signals. | |
135 | * | |
136 | * @param signal | |
137 | * the signal to dispatch | |
138 | */ | |
139 | public static synchronized void dispatchSignal(TmfSignal signal) { | |
140 | int signalId = fSignalId++; | |
141 | sendSignal(new TmfStartSynchSignal(signalId)); | |
142 | signal.setReference(signalId); | |
143 | sendSignal(signal); | |
144 | sendSignal(new TmfEndSynchSignal(signalId)); | |
145 | } | |
146 | ||
d91063d0 BH |
147 | /** |
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)}. | |
151 | * | |
152 | * If a signal is already processed the signal will be queued and | |
153 | * dispatched after the ongoing signal finishes. | |
154 | * | |
155 | * @param signal | |
156 | * the signal to dispatch | |
157 | * @since 3.0 | |
158 | */ | |
159 | public static void dispatchSignalAsync(final TmfSignal signal) { | |
160 | if (!fExecutor.isShutdown()) { | |
161 | fExecutor.execute(new Runnable() { | |
162 | @Override | |
163 | public void run() { | |
164 | dispatchSignal(signal); | |
165 | } | |
166 | }); | |
167 | } | |
168 | } | |
169 | ||
170 | /** | |
171 | * Disposes the signal manager | |
172 | * @since 3.0 | |
173 | */ | |
174 | public static void dispose() { | |
175 | fExecutor.shutdown(); | |
176 | } | |
177 | ||
d9ec9800 | 178 | private static void sendSignal(TmfSignal signal) { |
d5c13688 FC |
179 | sendSignal(fVIPListeners, signal); |
180 | sendSignal(fListeners, signal); | |
181 | } | |
182 | ||
d9ec9800 | 183 | private static void sendSignal(Map<Object, Method[]> listeners, TmfSignal signal) { |
d5c13688 | 184 | |
5500a7f0 FC |
185 | if (TmfCoreTracer.isSignalTraced()) { |
186 | TmfCoreTracer.traceSignal(signal, "(start)"); //$NON-NLS-1$ | |
6256d8ad | 187 | } |
d5c13688 FC |
188 | |
189 | // Build the list of listener methods that are registered for this signal | |
190 | Class<?> signalClass = signal.getClass(); | |
a4524c1b | 191 | Map<Object, List<Method>> targets = new HashMap<>(); |
d5c13688 FC |
192 | targets.clear(); |
193 | for (Map.Entry<Object, Method[]> entry : listeners.entrySet()) { | |
a4524c1b | 194 | List<Method> matchingMethods = new ArrayList<>(); |
d5c13688 FC |
195 | for (Method method : entry.getValue()) { |
196 | if (method.getParameterTypes()[0].isAssignableFrom(signalClass)) { | |
197 | matchingMethods.add(method); | |
198 | } | |
199 | } | |
200 | if (!matchingMethods.isEmpty()) { | |
201 | targets.put(entry.getKey(), matchingMethods); | |
202 | } | |
203 | } | |
204 | ||
6256d8ad | 205 | // Call the signal handlers |
d5c13688 FC |
206 | for (Map.Entry<Object, List<Method>> entry : targets.entrySet()) { |
207 | for (Method method : entry.getValue()) { | |
208 | try { | |
209 | method.invoke(entry.getKey(), new Object[] { signal }); | |
5500a7f0 | 210 | if (TmfCoreTracer.isSignalTraced()) { |
d5c13688 FC |
211 | Object key = entry.getKey(); |
212 | String hash = String.format("%1$08X", entry.getKey().hashCode()); //$NON-NLS-1$ | |
213 | String target = "[" + hash + "] " + key.getClass().getSimpleName() + ":" + method.getName(); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ | |
5500a7f0 | 214 | TmfCoreTracer.traceSignal(signal, target); |
d5c13688 FC |
215 | } |
216 | } catch (IllegalArgumentException e) { | |
5500a7f0 | 217 | Activator.logError("Exception handling signal " + signal + " in method " + method, e); //$NON-NLS-1$ //$NON-NLS-2$ |
d5c13688 | 218 | } catch (IllegalAccessException e) { |
5500a7f0 | 219 | Activator.logError("Exception handling signal " + signal + " in method " + method, e); //$NON-NLS-1$ //$NON-NLS-2$ |
d5c13688 | 220 | } catch (InvocationTargetException e) { |
5500a7f0 | 221 | Activator.logError("Exception handling signal " + signal + " in method " + method, e); //$NON-NLS-1$ //$NON-NLS-2$ |
d5c13688 FC |
222 | } |
223 | } | |
224 | } | |
225 | ||
5500a7f0 FC |
226 | if (TmfCoreTracer.isSignalTraced()) { |
227 | TmfCoreTracer.traceSignal(signal, "(end)"); //$NON-NLS-1$ | |
6256d8ad | 228 | } |
d5c13688 | 229 | } |
dc299841 | 230 | |
dc299841 | 231 | } |