Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 63f08a6657f7e8bfa23480d7c92071a835a0f4f5 (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
189
190
191
192
193
194
195
196
197
198
/*******************************************************************************
 * Copyright (c) 2007, 2017 IBM Corporation and others.
 * 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.equinox.internal.event;

import java.security.Permission;
import java.util.*;
import org.eclipse.osgi.framework.eventmgr.EventDispatcher;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;
import org.osgi.util.tracker.ServiceTracker;

public class EventHandlerTracker extends ServiceTracker<EventHandler, EventHandlerWrapper> implements EventDispatcher<EventHandlerWrapper, Permission, Event> {

	private final LogTracker log;
	//* List<EventHandlerWrapper> of all handlers with topic of "*"
	private final List<EventHandlerWrapper> globalWildcard;
	// Map<String,List<EventHandlerWrapper>> key is topic prefix of partial wildcard
	private final Map<String, List<EventHandlerWrapper>> partialWildcard;
	// Map<String,List<EventHandlerWrapper>> key is topic name
	private final Map<String, List<EventHandlerWrapper>> topicName;

	public EventHandlerTracker(BundleContext context, LogTracker log) {
		super(context, EventHandler.class.getName(), null);
		this.log = log;
		globalWildcard = new ArrayList<EventHandlerWrapper>();
		partialWildcard = new HashMap<String, List<EventHandlerWrapper>>();
		topicName = new HashMap<String, List<EventHandlerWrapper>>();
	}

	public EventHandlerWrapper addingService(ServiceReference<EventHandler> reference) {
		EventHandlerWrapper wrapper = new EventHandlerWrapper(reference, context, log);
		synchronized (this) {
			if (wrapper.init()) {
				bucket(wrapper);
			}
		}
		return wrapper;
	}

	public void modifiedService(ServiceReference<EventHandler> reference, EventHandlerWrapper service) {
		synchronized (this) {
			unbucket(service);
			if (service.init()) {
				bucket(service);
				return;
			}
		}

		service.flush(); // needs to be called outside sync region
	}

	public void removedService(ServiceReference<EventHandler> reference, EventHandlerWrapper service) {
		synchronized (this) {
			unbucket(service);
		}
		service.flush(); // needs to be called outside sync region
	}

	/**
	 * Place the wrapper into the appropriate buckets.
	 * This is a performance optimization for event delivery.
	 * 
	 * @param wrapper The wrapper to place in buckets.
	 * @GuardedBy this
	 */
	private void bucket(EventHandlerWrapper wrapper) {
		final String[] topics = wrapper.getTopics();
		final int length = (topics == null) ? 0 : topics.length;
		for (int i = 0; i < length; i++) {
			String topic = topics[i];
			// global wildcard
			if (topic.equals("*")) { //$NON-NLS-1$
				globalWildcard.add(wrapper);
			}
			// partial wildcard
			else if (topic.endsWith("/*")) { //$NON-NLS-1$
				String key = topic.substring(0, topic.length() - 2); // Strip off "/*" from the end
				List<EventHandlerWrapper> wrappers = partialWildcard.get(key);
				if (wrappers == null) {
					wrappers = new ArrayList<EventHandlerWrapper>();
					partialWildcard.put(key, wrappers);
				}
				wrappers.add(wrapper);
			}
			// simple topic name
			else {
				List<EventHandlerWrapper> wrappers = topicName.get(topic);
				if (wrappers == null) {
					wrappers = new ArrayList<EventHandlerWrapper>();
					topicName.put(topic, wrappers);
				}
				wrappers.add(wrapper);
			}
		}
	}

	/**
	 * Remove the wrapper from the buckets.
	 * 
	 * @param wrapper The wrapper to remove from the buckets.
	 * @GuardedBy this
	 */
	private void unbucket(EventHandlerWrapper wrapper) {
		final String[] topics = wrapper.getTopics();
		final int length = (topics == null) ? 0 : topics.length;
		for (int i = 0; i < length; i++) {
			String topic = topics[i];
			// global wilcard
			if (topic.equals("*")) { //$NON-NLS-1$
				globalWildcard.remove(wrapper);
			}
			// partial wildcard
			else if (topic.endsWith("/*")) { //$NON-NLS-1$
				String key = topic.substring(0, topic.length() - 2); // Strip off "/*" from the end
				List<EventHandlerWrapper> wrappers = partialWildcard.get(key);
				if (wrappers != null) {
					wrappers.remove(wrapper);
					if (wrappers.isEmpty()) {
						partialWildcard.remove(key);
					}
				}
			}
			// simple topic name
			else {
				List<EventHandlerWrapper> wrappers = topicName.get(topic);
				if (wrappers != null) {
					wrappers.remove(wrapper);
					if (wrappers.isEmpty()) {
						topicName.remove(topic);
					}
				}
			}
		}
	}

	/**
	 * Return the set of handlers which subscribe to the event topic.
	 * A set is used to ensure a handler is not called for an event more than once.
	 * 
	 * @param topic
	 * @return a set of handlers
	 */
	public synchronized Set<EventHandlerWrapper> getHandlers(final String topic) {
		// Use a set to remove duplicates
		Set<EventHandlerWrapper> handlers = new HashSet<EventHandlerWrapper>();

		// Add the "*" handlers
		handlers.addAll(globalWildcard);

		// Add the handlers with partial matches
		if (partialWildcard.size() > 0) {
			int index = topic.lastIndexOf('/');
			while (index >= 0) {
				String subTopic = topic.substring(0, index);
				List<EventHandlerWrapper> wrappers = partialWildcard.get(subTopic);
				if (wrappers != null) {
					handlers.addAll(wrappers);
				}
				// Strip the last level from the topic. For example, org/osgi/framework becomes org/osgi.
				// Wildcard topics are inserted into the map with the "/*" stripped off.
				index = subTopic.lastIndexOf('/');
			}
		}

		// Add the handlers for matching topic names
		List<EventHandlerWrapper> wrappers = topicName.get(topic);
		if (wrappers != null) {
			handlers.addAll(wrappers);
		}

		return handlers;
	}

	/**
	 * Dispatches Event to EventHandlers
	 * 
	 * @param eventListener
	 * @param listenerObject
	 * @param eventAction
	 * @param eventObject
	 * @see org.eclipse.osgi.framework.eventmgr.EventDispatcher#dispatchEvent(java.lang.Object,
	 *      java.lang.Object, int, java.lang.Object)
	 */
	public void dispatchEvent(EventHandlerWrapper eventListener, Permission listenerObject, int eventAction, Event eventObject) {
		eventListener.handleEvent(eventObject, listenerObject);
	}
}

Back to the top