This document documents open questions about the
event model that still need to be addressed.
- Deferred Delivery of Events
(*Solved*)
- Dispatch Queue (*Solved*)
- Event Pooling (*Solved*)
- Event Aliasing (*Solved*)
- Web.xml Integration (*Partially
Solved*)
- Recursive Dispatching
(*Solved*)
- Runtime Error Notification
- Guaranteed Event Delivery
(*Solved*)
- ClientSideRedirect (*Solved*)
- 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.
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.
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.barracudamvc.examples.ex1.RenderEvent
org.barracudamvc.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.
- Provide the ability for event handlers to get access to the web.xml file.
- 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:
- 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.
- 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. |