Package org.eclipse.osgi.framework.eventmgr

Source Code of org.eclipse.osgi.framework.eventmgr.EventManager

/*******************************************************************************
* 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 org.eclipse.osgi.framework.eventmgr.EventListeners.ListElement;

/**
* 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. EventListeners objects
* should be used to manage listener lists.
*
* <p>This example uses the ficticous SomeEvent class and shows how to use this package
* to deliver a SomeEvent to a set of SomeEventListeners. 
* <pre>
*
*   // Create an EventManager with a name for an asynchronous event dispatch thread
*   EventManager eventManager = new EventManager("SomeEvent Async Event Dispatcher Thread");
*   // Create an EventListeners to hold the list of SomeEventListeners
*    EventListeners eventListeners = new EventListeners();
*
*    // Add a SomeEventListener to the listener list
*      eventListeners.addListener(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, 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.removeListener(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();
* </pre>
*
* <p>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 (EventListeners) for each listener type (4 types).
* Level one is managed by the framework and contains the list of BundleContexts which have
* registered a listener. Level 2 is managed by each 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 shares the array of listeners with the EventListeners object.
* EventListeners uses copy-on-write semantics for managing the array and will copy the array
* before changing it IF the array has been shared with a ListenerQueue. This minimizes
* object creation while guaranteeing 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 bewteen the time the snapshot was created and the
* attempt was made to deliver the event.
*
* <p> The highly dynamic nature of the OSGi framework had necessitated these features for
* proper and efficient event delivery. 
* @since 3.1
*/

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;

  /**
   * EventThread Name
   */
  protected final String threadName;

  /**
   * EventManager constructor. An EventManager object is responsible for
   * the delivery of events to listeners via an EventDispatcher.
   *
   */
  public EventManager() {
    this(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.
   */
  public EventManager(String threadName) {
    thread = null;
    this.threadName = threadName;
  }

  /**
   * This method can be called to release any resources associated with this
   * EventManager.
   * <p>
   * 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 (thread != null) {
      thread.close();
      thread = null;
    }
  }

  /**
   * 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 (thread == null) {
      /* if there is no thread, then create a new one */
      if (threadName == null) {
        thread = new EventThread();
      }
      else {
        thread = new EventThread(threadName);
      }
      thread.start(); /* start the new thread */
    }

    return thread;
  }

  /**
   * 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 null terminated array of ListElements with each element containing the primary and
   * companion object for a listener. This array must not be modified.
   * @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(ListElement[] listeners, EventDispatcher dispatcher, int eventAction, Object eventObject) {
    int size = listeners.length;
    for (int i = 0; i < size; i++) { /* iterate over the list of listeners */
      ListElement listener = listeners[i];
      if (listener == null) {    /* a null element terminates the list */
        break;
      }
      try {
        /* Call the EventDispatcher to complete the delivery of the event. */
        dispatcher.dispatchEvent(listener.primary, listener.companion, eventAction, eventObject);
      }
      catch (Throwable t) {
        /* Consume and ignore any exceptions thrown by the listener */
        if (DEBUG) {
          System.out.println("Exception in " + listener.primary); //$NON-NLS-1$
          t.printStackTrace();
        }
      }
    }
  }

  /**
   * This package private class is used for asynchronously dispatching events.
   */

  static class EventThread extends Thread {
    /**
     * 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 ListElement[] listeners;
      /** dispatcher of this event */
      final EventDispatcher dispatcher;
      /** action for this event */
      final int action;
      /** object for this event */
      final Object 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(ListElement[] l, EventDispatcher d, int a, Object 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(String threadName) {
      super(threadName);
      init();
    }

    /**
     * Constructor for the event thread.
     */
    EventThread() {
      super();
      init();
    }

    private void init() {
      running = true;
      head = null;
      tail = null;

      setDaemon(true); /* Mark thread as daemon thread */
    }

    /**
     * Stop thread.
     */
    void close() {
      running = false;
      interrupt();
    }

    /**
     * This method pulls events from
     * the queue and dispatches them.
     */
    public void run() {
      try {
        while (true) {
          Queued item = getNextEvent();
          if (item == null) {
            return;
          }
          EventManager.dispatchEvent(item.listeners, item.dispatcher, item.action, item.object);
        }
      }
      catch (RuntimeException e) {
        if (EventManager.DEBUG) {
          e.printStackTrace();
        }
        throw e;
      }
      catch (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(ListElement[] l, EventDispatcher d, int a, Object 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 (!running) { /* if we are stopping */
        return null;
      }

      Queued item = head;
      head = item.next;
      if (head == null) {
        tail = null;
      }

      return item;
    }
  }
}
TOP

Related Classes of org.eclipse.osgi.framework.eventmgr.EventManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.