All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.osgi.internal.framework.EquinoxEventPublisher Maven / Gradle / Ivy

Go to download

AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based @AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step. This library is a superset of AspectJ weaver and hence also of AspectJ runtime.

There is a newer version: 1.9.22.1
Show newest version
/*******************************************************************************
 * Copyright (c) 2012, 2021 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
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.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.SynchronousBundleListener;
import org.osgi.framework.hooks.bundle.EventHook;

public class EquinoxEventPublisher {
	@SuppressWarnings("deprecation")
	static final int FRAMEWORK_STOPPED_MASK = (FrameworkEvent.STOPPED | FrameworkEvent.STOPPED_BOOTCLASSPATH_MODIFIED
			| FrameworkEvent.STOPPED_UPDATE | FrameworkEvent.STOPPED_SYSTEM_REFRESHED);

	static final int BUNDLEEVENT = 1;
	static final int BUNDLEEVENTSYNC = 2;
	/* SERVICEEVENT(3) is now handled by ServiceRegistry */
	static final int FRAMEWORKEVENT = 4;

	private final EquinoxContainer container;

	private Object monitor = new Object();
	private EventManager eventManager;

	/*
	 * The following maps objects keep track of event listeners
	 * by BundleContext.  Each element is a Map that is the set
	 * of event listeners for a particular BundleContext.  The max number of
	 * elements each of the following maps will have is the number of bundles
	 * installed in the Framework.
	 */
	// Map of BundleContexts for bundle's BundleListeners.
	private final Map> allBundleListeners = new LinkedHashMap<>();

	// Map of BundleContexts for bundle's SynchronousBundleListeners.
	private final Map> allSyncBundleListeners = new LinkedHashMap<>();

	// Map of BundleContexts for bundle's FrameworkListeners.
	private final Map> allFrameworkListeners = new LinkedHashMap<>();

	public EquinoxEventPublisher(EquinoxContainer container) {
		this.container = container;
	}

	void init() {
		// create our event manager on init()
		resetEventManager(new EventManager("Framework Event Dispatcher: " + container.toString())); //$NON-NLS-1$
	}

	void close() {
		// ensure we have flushed any events in the queue
		flushFrameworkEvents();
		// close and clear out the event manager
		resetEventManager(null);
		// make sure we clear out all the remaining listeners
		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  ListenerQueue newListenerQueue() {
		synchronized (this.monitor) {
			return new ListenerQueue<>(eventManager);
		}
	}

	private boolean isEventManagerSet() {
		synchronized (this.monitor) {
			return eventManager != null;
		}
	}

	/**
	 * Deliver a BundleEvent to SynchronousBundleListeners (synchronous) and
	 * BundleListeners (asynchronous).
	 *
	 * @param type
	 *            BundleEvent type.
	 * @param bundle
	 *            Affected bundle or null.
	 * @param origin
	 *            The origin of the event
	 */
	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((PrivilegedAction) () -> {
				publishBundleEventPrivileged(event);
				return null;
			});
		}
	}

	void publishBundleEventPrivileged(BundleEvent event) {
		if (!isEventManagerSet()) {
			return;
		}
		/*
		 * We must collect the snapshots of the sync and async listeners
		 * BEFORE we dispatch the event.
		 */
		/* Collect snapshot of SynchronousBundleListeners */
		/* Build the listener snapshot */
		Map>> listenersSync;
		BundleContextImpl systemContext = null;
		Set> systemBundleListenersSync = null;
		synchronized (allSyncBundleListeners) {
			listenersSync = new LinkedHashMap<>(allSyncBundleListeners.size());
			for (Map.Entry> entry : allSyncBundleListeners.entrySet()) {
				CopyOnWriteIdentityMap listeners = entry.getValue();
				if (!listeners.isEmpty()) {
					Set> listenerEntries = listeners.entrySet();
					if (entry.getKey().getBundleImpl().getBundleId() == 0) {
						systemContext = entry.getKey();
						// record the snapshot; no need to create another copy
						// because the hooks are not exposed to this set
						systemBundleListenersSync = listenerEntries;
					}
					listenersSync.put(entry.getKey(), listeners.entrySet());
				}
			}
		}
		/* Collect snapshot of BundleListeners; only if the event is NOT STARTING or STOPPING or LAZY_ACTIVATION */
		Map>> listenersAsync = null;
		Set> systemBundleListenersAsync = null;
		if ((event.getType() & (BundleEvent.STARTING | BundleEvent.STOPPING | BundleEvent.LAZY_ACTIVATION)) == 0) {
			synchronized (allBundleListeners) {
				listenersAsync = new LinkedHashMap<>(allBundleListeners.size());
				for (Map.Entry> entry : allBundleListeners.entrySet()) {
					CopyOnWriteIdentityMap listeners = entry.getValue();
					if (!listeners.isEmpty()) {
						Set> listenerEntries = listeners.entrySet();
						if (entry.getKey().getBundleImpl().getBundleId() == 0) {
							systemContext = entry.getKey();
							// record the snapshot; no need to create another copy
							// because the hooks are not exposed to this set
							systemBundleListenersAsync = listenerEntries;
						}
						listenersAsync.put(entry.getKey(), listenerEntries);
					}
				}
			}
		}

		/* shrink the snapshot.
		 * keySet returns a Collection which cannot be added to and
		 * removals from that collection will result in removals of the
		 * entry from the snapshot.
		 */
		Collection shrinkable;
		if (listenersAsync == null) {
			shrinkable = asBundleContexts(listenersSync.keySet());
		} else {
			shrinkable = new ShrinkableCollection<>(asBundleContexts(listenersSync.keySet()), asBundleContexts(listenersAsync.keySet()));
		}

		notifyEventHooksPrivileged(event, shrinkable);

		// always add back the system bundle listeners if they were removed
		if (systemBundleListenersSync != null && !listenersSync.containsKey(systemContext)) {
			listenersSync.put(systemContext, systemBundleListenersSync);
		}
		if (systemBundleListenersAsync != null && !listenersAsync.containsKey(systemContext)) {
			listenersAsync.put(systemContext, systemBundleListenersAsync);
		}

		/* Dispatch the event to the snapshot for sync listeners */
		if (!listenersSync.isEmpty()) {
			ListenerQueue queue = newListenerQueue();
			for (Map.Entry>> entry : listenersSync.entrySet()) {
				@SuppressWarnings({"rawtypes", "unchecked"})
				EventDispatcher dispatcher = (EventDispatcher) entry.getKey();
				Set> listeners = entry.getValue();
				queue.queueListeners(listeners, dispatcher);
			}
			queue.dispatchEventSynchronous(BUNDLEEVENTSYNC, event);
		}

		/* Dispatch the event to the snapshot for async listeners */
		if ((listenersAsync != null) && !listenersAsync.isEmpty()) {
			ListenerQueue queue = newListenerQueue();
			for (Map.Entry>> entry : listenersAsync.entrySet()) {
				@SuppressWarnings({"rawtypes", "unchecked"})
				EventDispatcher dispatcher = (EventDispatcher) entry.getKey();
				Set> listeners = entry.getValue();
				queue.queueListeners(listeners, dispatcher);
			}
			queue.dispatchEventAsynchronous(BUNDLEEVENT, event);
		}
	}

	private void notifyEventHooksPrivileged(final BundleEvent event, final Collection result) {
		if (container.getConfiguration().getDebug().DEBUG_HOOKS) {
			Debug.println("notifyBundleEventHooks(" + event.getType() + ":" + event.getBundle() + ", " + result + " )"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
		}

		ServiceRegistry serviceRegistry = container.getServiceRegistry();
		if (serviceRegistry != null) {
			serviceRegistry.notifyHooksPrivileged(EventHook.class, "event", (hook, r) -> hook.event(event, result)); //$NON-NLS-1$
		}
	}

	/**
	 * Deliver a FrameworkEvent.
	 *
	 * @param type
	 *            FrameworkEvent type.
	 * @param bundle
	 *            Affected bundle or null for system bundle.
	 * @param throwable
	 *            Related exception or null.
	 */
	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((PrivilegedAction) () -> {
				publishFrameworkEventPrivileged(event, listeners);
				return null;
			});
		}
	}

	public void publishFrameworkEventPrivileged(FrameworkEvent event, FrameworkListener... callerListeners) {
		if (!isEventManagerSet()) {
			return;
		}
		// Build the listener snapshot
		Map>> listenerSnapshot;
		synchronized (allFrameworkListeners) {
			listenerSnapshot = new LinkedHashMap<>(allFrameworkListeners.size());
			for (Map.Entry> entry : allFrameworkListeners.entrySet()) {
				CopyOnWriteIdentityMap listeners = entry.getValue();
				if (!listeners.isEmpty()) {
					listenerSnapshot.put(entry.getKey(), listeners.entrySet());
				}
			}
		}
		// If framework event hook were defined they would be called here

		// deliver the event to the snapshot
		ListenerQueue queue = newListenerQueue();

		// add the listeners specified by the caller first
		if (callerListeners != null && callerListeners.length > 0) {
			Map listeners = new HashMap<>();
			for (FrameworkListener listener : callerListeners) {
				if (listener != null)
					listeners.put(listener, listener);
			}
			// We use the system bundle context as the dispatcher
			if (listeners.size() > 0) {
				BundleContextImpl systemContext = (BundleContextImpl) container.getStorage().getModuleContainer().getModule(0).getBundle().getBundleContext();
				@SuppressWarnings({"rawtypes", "unchecked"})
				EventDispatcher dispatcher = (EventDispatcher) systemContext;
				queue.queueListeners(listeners.entrySet(), dispatcher);
			}
		}

		for (Map.Entry>> entry : listenerSnapshot.entrySet()) {
			@SuppressWarnings({"rawtypes", "unchecked"})
			EventDispatcher dispatcher = (EventDispatcher) entry.getKey();
			Set> listeners = entry.getValue();
			queue.queueListeners(listeners, dispatcher);
		}

		queue.dispatchEventAsynchronous(FRAMEWORKEVENT, event);
		// close down the publisher if we got the stopped event
		if ((event.getType() & FRAMEWORK_STOPPED_MASK) != 0) {
			close();
		}
	}

	/**
	 * Coerce the generic type of a collection from Collection
	 * to Collection
	 * @param c Collection to be coerced.
	 * @return c coerced to Collection
	 */
	@SuppressWarnings("unchecked")
	public static Collection asBundleContexts(Collection c) {
		return (Collection) c;
	}

	void addBundleListener(BundleListener listener, BundleContextImpl context) {
		if (listener instanceof SynchronousBundleListener) {
			container.checkAdminPermission(context.getBundle(), AdminPermission.LISTENER);
			synchronized (allSyncBundleListeners) {
				CopyOnWriteIdentityMap listeners = allSyncBundleListeners.get(context);
				if (listeners == null) {
					listeners = new CopyOnWriteIdentityMap<>();
					allSyncBundleListeners.put(context, listeners);
				}
				listeners.put((SynchronousBundleListener) listener, (SynchronousBundleListener) listener);
			}
		} else {
			synchronized (allBundleListeners) {
				CopyOnWriteIdentityMap 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 listeners = allSyncBundleListeners.get(context);
				if (listeners != null)
					listeners.remove(listener);
			}
		} else {
			synchronized (allBundleListeners) {
				CopyOnWriteIdentityMap listeners = allBundleListeners.get(context);
				if (listeners != null)
					listeners.remove(listener);
			}
		}
	}

	void addFrameworkListener(FrameworkListener listener, BundleContextImpl context) {
		synchronized (allFrameworkListeners) {
			CopyOnWriteIdentityMap 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 listeners = allFrameworkListeners.get(context);
			if (listeners != null)
				listeners.remove(listener);
		}
	}

	void removeAllListeners(BundleContextImpl context) {
		// leave any left over listeners until the framework STOPPED event
		if (context.getBundleImpl().getBundleId() != 0) {
			synchronized (allBundleListeners) {
				allBundleListeners.remove(context);
			}
			synchronized (allSyncBundleListeners) {
				allSyncBundleListeners.remove(context);
			}
		}
		synchronized (allFrameworkListeners) {
			allFrameworkListeners.remove(context);
		}
	}

	void flushFrameworkEvents() {
		// Signal that we have flushed all events
		EventDispatcher dispatcher = (el, lo, ea, signal) -> signal.countDown();

		ListenerQueue queue = newListenerQueue();
		queue.queueListeners(Collections. singletonMap(dispatcher, dispatcher).entrySet(), dispatcher);

		// fire event with the flushedSignal latch
		CountDownLatch flushedSignal = new CountDownLatch(1);
		queue.dispatchEventAsynchronous(0, flushedSignal);

		try {
			// Wait for the flush signal; timeout after 30 seconds
			flushedSignal.await(30, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			// ignore but reset the interrupted flag
			Thread.currentThread().interrupt();
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy