WikiEventManager is a singleton class that manages the addition and removal of WikiEvent listeners to a event source, as well as the firing of events to those listeners. An "event source" is the object delegating its event handling to an inner delegating class supplied by this manager. The class being serviced is considered a "client" of the delegate. The WikiEventManager operates across any number of simultaneously-existing WikiEngines since it manages all delegation on a per-object basis. Anything that might fire a WikiEvent (or any of its subclasses) can be a client.

Using a Delegate for Event Listener Management#

Basically, rather than have all manner of client classes maintain their own listener lists, add and remove listener methods, any class wanting to attach listeners can simply request a delegate object to provide that service. The delegate handles the listener list, the add and remove listener methods. Firing events is then a matter of calling the WikiEventManager's fireEvent(Object,WikiEvent) method, where the Object is the client. Prior to instantiating the event object, the client can call isListening(Object) to see there are any listeners attached to its delegate.

Adding Listeners#

Adding a WikiEventListener to an object is very simple:

    WikiEventManager.addWikiEventListener(object,listener);

Removing Listeners#

Removing a WikiEventListener from an object is very simple:

    WikiEventManager.removeWikiEventListener(object,listener);
If you only have a reference to the listener, the following method will remove it from any clients managed by the WikiEventManager:
    WikiEventManager.removeWikiEventListener(listener);

Backward Compatibility: Replacing Existing fireEvent() Methods#

Using one manager for all events processing permits consolidation of all event listeners and their associated methods in one place rather than having them attached to specific subcomponents of an application, and avoids a great deal of event-related cut-and-paste code. Convenience methods that call the WikiEventManager for event delegation can be written to maintain existing APIs.

For example, an existing <tt>fireEvent()</tt> method might look something like this:

  protected final void fireEvent( WikiEvent event )
  {
      for ( Iterator it = m_listeners.iterator(); it.hasNext(); )
      {
          WikiEventListener listener = (WikiEventListener)it.next();
          listener.actionPerformed(event);
      }
  }
One disadvantage is that the above method is supplied with event objects, which are created even when no listener exists for them. In a busy wiki with many users unused/unnecessary event creation could be considerable. Another advantage is that in addition to the iterator, there must be code to support the addition and remove of listeners. The above could be replaced with the below code (and with no necessary local support for adding and removing listeners):
  protected final void fireEvent( int type )
  {
      if ( WikiEventManager.isListening(this) )
      {
          WikiEventManager.fireEvent(this,new WikiEngineEvent(this,type));
      }
  }
This only needs to be customized to supply the specific parameters for whatever WikiEvent you want to create.

Preloading Listeners#

This may be used to create listeners for objects that don't yet exist, particularly designed for embedded applications that need to be able to listen for the instantiation of an Object, by maintaining a cache of client-less WikiEvent sources that set their client upon being popped from the cache. Each time any of the methods expecting a client parameter is called with a null parameter it will preload an internal cache with a client-less delegate object that will be popped and returned in preference to creating a new object. This can have unwanted side effects if there are multiple clients populating the cache with listeners. The only check is for a Class match, so be aware if others might be populating the client-less cache with listeners.

Listener lifecycle#

Note that in most cases it is not necessary to remove a listener. As of 2.4.97, the listeners are stored as WeakReferences, and will be automatically cleaned at the next garbage collection, if you no longer hold a reference to them. Of course, until the garbage is collected, your object might still be getting events, so if you wish to avoid that, please remove it explicitly as described above.


Notes: Preloading Listeners#

The design includes the ability to preload listeners to the WikiEngine prior to instantiation, such that when WikiEngine's initialize() method is called, the listener fires a WikiEvent.INITIALIZING event right after the m_properties member variable has been set. This enables foreign code to alter anything in jspwiki.properties prior to the event API sending WikiEvent.INITIALIZED, signaling a finished initialization. See the Preloading Listeners section on the WikiEventManager page.

The WikiEventManager also manages the addition of event listeners to the WikiEngine prior to its instantiation. This is a "singleton-per-WikiEngine" object which works for either server or embedded applications. You can also ignore it and add listeners directly to the engine if you like. But if you use it for event management and you have an existing hook to the engine (or a WikiContext, which gets you the engine), you can then obtain all of the listeners attached to the engine and not have to deal with where the events are being fired from. This simplifies your life.

I've found a way around preloading WikiEventListeners in the WikiEventManager, which was a hook to intercept the initialization process of a WikiEngine by catching WikiEngineEvent.INITIALIZING and pre-set properties. This was necessary for embedded applications if one wanted to set something like installed paths or other settings from the user application.

If you think about it, when a Java application containing an embedded JSPWiki starts up, neither the WikiEngine nor the WikiEventManager exist. We need a way to alter the properties of the WikiEngine either before or during the initialization process based on settings coming from the parent application. To intercept the initialization, we 'preload' a listener to a singleton cache in the WikiEventManager, which given that the cache is static, doesn't even have to exist at that point. Since in the WikiEventManager listeners are normally attached to 'delegates' rather than directly to objects, we create a delegate based not on the object (the WikiEngine that does not yet exist) but on its *class*.

So, to preload a listener to the singleton WikiEventManager, i.e., attach a listener to a non-existent WikiEngine by class rather than by object, one could create a listener and call:

  WikiEventListener listener = getWikiEventListener();
  WikiEventManager.addWikiEventListener(
          WikiEngine.class,listener);
This adds the listener to the preload cache so that the when the engine fires its first events or asks if anybody is listening, we first check to see if there are any delegates in the preload cache whose class matches the incoming object (the WikiEngine), and rather than create a new delegate we remove the cached delegate and attach it to the WikiEngine. In this way we can attach listeners to a WikiEngine before it even exists.

Is anyone using the preload feature? It's been around since the beginning of the WikiEventManager and it may still prove useful. But if nobody is using it we could either deprecate it or outright remove it. If nobody is I'm sure it doesn't need to survive into JSPWiki 2.6. The one advantage it does have is that the alternative (using cascading properties in PropertyReader) requires writing a property file to the file system that gets picked up during initialization, whereas the preload feature is entirely based on listeners and requires no files.

Add new attachment

Only authorized users are allowed to upload new attachments.
« This page (revision-1) was last changed on 18-Jul-2007 03:37 by MurrayAltheim