Barracuda Event Model - Open Issues
barracuda.gif (11456 bytes)This document documents open questions about the event model that still need to be addressed.
  1. Deferred Delivery of Events (*Solved*)
  2. Dispatch Queue (*Solved*)
  3. Event Pooling (*Solved*)
  4. Event Aliasing (*Solved*)
  5. Web.xml Integration (*Partially Solved*)
  6. Recursive Dispatching (*Solved*)
  7. Runtime Error Notification
  8. Guaranteed Event Delivery (*Solved*)
  9. ClientSideRedirect (*Solved*)
  10. Service Implementation

Deferred Delivery of Events

The basic issue here is that there will be events that occur on the server that really need to update the client. For instance, a user's session may timeout (impacts one user). Or, data in a database may change (impacts all users sharing that data). Given the nature of HTTP there is no way for the server to initiate a callback, so somehow this event must be taken into consideration the next time the client makes a request of the server (which may be never).

Solution: To solve this problem, when a server side event occurs, some form of persistant state should be updated. Then, an event handler should be installed on HttpRequestEvent so that it gets invoked prior to the dispatching of all client requests (due to the polymorphic nature of all HttpRequestEvents). This event handler should check the persistant state, and if necessary override the incoming client event and insert a new event in the queue for dispatch.


Dispatch Queue

Right now, the only thing event listeners can do is append events to the DispatchQueue. While this is helpful, it seems obvious to me that there needs to be more advanced functionality. There will be times when it would be nice to remove events from the queue, or to insert the new event prior to the other events. So the question is, to what extent do we open it up?

I thought at first we should just make all the methods public, giving each listener full access to the queue. Upon further reflection, I don't think this is the best solution, because I think there should be an inherant contract that if an event is generated a listener is guaranteed to be able to receive it. If someone could remove it from the queue before the listener received it, that contract would be broken.

My inclination at this point is to expand the interface to allow access to the other events in the queue. A listener would not be able to remove or change the order of the events, and would still only be able to append events onto the queue. However, a listener could go through and mark all events in the queue as handled. This would prevent their delivery to any listeners except those which had overridden notifyAlways() to be true (typically something like a logging facility). So, an event could be removed from the queue "in effect" by marking it handled, although a listener that requires that event to be delivered (whether handled or not) could still get to it.

Solution: We have added the ability to retrieve a list of events in the queue. This still prevents handlers from removing events from the queue, but does allow them to mark existing events in the queue as handled, so that any new events they insert into the queue will get handled instead.


Event Pooling

There are several issues here.

  1. If we are going to allow listeners (and possibly other events) to fire new events, should we consider expanding access to the event pool?

    At first I though this was a good idea, but I have since changed my mind. First, both listeners and events would be posting events to the queue, and they would never know when those events were actually delivered and no longer in use. Consequently, there would be no mechanism to release them from the pool (we'd have to wait for them to time out)..

    Second, the real reason for using the pool in the first place is to avoid the performance penalties associate with using reflection to instantiate the event objects. In both cases given previously (events and listeners) for generating new events, the events could be generating without using reflection (ie. BaseEvent blah = new BlahEvent()). Normal instantiation does not suffer the same performance penalties as reflection, and so shouldn't require event pooling.

    The only real issue I can forsee with this is if we have a scenario where we end up needing to generate an additional event for every HTTP generated event coming into the system. In this case, performance might still be adversely impacted. However, I don't see any way around it given the limitations described in #1 above.

  2. Right now when events are checked out of the pool, a default timeout period is used. Should we be able to override this value to provide a custom setting for each event? In other words, if I need to check out a ReallyIntensiveProcessingEvent, should I be able to specify a longer timeout because I know it's going to take longer?

    Again, I thought this was a good idea at first, but upon further consideration I have changed my mind. This might be valid if we were going to allow listeners and such access to the pool. However, based on the previous section, I don't think this is valid. Consequently, the only source using the pool will typically be the ApplicationGateway, which won't have any inherant knowledge about the time-it-takes-to-handle aspect of the event anyway. In other words, the servlet will not have any way of knowing whether a ReallyLongEvent takes any more or less time to handle than a ReallyQuickEvent, because from it's perspective, they're both just class names.

So...I think we leave this one as is. If anything, we need to benchmark the pool to see how much difference it actually makes in the firstplace.

Solution: No expanded access to the EventPool. Only the ApplicationGateway should use it (and even then, it's optional).


Event Aliasing

We need to consider how to allow for event name mapping. By this, we mean that we need to find a way to represent events in Links by a name that is shorter (and more understandable) than the fully qualified event class name. Joel Boehland has done some initial prototying in this are and has submitted some sample code which will allow the event model to support this concept.

We need to review this code for potential ramifications. One that comes to mind immediately is: what if I have two events that try and map themselves to the same name? For instance, what if

org.enhydra.barracuda.examples.ex1.RenderEvent
org.enhydra.barracuda.examples.ex2.RenderEvent

both map to a name of "Render.event"? Should the event be delivered to both? Or would that be an error?

From my perspective a mechanism like this would primarily benefit systems where the designer might manually code the link into the page or the user needs to bookmark a page. Shorter names are more intuitive and user frindly. However, if we get to the point where we have a server-side component binding itself to the page and generating the link name, then it wouldn't be near as much of an issue to use the full name.

Solution: When adding events and listeners to the event broker, we convert the class names into all possible aliases. For instance, com.foo.blah.TestEvent would map to four different aliases:

  • com.foo.blah.TestEvent
  • foo.blah.TestEvent
  • blah.TestEvent
  • TestEvent

These aliases are stored in the event broker, so they can be used later to look up the fully qualified id names.

When the ApplicationGateway converts event names and listener ids to their fully qualified values, it queries the event broker. The event broker cross references the name to the fully qualified ID. If there are duplicates, an InvalidClassException is thrown because the alias is ambiguous. The ApplicationGateway will catch this error and treat it as an invalid event name.

By default, the DefaultBaseEventListener and DefaultBaseEvent implementations both generate short event names, although this feature can be controlled by the DefaultBaseEvent.USE_ID_ALIASES. Even if you are using short event names, events can be modified on an individual basis, and base event listener factories can always choose to return a fully qualified name if desired.


Web.xml Integration

Olegs Denisovs has asked for better integration with the web.xml file. As I understand it, there are two specific requests.

  1. Provide the ability for event handlers to get access to the web.xml file.
  2. Make it possible to register event handlers from within the web.xml file.

The first is self explanatory. This second request would allow developers to configure an application without recompiling or custom coding. It's essentially an assembly mechanism, and it sounds initially like something that Struts uses (I'm not positive on the specifics here).

I need to do a lot more thinking on this one, as well as talk to Olegs for more details. Initially, I think it's a valid request, but we will need to really consider how best to implement it. One of the biggest downsides of doing this kind of thing from an XML file is that you lose the advantages of strong typing. If the mechanism is voluntary, however, it's the developer's choice and there are certainly times when this flexibility is worth it.

Solution 1: Handlers can get access to the ServletContext and ServletConfig structures by EventContext object. In addition, handlers can use SimpleServiceFinder to iterate up the event gateway hierarchy and find the ApplicationGateway or whatever other "service" they need.

Solution 2: This is really still just a partial solution. We have defined a new interface called ApplicationAssembler. This interface has two methods called "assemble". The ApplicationGateway class has been modified to look for several key parameters:

  • ApplicationAssembler (the assembler class name)
  • AssemblyDescriptor (the XML file name)
  • SaxParser (opt - the class name of the parser to be used)

If the first two parameters are present the ApplicationGateway will attempt to instantiate the assembler, and then it will invoke it's "assemble" method, passing in the specified XML file. The ApplicationAssembler is then free to parse the XML file and dynamically set up an event hierarchy from that.

Right now, there is a DefaultApplicationAssembler in the experimental package. This illustrates a basic approach to assembly which should server as a starting point for assembly discussions. It should be noted, however, that we still don't have a clear consensus of exactly how the assembly process should be approached, so this implementation will almost certainly change. It may be that the whole assembly mechanism actually gets broken off into a separate project.


Recursive Dispatching

Simon Tuffs has wisely pointed out that typically, one of the big gotchas with event driven systems is the problem of recursion: what happens when a programmer inadvertently writes handlers so that Event A causes Event B to be dispatched, which in turn re-dispatches Event A. There are cases where this is valid behavior, but in most situations it's not, and when it happens, it can quickly cause the whole system to crash.

So the question is: how do we prevent this kind of fatal catastophe from occuring? Or at least make it less likely to happen?

Solution: First of all, much of this can be prevented by sound programming practices: event handlers should never dispatch events through the event broker, but rather should just add events to the event queue. Even this won't totally solve the problem, however.

I think the best solution is to keep track of how many times events get added to the queue, and if it exceeds a certain configurable number, then automatically through some kind of exception. This would prevent the worst case scenarios, and in the event that there really is a valid reason for doing recursive dispatching, the developer can always override the specific limit.

To this end, we have added a constant to DefaultEventDispatcher called MAX_DISPATCH_QUEUE_DEPTH which controls how deep the "handled" queue is allowed to get. If the depth exceeds this value an exception is thrown. This effectively eliminates recursive event dispatching.


Runtime Error Notification

Ideally, we'd like to catch as many errors as possible at compile time. However, there will always be some errors that only occur at runtime. For instance:

  1. If a developer creates a link using HTMLLink that is too long (>240 chars), it might be kind of nice for that error to be logged, even though flow could continue.
  2. If there are are duplicate event aliases, this is a condition of which the developer should really be notified. In this case, flow can again continue...it will not necessarily be a problem unless someone tries to access one of the event handlers by an alias.

We can always log this kind of occurence to the error console, but I am wondering if it might be nice to also email the developer.  So...here's the question: should the event model

  • a) have the concept of a admin email associated with an ApplicationGateway?
  • b) the ability to optionally notify the admin via email if certain events occur within the system?

Guaranteed Event Delivery

One of the potential problems with the current 2 Phase dispatch policy of the DefaultEventDispatcher is that you really can no longer guarantee that an event added to the queue will always be handled. For instance, if we are in the response phase and we add a non-response event, it will not get dispatched as currently implemented.

We could add logic so that after dispatching the Response events we loop and make sure there aren't any more request events, but then we get right back into the same problem: what happens if a request event handler adds another response event to the queue?

Should we then iterate repeatedly through the 2 phase dispatch until both Request and Response lists are empty? This seems like it would solve the problem and once again guarantee delivery...I need to think about this for implications.

Solution: We iterate repeatedly until both the Request and Response lists are empty.


Client Side Redirect

There is a problem with the ClientSideRedirectException in that you always lose your event context. There are occasions where you want to keep it.  In fact, this is probably true in the majority of cases. Generally, the reason for throwing the redirect is just to get the users browser to reflect a new URL. So we need some way of saving the context in the session and then when the event comes back in, reusing the event context instead of recreating it.

Solution: This has been fixed by internally catching the ClientSideRedirectException, storing the statemap in the users' session, and then rethrowing the exception. When the client request returns, the state is reconstituted from the user's session and handling continues, just as if we never left the server. Very cool.


Service Implementation

Basically, we need a way to install services (preferably through the web.xml file) which can then be located and used by any event gateways in the system. The question here is not so much "How to do it" but rather "Which way is best".

Given the presence of the event gateway hierarchy, it's very easy to use the SimpleServiceFinder to locate a service by class name in the hierarchy. It would be equally trivial to add some kind of "service" parameter to the web.xml file which would allow us to dynamically specify services for the application gateway.

The question here is should those services have some kind of lifecycle associated with them? (ie. one that matches the servlet lifecycle). And how closely should we mirror the EE 4.0 Services architecture? What about dependencies? (ie. we don't want to make anything depend on J2EE or Enhydra per se).

As an example of where I'd like to go with this...it'd be nice to be able to make the Enhydra Application object available as a service. I can think of other instances where we'd like to do something similar.

For all the latest information on Barracuda, please refer to http://barracuda.enhydra.org
Questions, comments, feedback? Let us know...
Copyright 1997-2002 Lutris Technologies, Inc. All rights reserved.