Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: ac2a8064b2c35e7986bef849b9364eb9593c8002 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/*******************************************************************************
 * Copyright (c) 2009, 2010 Ericsson
 *
 * All rights reserved. This program and the accompanying materials are
 * made available under the terms of the Eclipse Public License v1.0 which
 * accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Francois Chouinard - Initial API and implementation
 *******************************************************************************/

package org.eclipse.linuxtools.tmf.core.signal;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.linuxtools.internal.tmf.core.Activator;
import org.eclipse.linuxtools.internal.tmf.core.TmfCoreTracer;

/**
 * This class manages the set of signal listeners and the signals they are
 * interested in. When a signal is broadcasted, the appropriate listeners
 * signal handlers are invoked.
 *
 * @version 1.0
 * @author Francois Chouinard
 */
public class TmfSignalManager {

	// The set of event listeners and their corresponding handler methods.
	// Note: listeners could be restricted to ITmfComponents but there is no
	// harm in letting anyone use this since it is not tied to anything but
	// the signal data type.
	static private Map<Object, Method[]> fListeners = new HashMap<Object, Method[]>();
    static private Map<Object, Method[]> fVIPListeners = new HashMap<Object, Method[]>();

	// If requested, add universal signal tracer
	// TODO: Temporary solution: should be enabled/disabled dynamically
	private static boolean fTraceIsActive = false;
	private static TmfSignalTracer fSignalTracer;
	static {
		if (fTraceIsActive) {
			fSignalTracer = TmfSignalTracer.getInstance();
			register(fSignalTracer);
		}
	}

    /**
     * Register an object to the signal manager. This object can then implement
     * handler methods, marked with @TmfSignalHandler and with the expected
     * signal type as parameter.
     *
     * @param listener
     *            The object that will be notified of new signals
     */
    public static synchronized void register(Object listener) {
        Method[] methods = getSignalHandlerMethods(listener);
        if (methods.length > 0) {
            fListeners.put(listener, methods);
        }
    }

    /**
     * Register an object to the signal manager as a "VIP" listener. All VIP
     * listeners will all receive the signal before the manager moves on to the
     * lowly, non-VIP listeners.
     *
     * @param listener
     *            The object that will be notified of new signals
     */
    public static synchronized void registerVIP(Object listener) {
        Method[] methods = getSignalHandlerMethods(listener);
        if (methods.length > 0) {
            fVIPListeners.put(listener, methods);
        }
    }

    /**
     * De-register a listener object from the signal manager. This means that
     * its @TmfSignalHandler methods will no longer be called.
     *
     * @param listener
     *            The object to de-register
     */
    public static synchronized void deregister(Object listener) {
        fVIPListeners.remove(listener);
        fListeners.remove(listener);
    }

	/**
	 * Returns the list of signal handlers in the listener. Signal handler name
	 * is irrelevant; only the annotation (@TmfSignalHandler) is important.
	 *
	 * @param listener
	 * @return
	 */
	static private Method[] getSignalHandlerMethods(Object listener) {
		List<Method> handlers = new ArrayList<Method>();
		Method[] methods = listener.getClass().getMethods();
		for (Method method : methods) {
			if (method.isAnnotationPresent(TmfSignalHandler.class)) {
				handlers.add(method);
			}
		}
		return handlers.toArray(new Method[handlers.size()]);
	}

	static int fSignalId = 0;

	/**
	 * Invokes the handling methods that listens to signals of a given type.
	 *
	 * The list of handlers is built on-the-fly to allow for the dynamic
	 * creation/deletion of signal handlers. Since the number of signal
	 * handlers shouldn't be too high, this is not a big performance issue
	 * to pay for the flexibility.
	 *
	 * For synchronization purposes, the signal is bracketed by two synch signals.
	 *
	 * @param signal the signal to dispatch
	 */
	static public synchronized void dispatchSignal(TmfSignal signal) {
		int signalId = fSignalId++;
		sendSignal(new TmfStartSynchSignal(signalId));
		signal.setReference(signalId);
		sendSignal(signal);
		sendSignal(new TmfEndSynchSignal(signalId));
	}

    static private void sendSignal(TmfSignal signal) {
        sendSignal(fVIPListeners, signal);
        sendSignal(fListeners, signal);
    }

    static private void sendSignal(Map<Object, Method[]> listeners, TmfSignal signal) {

        if (TmfCoreTracer.isSignalTraced()) {
            TmfCoreTracer.traceSignal(signal, "(start)"); //$NON-NLS-1$
        }

        // Build the list of listener methods that are registered for this signal
        Class<?> signalClass = signal.getClass();
        Map<Object, List<Method>> targets = new HashMap<Object, List<Method>>();
        targets.clear();
        for (Map.Entry<Object, Method[]> entry : listeners.entrySet()) {
            List<Method> matchingMethods = new ArrayList<Method>();
            for (Method method : entry.getValue()) {
                if (method.getParameterTypes()[0].isAssignableFrom(signalClass)) {
                    matchingMethods.add(method);
                }
            }
            if (!matchingMethods.isEmpty()) {
                targets.put(entry.getKey(), matchingMethods);
            }
        }

        // Call the signal handlers
        for (Map.Entry<Object, List<Method>> entry : targets.entrySet()) {
            for (Method method : entry.getValue()) {
                try {
                    method.invoke(entry.getKey(), new Object[] { signal });
                    if (TmfCoreTracer.isSignalTraced()) {
                        Object key = entry.getKey();
                        String hash = String.format("%1$08X", entry.getKey().hashCode()); //$NON-NLS-1$
                        String target = "[" + hash + "] " + key.getClass().getSimpleName() + ":" + method.getName();   //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
                        TmfCoreTracer.traceSignal(signal, target);
                    }
                } catch (IllegalArgumentException e) {
                    Activator.logError("Exception handling signal " + signal + " in method " + method, e); //$NON-NLS-1$ //$NON-NLS-2$
                } catch (IllegalAccessException e) {
                    Activator.logError("Exception handling signal " + signal + " in method " + method, e); //$NON-NLS-1$ //$NON-NLS-2$
                } catch (InvocationTargetException e) {
                    Activator.logError("Exception handling signal " + signal + " in method " + method, e); //$NON-NLS-1$ //$NON-NLS-2$
                }
            }
        }

        if (TmfCoreTracer.isSignalTraced()) {
            TmfCoreTracer.traceSignal(signal, "(end)"); //$NON-NLS-1$
        }
    }

}

Back to the top