1 /*******************************************************************************
2  * Copyright (c) 2012, 2020 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.osgi.internal.framework;
15 
16 import java.security.AccessController;
17 import java.security.PrivilegedAction;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.LinkedHashMap;
22 import java.util.Map;
23 import java.util.Set;
24 import java.util.concurrent.CountDownLatch;
25 import java.util.concurrent.TimeUnit;
26 import org.eclipse.osgi.framework.eventmgr.CopyOnWriteIdentityMap;
27 import org.eclipse.osgi.framework.eventmgr.EventDispatcher;
28 import org.eclipse.osgi.framework.eventmgr.EventManager;
29 import org.eclipse.osgi.framework.eventmgr.ListenerQueue;
30 import org.eclipse.osgi.internal.debug.Debug;
31 import org.eclipse.osgi.internal.serviceregistry.HookContext;
32 import org.eclipse.osgi.internal.serviceregistry.ServiceRegistry;
33 import org.eclipse.osgi.internal.serviceregistry.ShrinkableCollection;
34 import org.osgi.framework.AdminPermission;
35 import org.osgi.framework.Bundle;
36 import org.osgi.framework.BundleContext;
37 import org.osgi.framework.BundleEvent;
38 import org.osgi.framework.BundleListener;
39 import org.osgi.framework.FrameworkEvent;
40 import org.osgi.framework.FrameworkListener;
41 import org.osgi.framework.ServiceRegistration;
42 import org.osgi.framework.SynchronousBundleListener;
43 import org.osgi.framework.hooks.bundle.CollisionHook;
44 import org.osgi.framework.hooks.bundle.EventHook;
45 
46 public class EquinoxEventPublisher {
47 	static final String eventHookName = EventHook.class.getName();
48 	static final String collisionHookName = CollisionHook.class.getName();
49 	static final int FRAMEWORK_STOPPED_MASK = (FrameworkEvent.STOPPED | FrameworkEvent.STOPPED_BOOTCLASSPATH_MODIFIED
50 			| FrameworkEvent.STOPPED_UPDATE | FrameworkEvent.STOPPED_SYSTEM_REFRESHED);
51 
52 	static final int BUNDLEEVENT = 1;
53 	static final int BUNDLEEVENTSYNC = 2;
54 	/* SERVICEEVENT(3) is now handled by ServiceRegistry */
55 	static final int FRAMEWORKEVENT = 4;
56 
57 	private final EquinoxContainer container;
58 
59 	private Object monitor = new Object();
60 	private EventManager eventManager;
61 
62 	/*
63 	 * The following maps objects keep track of event listeners
64 	 * by BundleContext.  Each element is a Map that is the set
65 	 * of event listeners for a particular BundleContext.  The max number of
66 	 * elements each of the following maps will have is the number of bundles
67 	 * installed in the Framework.
68 	 */
69 	// Map of BundleContexts for bundle's BundleListeners.
70 	private final Map<BundleContextImpl, CopyOnWriteIdentityMap<BundleListener, BundleListener>> allBundleListeners = new LinkedHashMap<>();
71 
72 	// Map of BundleContexts for bundle's SynchronousBundleListeners.
73 	private final Map<BundleContextImpl, CopyOnWriteIdentityMap<SynchronousBundleListener, SynchronousBundleListener>> allSyncBundleListeners = new LinkedHashMap<>();
74 
75 	// Map of BundleContexts for bundle's FrameworkListeners.
76 	private final Map<BundleContextImpl, CopyOnWriteIdentityMap<FrameworkListener, FrameworkListener>> allFrameworkListeners = new LinkedHashMap<>();
77 
EquinoxEventPublisher(EquinoxContainer container)78 	public EquinoxEventPublisher(EquinoxContainer container) {
79 		this.container = container;
80 	}
81 
init()82 	void init() {
83 		// create our event manager on init()
84 		resetEventManager(new EventManager("Framework Event Dispatcher: " + container.toString())); //$NON-NLS-1$
85 	}
86 
close()87 	void close() {
88 		// ensure we have flushed any events in the queue
89 		flushFrameworkEvents();
90 		// close and clear out the event manager
91 		resetEventManager(null);
92 		// make sure we clear out all the remaining listeners
93 		allBundleListeners.clear();
94 		allSyncBundleListeners.clear();
95 		allFrameworkListeners.clear();
96 	}
97 
resetEventManager(EventManager newEventManager)98 	private void resetEventManager(EventManager newEventManager) {
99 		EventManager currentEventManager;
100 		synchronized (this.monitor) {
101 			currentEventManager = eventManager;
102 			eventManager = newEventManager;
103 		}
104 		if (currentEventManager != null) {
105 			currentEventManager.close();
106 		}
107 	}
108 
newListenerQueue()109 	public <K, V, E> ListenerQueue<K, V, E> newListenerQueue() {
110 		synchronized (this.monitor) {
111 			return new ListenerQueue<>(eventManager);
112 		}
113 	}
114 
isEventManagerSet()115 	private boolean isEventManagerSet() {
116 		synchronized (this.monitor) {
117 			return eventManager != null;
118 		}
119 	}
120 
121 	/**
122 	 * Deliver a BundleEvent to SynchronousBundleListeners (synchronous) and
123 	 * BundleListeners (asynchronous).
124 	 *
125 	 * @param type
126 	 *            BundleEvent type.
127 	 * @param bundle
128 	 *            Affected bundle or null.
129 	 * @param origin
130 	 *            The origin of the event
131 	 */
publishBundleEvent(int type, Bundle bundle, Bundle origin)132 	public void publishBundleEvent(int type, Bundle bundle, Bundle origin) {
133 		if (origin != null) {
134 			publishBundleEvent(new BundleEvent(type, bundle, origin));
135 		} else {
136 			publishBundleEvent(new BundleEvent(type, bundle));
137 		}
138 	}
139 
publishBundleEvent(final BundleEvent event)140 	private void publishBundleEvent(final BundleEvent event) {
141 		if (System.getSecurityManager() == null) {
142 			publishBundleEventPrivileged(event);
143 		} else {
144 			AccessController.doPrivileged(new PrivilegedAction<Void>() {
145 				@Override
146 				public Void run() {
147 					publishBundleEventPrivileged(event);
148 					return null;
149 				}
150 			});
151 		}
152 	}
153 
publishBundleEventPrivileged(BundleEvent event)154 	void publishBundleEventPrivileged(BundleEvent event) {
155 		if (!isEventManagerSet()) {
156 			return;
157 		}
158 		/*
159 		 * We must collect the snapshots of the sync and async listeners
160 		 * BEFORE we dispatch the event.
161 		 */
162 		/* Collect snapshot of SynchronousBundleListeners */
163 		/* Build the listener snapshot */
164 		Map<BundleContextImpl, Set<Map.Entry<SynchronousBundleListener, SynchronousBundleListener>>> listenersSync;
165 		BundleContextImpl systemContext = null;
166 		Set<Map.Entry<SynchronousBundleListener, SynchronousBundleListener>> systemBundleListenersSync = null;
167 		synchronized (allSyncBundleListeners) {
168 			listenersSync = new LinkedHashMap<>(allSyncBundleListeners.size());
169 			for (Map.Entry<BundleContextImpl, CopyOnWriteIdentityMap<SynchronousBundleListener, SynchronousBundleListener>> entry : allSyncBundleListeners.entrySet()) {
170 				CopyOnWriteIdentityMap<SynchronousBundleListener, SynchronousBundleListener> listeners = entry.getValue();
171 				if (!listeners.isEmpty()) {
172 					Set<Map.Entry<SynchronousBundleListener, SynchronousBundleListener>> listenerEntries = listeners.entrySet();
173 					if (entry.getKey().getBundleImpl().getBundleId() == 0) {
174 						systemContext = entry.getKey();
175 						// record the snapshot; no need to create another copy
176 						// because the hooks are not exposed to this set
177 						systemBundleListenersSync = listenerEntries;
178 					}
179 					listenersSync.put(entry.getKey(), listeners.entrySet());
180 				}
181 			}
182 		}
183 		/* Collect snapshot of BundleListeners; only if the event is NOT STARTING or STOPPING or LAZY_ACTIVATION */
184 		Map<BundleContextImpl, Set<Map.Entry<BundleListener, BundleListener>>> listenersAsync = null;
185 		Set<Map.Entry<BundleListener, BundleListener>> systemBundleListenersAsync = null;
186 		if ((event.getType() & (BundleEvent.STARTING | BundleEvent.STOPPING | BundleEvent.LAZY_ACTIVATION)) == 0) {
187 			synchronized (allBundleListeners) {
188 				listenersAsync = new LinkedHashMap<>(allBundleListeners.size());
189 				for (Map.Entry<BundleContextImpl, CopyOnWriteIdentityMap<BundleListener, BundleListener>> entry : allBundleListeners.entrySet()) {
190 					CopyOnWriteIdentityMap<BundleListener, BundleListener> listeners = entry.getValue();
191 					if (!listeners.isEmpty()) {
192 						Set<Map.Entry<BundleListener, BundleListener>> listenerEntries = listeners.entrySet();
193 						if (entry.getKey().getBundleImpl().getBundleId() == 0) {
194 							systemContext = entry.getKey();
195 							// record the snapshot; no need to create another copy
196 							// because the hooks are not exposed to this set
197 							systemBundleListenersAsync = listenerEntries;
198 						}
199 						listenersAsync.put(entry.getKey(), listenerEntries);
200 					}
201 				}
202 			}
203 		}
204 
205 		/* shrink the snapshot.
206 		 * keySet returns a Collection which cannot be added to and
207 		 * removals from that collection will result in removals of the
208 		 * entry from the snapshot.
209 		 */
210 		Collection<BundleContext> shrinkable;
211 		if (listenersAsync == null) {
212 			shrinkable = asBundleContexts(listenersSync.keySet());
213 		} else {
214 			shrinkable = new ShrinkableCollection<>(asBundleContexts(listenersSync.keySet()), asBundleContexts(listenersAsync.keySet()));
215 		}
216 
217 		notifyEventHooksPrivileged(event, shrinkable);
218 
219 		// always add back the system bundle listeners if they were removed
220 		if (systemBundleListenersSync != null && !listenersSync.containsKey(systemContext)) {
221 			listenersSync.put(systemContext, systemBundleListenersSync);
222 		}
223 		if (systemBundleListenersAsync != null && !listenersAsync.containsKey(systemContext)) {
224 			listenersAsync.put(systemContext, systemBundleListenersAsync);
225 		}
226 
227 		/* Dispatch the event to the snapshot for sync listeners */
228 		if (!listenersSync.isEmpty()) {
229 			ListenerQueue<SynchronousBundleListener, SynchronousBundleListener, BundleEvent> queue = newListenerQueue();
230 			for (Map.Entry<BundleContextImpl, Set<Map.Entry<SynchronousBundleListener, SynchronousBundleListener>>> entry : listenersSync.entrySet()) {
231 				@SuppressWarnings({"rawtypes", "unchecked"})
232 				EventDispatcher<SynchronousBundleListener, SynchronousBundleListener, BundleEvent> dispatcher = (EventDispatcher) entry.getKey();
233 				Set<Map.Entry<SynchronousBundleListener, SynchronousBundleListener>> listeners = entry.getValue();
234 				queue.queueListeners(listeners, dispatcher);
235 			}
236 			queue.dispatchEventSynchronous(BUNDLEEVENTSYNC, event);
237 		}
238 
239 		/* Dispatch the event to the snapshot for async listeners */
240 		if ((listenersAsync != null) && !listenersAsync.isEmpty()) {
241 			ListenerQueue<BundleListener, BundleListener, BundleEvent> queue = newListenerQueue();
242 			for (Map.Entry<BundleContextImpl, Set<Map.Entry<BundleListener, BundleListener>>> entry : listenersAsync.entrySet()) {
243 				@SuppressWarnings({"rawtypes", "unchecked"})
244 				EventDispatcher<BundleListener, BundleListener, BundleEvent> dispatcher = (EventDispatcher) entry.getKey();
245 				Set<Map.Entry<BundleListener, BundleListener>> listeners = entry.getValue();
246 				queue.queueListeners(listeners, dispatcher);
247 			}
248 			queue.dispatchEventAsynchronous(BUNDLEEVENT, event);
249 		}
250 	}
251 
notifyEventHooksPrivileged(final BundleEvent event, final Collection<BundleContext> result)252 	private void notifyEventHooksPrivileged(final BundleEvent event, final Collection<BundleContext> result) {
253 		if (container.getConfiguration().getDebug().DEBUG_HOOKS) {
254 			Debug.println("notifyBundleEventHooks(" + event.getType() + ":" + event.getBundle() + ", " + result + " )"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
255 		}
256 
257 		ServiceRegistry serviceRegistry = container.getServiceRegistry();
258 		if (serviceRegistry != null) {
259 			serviceRegistry.notifyHooksPrivileged(new HookContext() {
260 				@Override
261 				public void call(Object hook, ServiceRegistration<?> hookRegistration) throws Exception {
262 					if (hook instanceof EventHook) {
263 						((EventHook) hook).event(event, result);
264 					}
265 				}
266 
267 				@Override
268 				public String getHookClassName() {
269 					return eventHookName;
270 				}
271 
272 				@Override
273 				public String getHookMethodName() {
274 					return "event"; //$NON-NLS-1$
275 				}
276 
277 				@Override
278 				public boolean skipRegistration(ServiceRegistration<?> hookRegistration) {
279 					return false;
280 				}
281 			});
282 		}
283 	}
284 
285 	/**
286 	 * Deliver a FrameworkEvent.
287 	 *
288 	 * @param type
289 	 *            FrameworkEvent type.
290 	 * @param bundle
291 	 *            Affected bundle or null for system bundle.
292 	 * @param throwable
293 	 *            Related exception or null.
294 	 */
publishFrameworkEvent(int type, Bundle bundle, Throwable throwable)295 	public void publishFrameworkEvent(int type, Bundle bundle, Throwable throwable) {
296 		publishFrameworkEvent(type, bundle, throwable, (FrameworkListener[]) null);
297 	}
298 
publishFrameworkEvent(int type, Bundle bundle, Throwable throwable, final FrameworkListener... listeners)299 	public void publishFrameworkEvent(int type, Bundle bundle, Throwable throwable, final FrameworkListener... listeners) {
300 		if (bundle == null)
301 			bundle = container.getStorage().getModuleContainer().getModule(0).getBundle();
302 		final FrameworkEvent event = new FrameworkEvent(type, bundle, throwable);
303 		if (System.getSecurityManager() == null) {
304 			publishFrameworkEventPrivileged(event, listeners);
305 		} else {
306 			AccessController.doPrivileged(new PrivilegedAction<Void>() {
307 				@Override
308 				public Void run() {
309 					publishFrameworkEventPrivileged(event, listeners);
310 					return null;
311 				}
312 			});
313 		}
314 	}
315 
publishFrameworkEventPrivileged(FrameworkEvent event, FrameworkListener... callerListeners)316 	public void publishFrameworkEventPrivileged(FrameworkEvent event, FrameworkListener... callerListeners) {
317 		if (!isEventManagerSet()) {
318 			return;
319 		}
320 		// Build the listener snapshot
321 		Map<BundleContextImpl, Set<Map.Entry<FrameworkListener, FrameworkListener>>> listenerSnapshot;
322 		synchronized (allFrameworkListeners) {
323 			listenerSnapshot = new LinkedHashMap<>(allFrameworkListeners.size());
324 			for (Map.Entry<BundleContextImpl, CopyOnWriteIdentityMap<FrameworkListener, FrameworkListener>> entry : allFrameworkListeners.entrySet()) {
325 				CopyOnWriteIdentityMap<FrameworkListener, FrameworkListener> listeners = entry.getValue();
326 				if (!listeners.isEmpty()) {
327 					listenerSnapshot.put(entry.getKey(), listeners.entrySet());
328 				}
329 			}
330 		}
331 		// If framework event hook were defined they would be called here
332 
333 		// deliver the event to the snapshot
334 		ListenerQueue<FrameworkListener, FrameworkListener, FrameworkEvent> queue = newListenerQueue();
335 
336 		// add the listeners specified by the caller first
337 		if (callerListeners != null && callerListeners.length > 0) {
338 			Map<FrameworkListener, FrameworkListener> listeners = new HashMap<>();
339 			for (FrameworkListener listener : callerListeners) {
340 				if (listener != null)
341 					listeners.put(listener, listener);
342 			}
343 			// We use the system bundle context as the dispatcher
344 			if (listeners.size() > 0) {
345 				BundleContextImpl systemContext = (BundleContextImpl) container.getStorage().getModuleContainer().getModule(0).getBundle().getBundleContext();
346 				@SuppressWarnings({"rawtypes", "unchecked"})
347 				EventDispatcher<FrameworkListener, FrameworkListener, FrameworkEvent> dispatcher = (EventDispatcher) systemContext;
348 				queue.queueListeners(listeners.entrySet(), dispatcher);
349 			}
350 		}
351 
352 		for (Map.Entry<BundleContextImpl, Set<Map.Entry<FrameworkListener, FrameworkListener>>> entry : listenerSnapshot.entrySet()) {
353 			@SuppressWarnings({"rawtypes", "unchecked"})
354 			EventDispatcher<FrameworkListener, FrameworkListener, FrameworkEvent> dispatcher = (EventDispatcher) entry.getKey();
355 			Set<Map.Entry<FrameworkListener, FrameworkListener>> listeners = entry.getValue();
356 			queue.queueListeners(listeners, dispatcher);
357 		}
358 
359 		queue.dispatchEventAsynchronous(FRAMEWORKEVENT, event);
360 		// close down the publisher if we got the stopped event
361 		if ((event.getType() & FRAMEWORK_STOPPED_MASK) != 0) {
362 			close();
363 		}
364 	}
365 
366 	/**
367 	 * Coerce the generic type of a collection from Collection<BundleContextImpl>
368 	 * to Collection<BundleContext>
369 	 * @param c Collection to be coerced.
370 	 * @return c coerced to Collection<BundleContext>
371 	 */
372 	@SuppressWarnings("unchecked")
asBundleContexts(Collection<? extends BundleContext> c)373 	public static Collection<BundleContext> asBundleContexts(Collection<? extends BundleContext> c) {
374 		return (Collection<BundleContext>) c;
375 	}
376 
addBundleListener(BundleListener listener, BundleContextImpl context)377 	void addBundleListener(BundleListener listener, BundleContextImpl context) {
378 		if (listener instanceof SynchronousBundleListener) {
379 			container.checkAdminPermission(context.getBundle(), AdminPermission.LISTENER);
380 			synchronized (allSyncBundleListeners) {
381 				CopyOnWriteIdentityMap<SynchronousBundleListener, SynchronousBundleListener> listeners = allSyncBundleListeners.get(context);
382 				if (listeners == null) {
383 					listeners = new CopyOnWriteIdentityMap<>();
384 					allSyncBundleListeners.put(context, listeners);
385 				}
386 				listeners.put((SynchronousBundleListener) listener, (SynchronousBundleListener) listener);
387 			}
388 		} else {
389 			synchronized (allBundleListeners) {
390 				CopyOnWriteIdentityMap<BundleListener, BundleListener> listeners = allBundleListeners.get(context);
391 				if (listeners == null) {
392 					listeners = new CopyOnWriteIdentityMap<>();
393 					allBundleListeners.put(context, listeners);
394 				}
395 				listeners.put(listener, listener);
396 			}
397 		}
398 	}
399 
removeBundleListener(BundleListener listener, BundleContextImpl context)400 	void removeBundleListener(BundleListener listener, BundleContextImpl context) {
401 		if (listener instanceof SynchronousBundleListener) {
402 			container.checkAdminPermission(context.getBundle(), AdminPermission.LISTENER);
403 			synchronized (allSyncBundleListeners) {
404 				CopyOnWriteIdentityMap<SynchronousBundleListener, SynchronousBundleListener> listeners = allSyncBundleListeners.get(context);
405 				if (listeners != null)
406 					listeners.remove(listener);
407 			}
408 		} else {
409 			synchronized (allBundleListeners) {
410 				CopyOnWriteIdentityMap<BundleListener, BundleListener> listeners = allBundleListeners.get(context);
411 				if (listeners != null)
412 					listeners.remove(listener);
413 			}
414 		}
415 	}
416 
addFrameworkListener(FrameworkListener listener, BundleContextImpl context)417 	void addFrameworkListener(FrameworkListener listener, BundleContextImpl context) {
418 		synchronized (allFrameworkListeners) {
419 			CopyOnWriteIdentityMap<FrameworkListener, FrameworkListener> listeners = allFrameworkListeners.get(context);
420 			if (listeners == null) {
421 				listeners = new CopyOnWriteIdentityMap<>();
422 				allFrameworkListeners.put(context, listeners);
423 			}
424 			listeners.put(listener, listener);
425 		}
426 	}
427 
removeFrameworkListener(FrameworkListener listener, BundleContextImpl context)428 	void removeFrameworkListener(FrameworkListener listener, BundleContextImpl context) {
429 		synchronized (allFrameworkListeners) {
430 			CopyOnWriteIdentityMap<FrameworkListener, FrameworkListener> listeners = allFrameworkListeners.get(context);
431 			if (listeners != null)
432 				listeners.remove(listener);
433 		}
434 	}
435 
removeAllListeners(BundleContextImpl context)436 	void removeAllListeners(BundleContextImpl context) {
437 		// leave any left over listeners until the framework STOPPED event
438 		if (context.getBundleImpl().getBundleId() != 0) {
439 			synchronized (allBundleListeners) {
440 				allBundleListeners.remove(context);
441 			}
442 			synchronized (allSyncBundleListeners) {
443 				allSyncBundleListeners.remove(context);
444 			}
445 		}
446 		synchronized (allFrameworkListeners) {
447 			allFrameworkListeners.remove(context);
448 		}
449 	}
450 
flushFrameworkEvents()451 	void flushFrameworkEvents() {
452 		EventDispatcher<Object, Object, CountDownLatch> dispatcher = new EventDispatcher<Object, Object, CountDownLatch>() {
453 			@Override
454 			public void dispatchEvent(Object eventListener, Object listenerObject, int eventAction, CountDownLatch flushedSignal) {
455 				// Signal that we have flushed all events
456 				flushedSignal.countDown();
457 			}
458 		};
459 
460 		ListenerQueue<Object, Object, CountDownLatch> queue = newListenerQueue();
461 		queue.queueListeners(Collections.<Object, Object> singletonMap(dispatcher, dispatcher).entrySet(), dispatcher);
462 
463 		// fire event with the flushedSignal latch
464 		CountDownLatch flushedSignal = new CountDownLatch(1);
465 		queue.dispatchEventAsynchronous(0, flushedSignal);
466 
467 		try {
468 			// Wait for the flush signal; timeout after 30 seconds
469 			flushedSignal.await(30, TimeUnit.SECONDS);
470 		} catch (InterruptedException e) {
471 			// ignore but reset the interrupted flag
472 			Thread.currentThread().interrupt();
473 		}
474 	}
475 }
476