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