package org.eclipse.osgi.internal.framework;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.eclipse.osgi.framework.eventmgr.CopyOnWriteIdentityMap;
import org.eclipse.osgi.framework.eventmgr.EventDispatcher;
import org.eclipse.osgi.framework.eventmgr.EventManager;
import org.eclipse.osgi.framework.eventmgr.ListenerQueue;
import org.eclipse.osgi.internal.debug.Debug;
import org.eclipse.osgi.internal.serviceregistry.HookContext;
import org.eclipse.osgi.internal.serviceregistry.ServiceRegistry;
import org.eclipse.osgi.internal.serviceregistry.ShrinkableCollection;
import org.osgi.framework.AdminPermission;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.FrameworkListener;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.SynchronousBundleListener;
import org.osgi.framework.hooks.bundle.CollisionHook;
import org.osgi.framework.hooks.bundle.EventHook;
public class EquinoxEventPublisher {
static final String eventHookName = EventHook.class.getName();
static final String collisionHookName = CollisionHook.class.getName();
static final int FRAMEWORK_STOPPED_MASK = (FrameworkEvent.STOPPED | FrameworkEvent.STOPPED_BOOTCLASSPATH_MODIFIED | FrameworkEvent.STOPPED_UPDATE);
static final int BUNDLEEVENT = 1;
static final int BUNDLEEVENTSYNC = 2;
static final int FRAMEWORKEVENT = 4;
private final EquinoxContainer container;
private Object monitor = new Object();
private EventManager eventManager;
private final Map<BundleContextImpl, CopyOnWriteIdentityMap<BundleListener, BundleListener>> allBundleListeners = new LinkedHashMap<>();
private final Map<BundleContextImpl, CopyOnWriteIdentityMap<SynchronousBundleListener, SynchronousBundleListener>> allSyncBundleListeners = new LinkedHashMap<>();
private final Map<BundleContextImpl, CopyOnWriteIdentityMap<FrameworkListener, FrameworkListener>> allFrameworkListeners = new LinkedHashMap<>();
public EquinoxEventPublisher(EquinoxContainer container) {
this.container = container;
}
void init() {
resetEventManager(new EventManager("Framework Event Dispatcher: " + container.toString()));
}
void close() {
flushFrameworkEvents();
resetEventManager(null);
allBundleListeners.clear();
allSyncBundleListeners.clear();
allFrameworkListeners.clear();
}
private void resetEventManager(EventManager newEventManager) {
EventManager currentEventManager;
synchronized (this.monitor) {
currentEventManager = eventManager;
eventManager = newEventManager;
}
if (currentEventManager != null) {
currentEventManager.close();
}
}
public <K, V, E> ListenerQueue<K, V, E> newListenerQueue() {
synchronized (this.monitor) {
return new ListenerQueue<>(eventManager);
}
}
private boolean isEventManagerSet() {
synchronized (this.monitor) {
return eventManager != null;
}
}
public void publishBundleEvent(int type, Bundle bundle, Bundle origin) {
if (origin != null) {
publishBundleEvent(new BundleEvent(type, bundle, origin));
} else {
publishBundleEvent(new BundleEvent(type, bundle));
}
}
private void publishBundleEvent(final BundleEvent event) {
if (System.getSecurityManager() == null) {
publishBundleEventPrivileged(event);
} else {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
publishBundleEventPrivileged(event);
return null;
}
});
}
}
void publishBundleEventPrivileged(BundleEvent event) {
if (!isEventManagerSet()) {
return;
}
Map<BundleContextImpl, Set<Map.Entry<SynchronousBundleListener, SynchronousBundleListener>>> listenersSync;
BundleContextImpl systemContext = null;
Set<Map.Entry<SynchronousBundleListener, SynchronousBundleListener>> systemBundleListenersSync = null;
synchronized (allSyncBundleListeners) {
listenersSync = new LinkedHashMap<>(allSyncBundleListeners.size());
for (Map.Entry<BundleContextImpl, CopyOnWriteIdentityMap<SynchronousBundleListener, SynchronousBundleListener>> entry : allSyncBundleListeners.entrySet()) {
CopyOnWriteIdentityMap<SynchronousBundleListener, SynchronousBundleListener> listeners = entry.getValue();
if (!listeners.isEmpty()) {
Set<Map.Entry<SynchronousBundleListener, SynchronousBundleListener>> listenerEntries = listeners.entrySet();
if (entry.getKey().getBundleImpl().getBundleId() == 0) {
systemContext = entry.getKey();
systemBundleListenersSync = listenerEntries;
}
listenersSync.put(entry.getKey(), listeners.entrySet());
}
}
}
Map<BundleContextImpl, Set<Map.Entry<BundleListener, BundleListener>>> listenersAsync = null;
Set<Map.Entry<BundleListener, BundleListener>> systemBundleListenersAsync = null;
if ((event.getType() & (BundleEvent.STARTING | BundleEvent.STOPPING | BundleEvent.LAZY_ACTIVATION)) == 0) {
synchronized (allBundleListeners) {
listenersAsync = new LinkedHashMap<>(allBundleListeners.size());
for (Map.Entry<BundleContextImpl, CopyOnWriteIdentityMap<BundleListener, BundleListener>> entry : allBundleListeners.entrySet()) {
CopyOnWriteIdentityMap<BundleListener, BundleListener> listeners = entry.getValue();
if (!listeners.isEmpty()) {
Set<Map.Entry<BundleListener, BundleListener>> listenerEntries = listeners.entrySet();
if (entry.getKey().getBundleImpl().getBundleId() == 0) {
systemContext = entry.getKey();
systemBundleListenersAsync = listenerEntries;
}
listenersAsync.put(entry.getKey(), listenerEntries);
}
}
}
}
Collection<BundleContext> shrinkable;
if (listenersAsync == null) {
shrinkable = asBundleContexts(listenersSync.keySet());
} else {
shrinkable = new ShrinkableCollection<>(asBundleContexts(listenersSync.keySet()), asBundleContexts(listenersAsync.keySet()));
}
notifyEventHooksPrivileged(event, shrinkable);
if (systemBundleListenersSync != null && !listenersSync.containsKey(systemContext)) {
listenersSync.put(systemContext, systemBundleListenersSync);
}
if (systemBundleListenersAsync != null && !listenersAsync.containsKey(systemContext)) {
listenersAsync.put(systemContext, systemBundleListenersAsync);
}
if (!listenersSync.isEmpty()) {
ListenerQueue<SynchronousBundleListener, SynchronousBundleListener, BundleEvent> queue = newListenerQueue();
for (Map.Entry<BundleContextImpl, Set<Map.Entry<SynchronousBundleListener, SynchronousBundleListener>>> entry : listenersSync.entrySet()) {
@SuppressWarnings({"rawtypes", "unchecked"})
EventDispatcher<SynchronousBundleListener, SynchronousBundleListener, BundleEvent> dispatcher = (EventDispatcher) entry.getKey();
Set<Map.Entry<SynchronousBundleListener, SynchronousBundleListener>> listeners = entry.getValue();
queue.queueListeners(listeners, dispatcher);
}
queue.dispatchEventSynchronous(BUNDLEEVENTSYNC, event);
}
if ((listenersAsync != null) && !listenersAsync.isEmpty()) {
ListenerQueue<BundleListener, BundleListener, BundleEvent> queue = newListenerQueue();
for (Map.Entry<BundleContextImpl, Set<Map.Entry<BundleListener, BundleListener>>> entry : listenersAsync.entrySet()) {
@SuppressWarnings({"rawtypes", "unchecked"})
EventDispatcher<BundleListener, BundleListener, BundleEvent> dispatcher = (EventDispatcher) entry.getKey();
Set<Map.Entry<BundleListener, BundleListener>> listeners = entry.getValue();
queue.queueListeners(listeners, dispatcher);
}
queue.dispatchEventAsynchronous(BUNDLEEVENT, event);
}
}
private void notifyEventHooksPrivileged(final BundleEvent event, final Collection<BundleContext> result) {
if (container.getConfiguration().getDebug().DEBUG_HOOKS) {
Debug.println("notifyBundleEventHooks(" + event.getType() + ":" + event.getBundle() + ", " + result + " )");
}
ServiceRegistry serviceRegistry = container.getServiceRegistry();
if (serviceRegistry != null) {
serviceRegistry.notifyHooksPrivileged(new HookContext() {
@Override
public void call(Object hook, ServiceRegistration<?> hookRegistration) throws Exception {
if (hook instanceof EventHook) {
((EventHook) hook).event(event, result);
}
}
@Override
public String getHookClassName() {
return eventHookName;
}
@Override
public String getHookMethodName() {
return "event";
}
@Override
public boolean skipRegistration(ServiceRegistration<?> hookRegistration) {
return false;
}
});
}
}
public void publishFrameworkEvent(int type, Bundle bundle, Throwable throwable) {
publishFrameworkEvent(type, bundle, throwable, (FrameworkListener[]) null);
}
public void publishFrameworkEvent(int type, Bundle bundle, Throwable throwable, final FrameworkListener... listeners) {
if (bundle == null)
bundle = container.getStorage().getModuleContainer().getModule(0).getBundle();
final FrameworkEvent event = new FrameworkEvent(type, bundle, throwable);
if (System.getSecurityManager() == null) {
publishFrameworkEventPrivileged(event, listeners);
} else {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
publishFrameworkEventPrivileged(event, listeners);
return null;
}
});
}
}
public void publishFrameworkEventPrivileged(FrameworkEvent event, FrameworkListener... callerListeners) {
if (!isEventManagerSet()) {
return;
}
Map<BundleContextImpl, Set<Map.Entry<FrameworkListener, FrameworkListener>>> listenerSnapshot;
synchronized (allFrameworkListeners) {
listenerSnapshot = new LinkedHashMap<>(allFrameworkListeners.size());
for (Map.Entry<BundleContextImpl, CopyOnWriteIdentityMap<FrameworkListener, FrameworkListener>> entry : allFrameworkListeners.entrySet()) {
CopyOnWriteIdentityMap<FrameworkListener, FrameworkListener> listeners = entry.getValue();
if (!listeners.isEmpty()) {
listenerSnapshot.put(entry.getKey(), listeners.entrySet());
}
}
}
ListenerQueue<FrameworkListener, FrameworkListener, FrameworkEvent> queue = newListenerQueue();
if (callerListeners != null && callerListeners.length > 0) {
Map<FrameworkListener, FrameworkListener> listeners = new HashMap<>();
for (FrameworkListener listener : callerListeners) {
if (listener != null)
listeners.put(listener, listener);
}
if (listeners.size() > 0) {
BundleContextImpl systemContext = (BundleContextImpl) container.getStorage().getModuleContainer().getModule(0).getBundle().getBundleContext();
@SuppressWarnings({"rawtypes", "unchecked"})
EventDispatcher<FrameworkListener, FrameworkListener, FrameworkEvent> dispatcher = (EventDispatcher) systemContext;
queue.queueListeners(listeners.entrySet(), dispatcher);
}
}
for (Map.Entry<BundleContextImpl, Set<Map.Entry<FrameworkListener, FrameworkListener>>> entry : listenerSnapshot.entrySet()) {
@SuppressWarnings({"rawtypes", "unchecked"})
EventDispatcher<FrameworkListener, FrameworkListener, FrameworkEvent> dispatcher = (EventDispatcher) entry.getKey();
Set<Map.Entry<FrameworkListener, FrameworkListener>> listeners = entry.getValue();
queue.queueListeners(listeners, dispatcher);
}
queue.dispatchEventAsynchronous(FRAMEWORKEVENT, event);
if ((event.getType() & FRAMEWORK_STOPPED_MASK) != 0) {
close();
}
}
@SuppressWarnings("unchecked")
public static Collection<BundleContext> asBundleContexts(Collection<? extends BundleContext> c) {
return (Collection<BundleContext>) c;
}
void addBundleListener(BundleListener listener, BundleContextImpl context) {
if (listener instanceof SynchronousBundleListener) {
container.checkAdminPermission(context.getBundle(), AdminPermission.LISTENER);
synchronized (allSyncBundleListeners) {
CopyOnWriteIdentityMap<SynchronousBundleListener, SynchronousBundleListener> listeners = allSyncBundleListeners.get(context);
if (listeners == null) {
listeners = new CopyOnWriteIdentityMap<>();
allSyncBundleListeners.put(context, listeners);
}
listeners.put((SynchronousBundleListener) listener, (SynchronousBundleListener) listener);
}
} else {
synchronized (allBundleListeners) {
CopyOnWriteIdentityMap<BundleListener, BundleListener> listeners = allBundleListeners.get(context);
if (listeners == null) {
listeners = new CopyOnWriteIdentityMap<>();
allBundleListeners.put(context, listeners);
}
listeners.put(listener, listener);
}
}
}
void removeBundleListener(BundleListener listener, BundleContextImpl context) {
if (listener instanceof SynchronousBundleListener) {
container.checkAdminPermission(context.getBundle(), AdminPermission.LISTENER);
synchronized (allSyncBundleListeners) {
CopyOnWriteIdentityMap<SynchronousBundleListener, SynchronousBundleListener> listeners = allSyncBundleListeners.get(context);
if (listeners != null)
listeners.remove(listener);
}
} else {
synchronized (allBundleListeners) {
CopyOnWriteIdentityMap<BundleListener, BundleListener> listeners = allBundleListeners.get(context);
if (listeners != null)
listeners.remove(listener);
}
}
}
void addFrameworkListener(FrameworkListener listener, BundleContextImpl context) {
synchronized (allFrameworkListeners) {
CopyOnWriteIdentityMap<FrameworkListener, FrameworkListener> listeners = allFrameworkListeners.get(context);
if (listeners == null) {
listeners = new CopyOnWriteIdentityMap<>();
allFrameworkListeners.put(context, listeners);
}
listeners.put(listener, listener);
}
}
void removeFrameworkListener(FrameworkListener listener, BundleContextImpl context) {
synchronized (allFrameworkListeners) {
CopyOnWriteIdentityMap<FrameworkListener, FrameworkListener> listeners = allFrameworkListeners.get(context);
if (listeners != null)
listeners.remove(listener);
}
}
void removeAllListeners(BundleContextImpl context) {
if (context.getBundleImpl().getBundleId() != 0) {
synchronized (allBundleListeners) {
allBundleListeners.remove(context);
}
synchronized (allSyncBundleListeners) {
allSyncBundleListeners.remove(context);
}
}
synchronized (allFrameworkListeners) {
allFrameworkListeners.remove(context);
}
}
void flushFrameworkEvents() {
EventDispatcher<Object, Object, CountDownLatch> dispatcher = new EventDispatcher<Object, Object, CountDownLatch>() {
@Override
public void dispatchEvent(Object eventListener, Object listenerObject, int eventAction, CountDownLatch flushedSignal) {
flushedSignal.countDown();
}
};
ListenerQueue<Object, Object, CountDownLatch> queue = newListenerQueue();
queue.queueListeners(Collections.<Object, Object> singletonMap(dispatcher, dispatcher).entrySet(), dispatcher);
CountDownLatch flushedSignal = new CountDownLatch(1);
queue.dispatchEventAsynchronous(0, flushedSignal);
try {
flushedSignal.await(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}