Skip to main content

Server-Side Typed Event Distributors

January 26, 2006

{cs.r.title}









Contents
Applications of Event Distributors
Events Seen as Interfaces
SWIHttpEvents: The Static Wrapper
for IHttpEvents
Initializing the SWIHttpEvents
Looking at a Trivial Implementation
for IHttpEvents
Need for an Event Distributor
Responding to only Selected Events
How to Raise an Event?
Event Distributor Class Diagram
JavaBeans and Property Change Events
Parallels to Remoting Proxies
Resources

Event frameworks such as publish/subscribe were much in vogue
during the early- to mid-90s. Those were also the days when C++, UI
frameworks, and CORBA were quite popular. This was just before the
onslaught of web-based programming on system design, with an
emphasis on simplicity. With the simplified thin UI of web clients,
the elaborate event designs took somewhat of a back seat.

Nevertheless, "events" continue to play a significant role in the
enterprise messaging world. TIBCO, MQ, and EJB Message Beans are
all examples of the prevalence of event-based, enterprise-level
architectures. Events also play an important role for in-process
software design, so that systems can be built with flexibility and
configurability, especially in the form of libraries and
frameworks.

This article documents an idea called the "Event Distributor"
model, where event responders can be decoupled in a type-safe
manner. Although decoupling of events is not new, this article
explores a typed, delegation-based approach without involving an
explicit subscription to solve the problem. The approach also talks
about how to take any interface and convert it to an event-based
(or event-inspired) interface. This article, more importantly,
documents the number of classes needed to implement such a pattern
so that the solution can be served as a copy book.

This article also examines how this kind of implementation for
events relates to messaging systems such as JMS and MQ on one hand,
and on the other, to the event-generation
mechanism of JavaBeans.

Applications of Event Distributors

Before going into describing how Event Distributors can be
implemented, I want to point out the practical uses of Event
Distributors. Event Distributors have applications for such things
as HTTP events and connection pool events in an application server.
For example, HTTP events can be used to detect and respond to such
occurrences as:

  • A request starting or ending.
  • A session being established or terminated.
  • A user logging in or logging out.

In a similar fashion, "connection pool" events can be used to
monitor such events as:

  • A connection being created or closed.
  • A connection being requested from the pool or returned to the
    pool.

Events Seen as Interfaces

An application server, being a container, is a generator of a
number of events, whether they are seen as events or not. Let me
start by considering a typical HTTP pipeline and the typical events
such a pipeline produces:

  • Application start
  • Application stop
  • Begin HTTP request
  • End HTTP request
  • Session start
  • Session end
  • User login
  • User logoff

These events are largely independent of each other, but work
within a specific context. For example, an HTTP begin request will
have the request and response objects as its context, whereas a
session start will have the session object as its context. In that
sense, each event is like a method call, waiting to be answered
either synchronously or asynchronously.

In windowing systems, these events are modeled as messages with a
unique message ID and a set of parameters. Responders to these
events will register based on an event ID and then retrieve the
parameters for that event from the bag of parameters.

The treatment of events is also similar in messaging systems
such as MQ and TIBCO, and to an extent, mirrored in JMS. In these
systems, messages typically leave process boundaries. Messages are
also expected to carry payloads that are large and sometimes even
streams of data. These systems explicitly depend on an intermediate
layer called a "queue," where events are persisted and guaranteed
if necessary. Especially in JMS and EJB 2 MDBs, each event is
mapped to a specific bean in the EJB tier via an onMessage method
on that bean. So if you were to have n number of events,
then you would need n number of beans to respond to those
events.

In contrast, the events described in this article are largely
in-process events that work on in-process entities, which
get passed around via reference and not by value. The problem space
for in-process events is a lot simpler. Taking these facts into
account, I suggest that sets of events can be grouped as a
consolidated interface
. If I were to take the above HTTP-related events and apply these principles, the resulting interface
would look like this:

[prettify]
public interface IHttpEvents
{
   public boolean applicationStart() 
         throws AspireServletException;
      
   public boolean applicationStop() 
         throws AspireServletException;
      
   public boolean sessionStart(
         HttpSession session, 
         HttpServletRequest request, 
         HttpServletResponse response) 
         throws AspireServletException;
      
   public boolean sessionStop() 
         throws AspireServletException;
      
   public boolean beginRequest(
         HttpServletRequest request, 
         HttpServletResponse response) 
         throws AspireServletException;
      
   public boolean endRequest(
         HttpServletRequest request, 
         HttpServletResponse response) 
         throws AspireServletException;
      
   public boolean userLogin(String username, 
         HttpSession session, 
         HttpServletRequest request, 
         HttpServletResponse response) 
         throws AspireServletException;
      
   public boolean userLogoff(String username, 
         HttpSession session, 
         HttpServletRequest request, 
         HttpServletResponse response) 
         throws AspireServletException;

} //eof-class
[/prettify]

Each event is expressed as a method call in the interface. The
parameters of each event have become arguments to the method
representing the corresponding event. As Java uses checked
exceptions, it is best to declare interfaces with an
exception.
One lesson I have learned over the years, while
designing interfaces, is to always have a declared exception to the
interface methods. When I didn't anticipate that, I always paid.
This is not as essential to languages where exceptions are
primarily runtime exceptions and not checked exceptions.

SWIHttpEvents: The Static Wrapper for IHttpEvents

The intent of an event interface is that I can allow an
implementation which can be supplied at run time. For example, I can
go with an implementation that looks like this:

[prettify]
class MyEventResponse implements IHttpEvents
{
//All implemented methods
}
[/prettify]

Then, to generate a beginRequest event, I can use
the following code:

[prettify]
..
IHttpEvents httpEvents = SomeUtility.getHttpEventImplementation();
httpEvents.beginRequest(request,response)
[/prettify]

As I raise events from different parts of the program, it is
tedious to always get the interface and then call the method on the
interface. It is lot easier to just do the following:

[prettify]
..
SWIHttpEvents.beginRequest(request,response)
[/prettify]

Where SWIHttpEvents is a static wrapper class to
the IHttpEvents interface. A fairly complete
definition for the static wrapper class SWIHttpEvents is provided
below.

The code below assumes that the container will provide a
standard set of services such as a factory service, a configuration
service, and a logging service. In the example below, these services
are encapsulated in another static wrapper called
AppObjects, where each application scope object
represents a service.

These services are fairly common in modern containers such as
Spring and "http://www.picocontainer.org/">Pico. Even if one doesn't have
access to these containers, it is not that hard to simulate what is
required here by reading a properties file and instantiating
singletons to represent event handlers. The AppObjects
used here is taken from the container "http://www.activeintellect.com/aspire/docpages">Aspire/J2EE.

If you would like to copy and compile the following code for
your use, you will need to replace all of the calls to
AppObjects with an equivalent call that suits your
environment.

[prettify]
public class SWIHttpEvents
{
//**********************************************
//* A place holder for the implementation   
//**********************************************   
   public static IHttpEvents m_events = null;

//**********************************************
//* static initialization with the implementation   
//**********************************************   
   static
   {
      try
      {
         //Call a factory service to instantiate
         //an event implementation for IHttpEvents
         m_events = 
         (IHttpEvents)
           AppObjects.getObject(IHttpEvents.NAME,null);
      }
      catch(RequestExecutionException x)
      {
        //log the warning
        String msg = 
          "Warn: No http events class available."
          + " No events will be reported";
       
        //use your container to log the message
         AppObjects.log(msg);
       
        //null event handler means no
        //events will be fired.
        m_events=null;
      }
   }
   
//**********************************************
//* Individual delegated methods   
//**********************************************   
   static public boolean applicationStart() 
   throws AspireServletException
   {
      if (m_events == null) return true;
      return m_events.applicationStart();
   }

   static public boolean applicationStop() 
   throws AspireServletException
   {
      if (m_events == null) return true;
      return m_events.applicationStop();
   }

   static public boolean sessionStart(
            HttpSession session,
            HttpServletRequest request,
            HttpServletResponse response) 
   throws AspireServletException
   {
      if (m_events == null) return true;
      return m_events.sessionStart(session
                 ,request,response);
   }

   static public boolean sessionStop() 
   throws AspireServletException
   {
      if (m_events == null) return true;
      return m_events.sessionStop();
   }

   static public boolean beginRequest(
            HttpServletRequest request, 
            HttpServletResponse response) 
   throws AspireServletException
   {
      if (m_events == null) return true;
      return m_events.beginRequest(
               request,response);
   }
   
   static public boolean endRequest(
            HttpServletRequest request, 
            HttpServletResponse response) 
   throws AspireServletException
   {
      if (m_events == null) return true;
      return m_events.endRequest(
             request,response);
   }
   
   static public boolean userLogin(
            String username,
         HttpSession session,
         HttpServletRequest request,
         HttpServletResponse response) 
   throws AspireServletException
   {
      if (m_events == null) return true;
      return m_events.userLogin(username
           ,session,request,response);
   }

   static public boolean userLogoff(
            String username,
         HttpSession session,
         HttpServletRequest request,
         HttpServletResponse response) 
   throws AspireServletException
   {
      if (m_events == null) return true;
      return m_events.userLogoff(username
              ,session,request,response);
   }
}//eof-class
[/prettify]







Initializing the SWIHttpEvents

As SWIHttpEvents is a static wrapper (or a static
proxy) to the actual implementation of IHttpEvents, it
is initialized up front with a preferred copy of an implementation
object. For optimizing calls to individual events, the following
code fragment in the above static wrapper class caches the
implementation object.

[prettify]
   public static IHttpEvents m_events = null;
   static
   {
      try
      {
         //Call a factory service to instantiate
         //an event implementation for 
       // IHttpEvents
         m_events = 
         (IHttpEvents)
           AppObjects.getObject(
             IHttpEvents.NAME,null);
      }
      catch(RequestExecutionException x)
      {
        //log the warning
        String msg = 
          "Warn: No http events class available."
          + " No events will be reported";
       
        //use your container to log the message
         AppObjects.log(msg);
       
        //null event handler means no
        //events will be fired.
        m_events=null;
      }
   }
[/prettify]

Initialization in the above code is carried out using a static
block. The event implementation object is located through a factory
service. Depending on your container, various approaches can be
used. The example above, as pointed out already, is taken from the open
source container that I wrote, called Aspire. In Aspire,
AppObjects is a collection of application-level
objects. The AppObjects static class exposes some of
the public members of these services as static methods for
simplicity. Underneath, in Aspire, a configuration file provides
this name to class binding. Here is an actual example:

[prettify]
request.HttpEvents.classname=\
com.ai.servlets.HttpEventDistributor
[/prettify]

Aspire uses this binding to return an implementation of
IHttpEvents in response to a request for creating an
object with a symbolic name of HttpEvents. Aspire by
default assumes the instance is a singleton, while the class
designer has the ability to override that capability if needed. If
the above line is not available in the configuration file, then
null is returned. In that case,
SWIHttpEvents is coded in such a way that events are
ignored and the code does not throw any exceptions. The following
method implementation is an example of that policy:

[prettify]
   static public boolean userLogin(
      String username,
      HttpSession session,
      HttpServletRequest request,
      HttpServletResponse response) 
   throws AspireServletException
   {
      //if there is no implementation do not
     //call the method.
      if (m_events == null) return true;
     
     //An implementation is available
     //delegate the method to the implementation
      return m_events.userLogin(username,
              session,request,response);
   }
[/prettify]

Looking at a Trivial Implementation for IHttpEvents

Now that a provision is made for implementing and calling the
events let me show you a trivial implementation for the events
interface:

[prettify]
public class LogHttpEvents implements IHttpEvents
{
   public boolean beginRequest(
      HttpServletRequest request, 
     HttpServletResponse response) 
   throws AspireServletException
   {
      AppObjects.log("Info:request begin event");
      return true;
   }
   
   public boolean endRequest(
      HttpServletRequest request, 
     HttpServletResponse response) 
   throws AspireServletException
   {
      AppObjects.log("Info:request end event");
      return true;
   }
   ... so on and so forth
}
[/prettify]

Now I can specify this class as my implementation of the
IHttpEvents interface as follows:

[prettify]
request.HttpEvents.classname=com.ai.servlets.LogHttpEvents
[/prettify]

So far, I have shown how to take a disconnected set of events and
declare them as a cohesive typed interface. I have then shown how
a static wrapper to this interface simplifies the publishing of
these events. But there is more work to be done to really have this
interface simulate event behavior. I will now turn my attention to
an implementation called EventDistributor that allows for
multiple subscriptions.

Need for an Event Distributor

There is an obvious drawback, or rather a limitation, to the
above single point implementation. The static wrapper assumes that
I can supply only one implementation. What if I want to accomplish
two tasks for an event? This is similar to having two subscribers
for an event. This issue is addressed by an event distributor. An
event distributor acts like any other implementation of
IHttpEvents but distributes the events on to a "chain"
or "bus" of implementations. Thus, an event distributor will delegate
the events to any number of implementations. Here is sample
configuration file to accommodate this design:

[prettify]
request.HttpEvents.classname=com.ai.servlets.HttpEventDistributor
request.HttpEvents.eventchain=EventHandler1,EventHandler2,EventHandler3

request.EventHandler1.classname=com.mypackage.mysubpackage.EventHandler1
request.EventHandler2.classname=com.mypackage.mysubpackage.EventHandler2
request.EventHandler3.classname=com.mypackage.mysubpackage.EventHandler3
[/prettify]

The implementation of the HttpEventDistributor is
such that it will call each event handler for each method, and if
the method returns true, it will continue to call event handlers
down the chain. If the method returns false, it will terminate the
chain. An exception can also break the chain. All of this is just
a strategy. Different sorts of event distributors can be written
with different strategies. Here is the code for the event
distributor for this particular strategy.

[prettify]
public class HttpEventDistributor 
    implements IHttpEvents, IInitializable
{
   //Keep a list of IHttpEvent handlers
   private List m_eventHandlers=new ArrayList();
   
   //Load up the handlers at initialization 
   // time of this factory loaded class
   
//**********************************************
//* initialize method   
//**********************************************   
   public Object initialize(String requestName)
         throws RequestExecutionException
   {
      //The event handlers are specified as a 
     //comma separated string of symbolic event 
     //handler names.
     
      String eventHandlers = 
        AppObjects.getValue(
          requestName + ".eventchain",null);
         
      if (eventHandlers != null)
      {
         Vector v = 
        Tokenizer.tokenize(eventHandlers,",");

         for (Enumeration e = v.elements();
            e.hasMoreElements();)
         {
            String eventHandler = 
             (String)e.nextElement();
            try
            {
               //Use the factory interface again 
            // to instantiate the event
            // handler object based on the 
            // symbolic name.
            
               IHttpEvents ieh = 
               (IHttpEvents)AppObjects
                  .getObject(eventHandler,null);
               m_eventHandlers.add(ieh);
            }
            catch(RequestExecutionException x)
            {
               AppObjects.log("log error",x);
               continue;
            }
         }
      }
   }

//**********************************************
//* beginRequest implementation   
//**********************************************   
   //Call each handler's method
   public boolean beginRequest(
        HttpServletRequest request, 
      HttpServletResponse response) 
   throws AspireServletException
   {
      Iterator itr = m_eventHandlers.iterator();
      while(itr.hasNext())
      {
            IHttpEvents ihe = (IHttpEvents)itr.next();
            boolean rtncode = ihe.beginRequest(request,response);
            if (rtncode == false) return false;
      }
      return true;
   }
   
//**********************************************
//* endRequest implementation   
//**********************************************   
   //Another example
   public boolean endRequest(
        HttpServletRequest request, 
      HttpServletResponse response) 
   throws AspireServletException
   {
      Iterator itr = m_eventHandlers.iterator();
      while(itr.hasNext())
      {
            IHttpEvents ihe = 
           (IHttpEvents)itr.next();
           
            boolean rtncode = 
           ihe.endRequest(request,response);
           
            if (rtncode == false) return false;
      }
      return true;
   }
   
   .. so on and so forth
}
[/prettify]

Again, for the sake of space, I have used some of the facilities
from Aspire in the above code. You should be able to substitute
similar facilities from your container or write equivalent
code.

An interesting insight into the workings of an event distributor
is that it breaks down the general notion that an interface is
implemented by a single implementation
. In this case, an
interface is seen as a gateway to a chain of implementations. In
this respect, an in-process event mechanism is lot closer to the
concept of "delegates" as laid out in C# than to the MDBs in
EJB2. I certainly do not mean to imply one version is better than
the other, as I believe both approaches are valid in their
respective domains.







Responding to only Selected Events

If I am implementing the IHttpEvents directly, I am
forced to implement all of the events (or methods), whether I am
interested in them or not. This can be unnecessarily tedious.
Instead, I usually extend the LogHttpEvents, which does
nothing but log each event, and then implement the only one or two
methods that concern the event at hand.

To draw a parallel, while editing this article, the editor
pointed out that in the AWT/Swing/JavaBeans world, there's a
concept of an "adapter" implementation, which no-ops all interface
methods, so you subclass and override only those methods that
interest you. That is what is exactly happening here, as well in the
following example:

[prettify]
public class HttpRequestCharacterEncodingHandler 
extends LogHttpEvents
{
   public boolean beginRequest(
        HttpServletRequest request, 
      HttpServletResponse response) 
   throws AspireServletException
   {
      try
      {
         String enc = request.getCharacterEncoding();
         if (enc == null)
         {
            String encoding = 
            AppObjects.getValue(
               m_requestName 
              + ".encoding", 
              "UTF-8");
              
            request.setCharacterEncoding(encoding);
            AppObjects.log(
           "Info:setting encoding to " + encoding);
         }
         return true;
      }
      catch(UnsupportedEncodingException x)
      {
         throw new AspireServletException(
          "report error",x);
      }
   }//eof-function
}//eof-class
[/prettify]

How to Raise an Event?

So far I have documented primarily how to respond to events.
Here is an example of how to raise an event using the
SWIHttpEvents wrapper.

[prettify]
   private boolean login(
      String username, 
      String password, 
      HttpServletRequest request, 
      HttpServletResponse response, 
      HttpSession session)
   throws RequestExecutionException, 
          AuthorizationException, 
          AspireServletException
   {
      boolean validPassword = 
        yourLogin(username,password);
       
      if (validPassword == false)
      {
       //Invalid password
       throw new AuthorizationException(..);
      }
          
      //Good password
      ServletCompatibility.
        putSessionValue(session
         ,AspireConstants.ASPIRE_USER_NAME_KEY
         ,username);
                 
     
      SWIHttpEvents.userLogin(username
             ,session
                 ,request
                 ,response);
     
      
      ServletCompatibility
             .putSessionValue(session,
                     AspireConstants.ASPIRE_LOGGEDIN_STATUS_KEY
                         , "true");
      
      return true;
   }
[/prettify]

In this example, as part of the login process, a successful
login event is raised using the highlighted code segment.

Event Distributor Class Diagram

The general pattern of the event distributor can be summarized
pictorially using the class diagram in Figure 1.

Figure 1
Figure 1. Event Distributor class diagram (click for full-size version)

In the diagram, client sections of the code (such as "client1"
and "client2") raise events by obtaining a reference to an
IHttpEvents and calling methods on that interface.
Each method corresponds to an equivalent event. Instead of
repeating this process, every time a client needs the event
interface, this is handled by an intermediate static
class
called SWIHttpEvents.

The event interface could have been implemented by any number of
implementations, one of which is a concrete event distributor
called HttpEventDistributor and the other a
default implementation for the event interface.

The default implementation acts as an abstract implementation
for the derived event-specific implementations, whereby the derived
implementation can pick and choose the events to implement. In the
diagram, two such event handlers are being shown. It is also
possible that a given event may be implemented by more than one
implementation.

This class diagram can serve as a guide for implementing new
event interfaces. In particular, the naming conventions and the
various classes listed here could serve as a copy book.

JavaBeans and Property Change Events

Earlier in this article, I compared the events discussed so
far with the macro-level messaging systems such as JMS. On the
other side of the spectrum, at the micro level, many Java
programmers are quite familiar with the property change events in
the JavaBeans architecture. In this architecture, one can register
property change listeners. In this scenario, when the value of an
attribute of a bean changes, a whole number of listeners can be
invoked. This model is often used in UI toolkits and frameworks.
The events I have noted in this article are not granular enough to
be tied to a set of properties but exist independently, and also
don't adhere to the concept of a "value change," where one could
track the value before and after an event. I believe these are
different semantics and require separate treatment.

Parallels to Remoting Proxies

The approach suggested here has parallels to other component
architectures such as dynamic proxies in Java, remoting in C#,
delegates in C#, and even EJBs. Although the examples show a
straightforward, non-reflection-based approach, it is possible to
redesign the principles using reflection, whereby a number of
intermediate classes can be eliminated.

It is even conceivable to design language constructs for
interfaces where a natural delegation such as this is inherent in
the language. For example, I can envision the following:

[prettify]
public static class SWIHttpEvents 
defined_as_a_multiple_delegate_for IHttpEvents
{
//No other code necessary
}
[/prettify]

All the code above will be automatically enabled. To support
this, perhaps configuration should become a natural part of the
language as well. Also, bringing this higher level of abstraction to
a language allows programmers to think of their interfaces as
delegated events where appropriate.

Resources

width="1" height="1" border="0" alt=" " />
Satya Komatineni is the author of AspireJ2EE (http://www.activeintellect.com), an open source web development RAD tool for J2EE/XML.
Related Topics >> Programming   |