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

org.eclipse.osgi.framework.eventmgr.EventManager Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2003, 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.framework.eventmgr;

import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Map;
import java.util.Set;

/**
 * This class is the central class for the Event Manager. Each
 * program that wishes to use the Event Manager should construct
 * an EventManager object and use that object to construct
 * ListenerQueue for dispatching events. CopyOnWriteIdentityMap objects
 * must be used to manage listener lists.
 *
 * 

This example uses the fictitious SomeEvent class and shows how to use this package * to deliver a SomeEvent to a set of SomeEventListeners. *

 *
 * 	// Create an EventManager with a name for an asynchronous event dispatch thread
 * 	EventManager eventManager = new EventManager("SomeEvent Async Event Dispatcher Thread");
 * 	// Create a CopyOnWriteIdentityMap to hold the list of SomeEventListeners
 *	Map eventListeners = new CopyOnWriteIdentityMap();
 *
 *	// Add a SomeEventListener to the listener list
 *	eventListeners.put(someEventListener, null);
 *
 *	// Asynchronously deliver a SomeEvent to registered SomeEventListeners
 *	// Create the listener queue for this event delivery
 *	ListenerQueue listenerQueue = new ListenerQueue(eventManager);
 *	// Add the listeners to the queue and associate them with the event dispatcher
 *	listenerQueue.queueListeners(eventListeners.entrySet(), new EventDispatcher() {
 *		public void dispatchEvent(Object eventListener, Object listenerObject,
 *                                    int eventAction, Object eventObject) {
 * 			try {
 *				(SomeEventListener)eventListener.someEventOccured((SomeEvent)eventObject);
 * 			} catch (Throwable t) {
 * 				// properly log/handle any Throwable thrown by the listener
 * 			}
 *		}
 *	});
 *	// Deliver the event to the listeners.
 *	listenerQueue.dispatchEventAsynchronous(0, new SomeEvent());
 *
 *	// Remove the listener from the listener list
 *	eventListeners.remove(someEventListener);
 *
 *	// Close EventManager to clean when done to terminate async event dispatch thread.
 *	// Note that closing the event manager while asynchronously delivering events
 *	// may cause some events to not be delivered before the async event dispatch
 *	// thread terminates
 *	eventManager.close();
 * 
* *

At first glance, this package may seem more complicated than necessary * but it has support for some important features. The listener list supports * companion objects for each listener object. This is used by the OSGi framework * to create wrapper objects for a listener which are passed to the event dispatcher. * The ListenerQueue class is used to build a snap shot of the listeners prior to beginning * event dispatch. * * The OSGi framework uses a 2 level listener list for each listener type (4 types). * Level one is managed per framework instance and contains the list of BundleContexts which have * registered a listener. Level 2 is managed per BundleContext for the listeners in that * context. This allows all the listeners of a bundle to be easily and atomically removed from * the level one list. To use a "flat" list for all bundles would require the list to know which * bundle registered a listener object so that the list could be traversed when stopping a bundle * to remove all the bundle's listeners. * * When an event is fired, a snapshot list (ListenerQueue) must be made of the current listeners before delivery * is attempted. The snapshot list is necessary to allow the listener list to be modified while the * event is being delivered to the snapshot list. The memory cost of the snapshot list is * low since the ListenerQueue object uses the copy-on-write semantics * of the CopyOnWriteIdentityMap. This guarantees the snapshot list is never modified once created. * * The OSGi framework also uses a 2 level dispatch technique (EventDispatcher). * Level one dispatch is used by the framework to add the level 2 listener list of each * BundleContext to the snapshot in preparation for delivery of the event. * Level 2 dispatch is used as the final event deliverer and must cast the listener * and event objects to the proper type before calling the listener. Level 2 dispatch * will cancel delivery of an event * to a bundle that has stopped between the time the snapshot was created and the * attempt was made to deliver the event. * *

The highly dynamic nature of the OSGi framework had necessitated these features for * proper and efficient event delivery. * @since 3.1 * @noextend This class is not intended to be subclassed by clients. */ public class EventManager { static final boolean DEBUG = false; /** * EventThread for asynchronous dispatch of events. * Access to this field must be protected by a synchronized region. */ private EventThread thread; /** * Once closed, an attempt to create a new EventThread will result in an * IllegalStateException. */ private boolean closed; /** * Thread name used for asynchronous event delivery */ protected final String threadName; /** * The thread group used for asynchronous event delivery */ protected final ThreadGroup threadGroup; /** * EventManager constructor. An EventManager object is responsible for * the delivery of events to listeners via an EventDispatcher. * */ public EventManager() { this(null, null); } /** * EventManager constructor. An EventManager object is responsible for * the delivery of events to listeners via an EventDispatcher. * * @param threadName The name to give the event thread associated with * this EventManager. A null value is allowed. */ public EventManager(String threadName) { this(threadName, null); } /** * EventManager constructor. An EventManager object is responsible for * the delivery of events to listeners via an EventDispatcher. * * @param threadName The name to give the event thread associated with * this EventManager. A null value is allowed. * @param threadGroup The thread group to use for the asynchronous event * thread associated with this EventManager. A null value is allowed. * @since 3.4 */ public EventManager(String threadName, ThreadGroup threadGroup) { thread = null; closed = false; this.threadName = threadName; this.threadGroup = threadGroup; } /** * This method can be called to release any resources associated with this * EventManager. *

* Closing this EventManager while it is asynchronously delivering events * may cause some events to not be delivered before the async event dispatch * thread terminates. */ public synchronized void close() { if (closed) { return; } if (thread != null) { thread.close(); thread = null; } closed = true; } /** * Returns the EventThread to use for dispatching events asynchronously for * this EventManager. * * @return EventThread to use for dispatching events asynchronously for * this EventManager. */ synchronized EventThread getEventThread() { if (closed) { throw new IllegalStateException(); } if (thread == null) { /* if there is no thread, then create a new one */ thread = AccessController.doPrivileged(new PrivilegedAction>() { @Override public EventThread run() { EventThread t = new EventThread<>(threadGroup, threadName); return t; } }); /* start the new thread */ thread.start(); } @SuppressWarnings("unchecked") EventThread result = (EventThread) thread; return result; } /** * This method calls the EventDispatcher object to complete the dispatch of * the event. If there are more elements in the list, call dispatchEvent * on the next item on the list. * This method is package private. * * @param listeners A Set of entries from a CopyOnWriteIdentityMap map. * @param dispatcher Call back object which is called to complete the delivery of * the event. * @param eventAction This value was passed by the event source and * is passed to this method. This is passed on to the call back object. * @param eventObject This object was created by the event source and * is passed to this method. This is passed on to the call back object. */ static void dispatchEvent(Set> listeners, EventDispatcher dispatcher, int eventAction, E eventObject) { for (Map.Entry listener : listeners) { /* iterate over the list of listeners */ final K eventListener = listener.getKey(); final V listenerObject = listener.getValue(); try { /* Call the EventDispatcher to complete the delivery of the event. */ dispatcher.dispatchEvent(eventListener, listenerObject, eventAction, eventObject); } catch (Throwable t) { /* Consume and ignore any exceptions thrown by the listener */ if (DEBUG) { System.out.println("Exception in " + eventListener); //$NON-NLS-1$ t.printStackTrace(); } } } } /** * This package private class is used for asynchronously dispatching events. */ static class EventThread extends Thread { private static int nextThreadNumber; /** * Queued is a nested top-level (non-member) class. This class * represents the items which are placed on the asynch dispatch queue. * This class is private. */ private static class Queued { /** listener list for this event */ final Set> listeners; /** dispatcher of this event */ final EventDispatcher dispatcher; /** action for this event */ final int action; /** object for this event */ final E object; /** next item in event queue */ Queued next; /** * Constructor for event queue item * * @param l Listener list for this event * @param d Dispatcher for this event * @param a Action for this event * @param o Object for this event */ Queued(Set> l, EventDispatcher d, int a, E o) { listeners = l; dispatcher = d; action = a; object = o; next = null; } } /** item at the head of the event queue */ private Queued head; /** item at the tail of the event queue */ private Queued tail; /** if false the thread must terminate */ private volatile boolean running; /** * Constructor for the event thread. * @param threadName Name of the EventThread */ EventThread(ThreadGroup threadGroup, String threadName) { super(threadGroup, threadName == null ? getNextName() : threadName); running = true; head = null; tail = null; setDaemon(true); /* Mark thread as daemon thread */ } private static synchronized String getNextName() { return "EventManagerThread-" + nextThreadNumber++; //$NON-NLS-1$ } /** * Constructor for the event thread. * @param threadName Name of the EventThread */ EventThread(String threadName) { this(null, threadName); } /** * Constructor for the event thread. */ EventThread() { this(null, null); } /** * Stop thread. */ void close() { running = false; interrupt(); } /** * This method pulls events from * the queue and dispatches them. */ @Override public void run() { try { while (true) { Queued item = getNextEvent(); if (item == null) { return; } EventManager.dispatchEvent(item.listeners, item.dispatcher, item.action, item.object); // Bug 299589: since the call to getNextEvent() will eventually block for a long time, we need to make sure that the 'item' // variable is cleared of the previous value before the call to getNextEvent(). See VM SPec 2.5.7 for why the compiler // will not automatically clear this variable for each loop iteration. item = null; } } catch (RuntimeException | Error e) { if (EventManager.DEBUG) { e.printStackTrace(); } throw e; } } /** * This methods takes the input parameters and creates a Queued * object and queues it. * The thread is notified. * * @param l Listener list for this event * @param d Dispatcher for this event * @param a Action for this event * @param o Object for this event */ synchronized void postEvent(Set> l, EventDispatcher d, int a, E o) { if (!isAlive()) { /* If the thread is not alive, throw an exception */ throw new IllegalStateException(); } Queued item = new Queued<>(l, d, a, o); if (head == null) /* if the queue was empty */ { head = item; tail = item; } else /* else add to end of queue */ { tail.next = item; tail = item; } notify(); } /** * This method is called by the thread to remove * items from the queue so that they can be dispatched to their listeners. * If the queue is empty, the thread waits. * * @return The Queued removed from the top of the queue or null * if the thread has been requested to stop. */ private synchronized Queued getNextEvent() { while (running && (head == null)) { try { wait(); } catch (InterruptedException e) { // If interrupted, we will loop back up and check running } } if (!running) { /* if we are stopping */ return null; } Queued item = head; head = item.next; if (head == null) { tail = null; } return item; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy