| /******************************************************************************* |
| * Copyright (c) 2003, 2005 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.osgi.framework.eventmgr; |
| |
| import java.util.ArrayList; |
| import org.eclipse.osgi.framework.eventmgr.EventListeners.ListElement; |
| import org.eclipse.osgi.framework.eventmgr.EventManager.EventThread; |
| |
| /** |
| * The ListenerQueue is used to snapshot the set of listeners at the time the event |
| * is fired. The snapshot list is then used to dispatch |
| * events to those listeners. A ListenerQueue object is associated with a |
| * specific EventManager object. ListenerQueue objects constructed with the same |
| * EventManager object will get in-order delivery of events |
| * using asynchronous delivery. No delivery order is guaranteed for synchronous |
| * delivery to avoid any potential deadly embraces. |
| * |
| * <p>ListenerQueue objects are created as necesssary to build a set of listeners |
| * that should receive a specific event or events. Once the set is created, the event |
| * can then be synchronously or asynchronously delivered to the set of |
| * listeners. After the event has been dispatched for delivery, the |
| * ListenerQueue object should be discarded as it is likely the list of listeners is stale. |
| * A new ListenerQueue object should be created when it is time to deliver |
| * another event. The memory cost of a ListenerQueue object is |
| * low since the ListenerQueue object shares the array of listeners with the EventListeners |
| * object which are queued. |
| * EventListeners uses copy-on-write semantics for managing the array and will copy the array |
| * before changing it once the array has been shared with a ListenerQueue. This minimizes |
| * object creation while guaranteeing the snapshot list is never modified once created. |
| * @since 3.1 |
| */ |
| public class ListenerQueue { |
| /** |
| * EventManager with which this queue is associated. |
| */ |
| protected final EventManager manager; |
| /** |
| * A list of listener lists. |
| */ |
| private final ArrayList queue; |
| |
| /** |
| * Once the listener queue has been used to dispatch an event, |
| * you cannot add modify the queue. |
| * Access to this field must be protected by a synchronized region. |
| */ |
| private boolean readOnly; |
| |
| /** |
| * ListenerQueue constructor. This method creates an empty snapshop list. |
| * |
| * @param manager The EventManager this queue is associated with. |
| * @throws IllegalArgumentException If manager is null. |
| */ |
| public ListenerQueue(EventManager manager) { |
| if (manager == null) { |
| throw new IllegalArgumentException(); |
| } |
| |
| this.manager = manager; |
| queue = new ArrayList(); |
| readOnly = false; |
| } |
| |
| /** |
| * Add a listener list to the snapshot list. This method can be called multiple times, prior to |
| * calling one of the dispatchEvent methods, to build the set of listeners for the |
| * delivery of a specific event. The current list of listeners in the specified EventListeners |
| * object is added to the snapshot list. |
| * |
| * @param listeners An EventListeners object to add to the queue. The current listeners |
| * in the EventListeners object will be called when an event is dispatched. |
| * @param dispatcher An EventDispatcher object to use when dispatching an event |
| * to the listeners on the specified EventListeners. |
| * @throws IllegalStateException If called after one of the dispatch methods has been called. |
| */ |
| public synchronized void queueListeners(EventListeners listeners, EventDispatcher dispatcher) { |
| if (readOnly) { |
| throw new IllegalStateException(); |
| } |
| |
| if (listeners != null) { |
| ListElement[] list = listeners.getListeners(); |
| |
| if (list.length > 0) { |
| queue.add(new EventListeners.ListElement(list, dispatcher)); |
| } |
| } |
| } |
| |
| /** |
| * Asynchronously dispatch an event to the snapshot list. An event dispatch thread |
| * maintained by the associated EventManager is used to deliver the events. |
| * This method may return immediately to the caller. |
| * |
| * @param eventAction This value is passed to the EventDispatcher. |
| * @param eventObject This object is passed to the EventDispatcher. |
| */ |
| public void dispatchEventAsynchronous(int eventAction, Object eventObject) { |
| synchronized (this) { |
| readOnly = true; |
| } |
| EventThread eventThread = manager.getEventThread(); |
| synchronized (eventThread) { /* synchronize on the EventThread to ensure no interleaving of posting to the event thread */ |
| int size = queue.size(); |
| for (int i = 0; i < size; i++) { /* iterate over the list of listener lists */ |
| ListElement list = (ListElement)queue.get(i); |
| eventThread.postEvent((ListElement[]) list.primary, (EventDispatcher) list.companion, eventAction, eventObject); |
| } |
| } |
| } |
| |
| /** |
| * Synchronously dispatch an event to the snapshot list. The event may |
| * be dispatched on the current thread or an event dispatch thread |
| * maintained by the associated EventManager. |
| * This method will not return to the caller until the EventDispatcher |
| * has been called (and has returned) for each listener on the queue. |
| * |
| * @param eventAction This value is passed to the EventDispatcher. |
| * @param eventObject This object is passed to the EventDispatcher. |
| */ |
| public void dispatchEventSynchronous(int eventAction, Object eventObject) { |
| synchronized (this) { |
| readOnly = true; |
| } |
| // We can't guarantee any delivery order for synchronous events. |
| // Attempts to do so result in deadly embraces. |
| int size = queue.size(); |
| for (int i = 0; i < size; i++) { /* iterate over the list of listener lists */ |
| ListElement list = (ListElement)queue.get(i); |
| EventManager.dispatchEvent((ListElement[]) list.primary, (EventDispatcher) list.companion, eventAction, eventObject); |
| } |
| } |
| } |