"Craig R. McClanahan" wrote:
> * As fleshed out, the only events reported with this model so far
> are before and after an Action's perform() method is called.
> The abstract talks about building listeners for all "interesting"
> events. If we're going to do a listeners model, I think we should
> extend it to basically all of the processXxx methods, not just
> processPerform().
Yes.
> * If we go with generalized events, putting the firing logic inside
> Action seems wrong -- general purpose support classes, or
> public methods inside ActionServlet, seem more appropriate.
Agreed. I vote for a support class.
> * Given that there will be many more events, we've got some
> representation choices:
>
> * Single ActionEvent class, or subclasses for various types
> of events that have different properties (some events care
> about the current ActionMapping and some don't).
I envision an event hierarchy.
> * SIngle registrations of ActionListeners that receive all types of
> events, or separate registrations for separate event families?
The latter would be more type-safe, and is probably preferred.
> * I also have a couple of nit-picky type thoughts:
>
> * Event type codes inside the ActionEvent seem redundant,
> given that the type is implicitly defined by which listener
> method you call.
True, but that assumes that the handler will never pass the event
to other methods that need to distinguish the event type -- the
information's not available to those methods.
> * An ActionEvent, or the ActionListener that receives it,
> should have knowledge of the ActionServlet
> it is associated with, to provide access to underlying resources
> provided by the servlet. (The whole event listener model is
> intimately tied to Struts anyway, so this does not seem onerous).
Agreed.
> * We need to use collection classes (with an implementation I'm
> currently working on) that do not require lots of synchronization locks
> or new object creations when processing event notifications, since
> they happen on every request.
Ok.
david
>
>
> Thoughts?
>
> Craig
>
> David Geary wrote:
>
> > ABSTRACT
> >
> > It's often convenient, and sometimes necessary, to handle Struts events,
> > such as when an action has its locale set, or when the action servlet
> > processes an action's form. This document proposes retrofitting Struts
> > with the delegation event model. That model, which is used by the AWT
> > and
> > Swing, makes event handling simple, flexible, and scalable.
> >
> > CREDITS
> >
> > Delegation and Event Model for Struts? -- posted to struts-dev by
> > Robert Leland
> >
> > INTRODUCTION
> >
> > Currently, you can use inheritance to handle Struts events like those
> > described above. Typically, that means extending ActionServlet and
> > overriding a protected method, such as
> > ActionServlet.processActionPerform.
> >
> > Inheritance-based event handling is inflexible and does not scale well
> > because event sources and listeners are tightly coupled at compile time.
> > This was evident to AWT engineers, who replaced the AWT's original
> > inheritance-based event model with the delegation event model.
> >
> > The delegation event model, which has its roots in java.util, implements
> > the Observer design pattern to loosely couple event sources and event
> > listeners at runtime. That loose coupling makes it easy to associate
> > disparate types of objects, so that event listeners can easily react to
> > changes in event sources.
> >
> > STRUTS AND THE DELEGATION EVENT MODEL
> >
> > So what does it mean to retrofit Struts with the delegation event model?
> > It means that Struts will fire events when it performs certain
> > functions. You can register with Struts as an event listener, and
> > handle events as you see fit.
> >
> > This proposal advocates firing events for all interesting Struts
> > functions; for example, the action servlet should fire a robust set of
> > events for processing actions and forms, performing mappings, etc.
> > Implementing support for those events follows the same
> > design pattern discussed in this proposal for implementing action
> > events.
> >
> > This proposal illustrates how to modify Struts to fire events just
> > before, and immediately after, a Struts action has its perform method
> > invoked. Those events are hereafter known as action events.
> >
> > IMPLEMENTING ACTION EVENTS AND ACTION LISTENERS
> >
> > Getting Struts to fire action events is easy. First, we define a
> > listener interface and an event class:
> >
> > org/struts/apache/event/action/ActionListener.java
> > org/struts/apache/event/action/ActionEvent.java
> >
> > Here's the listing for ActionListener:
> >
> > public interface ActionListener {
> > public void beforeActionPerform(ActionEvent event)
> > throws ServletException;
> > public void afterActionPerform(ActionEvent event)
> > throws ServletException;
> > }
> >
> > ActionListener methods are passed instances of ActionEvent. Here's the
> > listing for that class:
> >
> > public class ActionEvent extends java.util.EventObject {
> > public static final int BEFORE_ACTION_PERFORM=0,
> > AFTER_ACTION_PERFORM=1;
> > private int eventType;
> > private HttpServletRequest request;
> > private HttpServletResponse response;
> > public ActionEvent(Action action, int eventType,
> > HttpServletRequest request,
> > HttpServletResponse response) {
> > super(action); // specifies action as the event source
> > this.eventType = eventType;
> > this.request = request;
> > this.response = response;
> > }
> > public int getEventType() { return eventType; }
> > public HttpServletRequest getRequest() { return request; }
> > public HttpServletResponse getResponse() { return response; }
> > }
> >
> > Through action events, action listeners have access to:
> >
> > event type (BEFORE_ACTION_PERFORM, AFTER_ACTION_PERFORM)
> > action
> > request
> > response
> >
> > HANDLING ACTION EVENTS
> >
> > Here's how you use action events and listeners:
> >
> > // first, implement a listener that handles action events
> >
> > public class MyListener implements
> > org.apache.struts.event.ActionListener {
> > public void beforeActionPerform(ActionEvent event) {
> > // handle event
> > }
> > public void afterActionPerform(ActionEvent event) {
> > // handle event
> > }
> > }
> >
> > // Then register your listener with an action:
> >
> > someAction.addActionListener(new MyListener());
> >
> > Thereafter, MyListener.beforeActionPerform and
> > MyListener.afterActionPerform will be called before and after
> > someAction's perform method, respectively.
> >
> > Let's see what changes need to be made to Struts to make this work.
> >
> > STRUTS MODIFICATIONS FOR SUPPORTING ACTION EVENTS
> >
> > Only two Struts classes need to be modified to support firing action
> > events: Action and ActionServlet. Methods are added to the Action class
> > for registering action listeners and firing events:
> >
> > // the following is added to org.apache.struts.action.Action:
> >
> > import java.util.Enumeration;
> > import java.util.Vector;
> > import org.apache.struts.event.action.ActionEvent;
> > import org.apache.struts.event.action.ActionListener;
> >
> > public class Action {
> > ...
> >
> > protected static Vector listeners = new Vector();
> >
> > public void addActionListener(ActionListener listener) {
> > listeners.addElement(listener);
> > }
> > public void removeActionListener(ActionListener listener) {
> > listeners.remove(listener);
> > }
> > public void beforeAction(ActionEvent event)
> > throws ServletException {
> > fireEvent(event);
> > }
> > public void afterAction(ActionEvent event)
> > throws ServletException {
> > fireEvent(event);
> > }
> > protected void fireEvent(ActionEvent event)
> > throws ServletException {
> > Enumeration it = listeners.elements();
> >
> > while(it.hasMoreElements()) {
> > ActionListener listener =
> > (ActionListener)it.nextElement();
> >
> > switch(event.getEventType()) {
> > case ActionEvent.BEFORE_ACTION_PERFORM:
> > listener.beforeActionPerform(event);
> > break;
> > case ActionEvent.AFTER_ACTION_PERFORM:
> > listener.afterActionPerform(event);
> > break;
> > }
> > }
> > }
> > ...
> > }
> >
> > Now Struts actions can fire action events to registered action
> > listeners. ActionServlet.processActionCreate is modified to call
> > Action.fireEvent, like this:
> >
> > protected void processActionPerform(Action action,
> > ActionMapping mapping,
> > ActionForm formInstance,
> > HttpServletRequest request,
> > HttpServletResponse response)
> > throws IOException,
> > ServletException {
> > action.fireEvent(new ActionEvent(action,
> > ActionEvent.BEFORE_ACTION_PERFORM,
> > (HttpServletRequest)request,
> > (HttpServletResponse)response));
> >
> > // Perform the requested action
> > ActionForward forward =
> > action.perform(mapping, formInstance, request, response);
> >
> > action.fireEvent(new ActionEvent(action,
> > ActionEvent.AFTER_ACTION_PERFORM,
> > (HttpServletRequest)request,
> > (HttpServletResponse)response));
> > ...
> > }
> >
> > CONCLUSION
> >
> > Struts will be a more powerful and extensible framework if developers
> > can handle Struts events, which can be accomplished as outlined in this
> > proposal with the delegation event model.
> >
> > This proposal has illustrated modifying Struts to fire action events. If
> > this proposal is accepted, Struts should be modified to fire many
> > meaningful events. The ActionServlet class alone is rife with methods
> > that should fire events; for example, initApplication,
> > processActionForm, processLocale, etc.
> >
> > A practical use of the action events discussed in this proposal can be
> > found in the proposal 'Tokens and Events: Handling Illicit Access to
> > Sensitive Pages'. That proposal implements an action listener
> > that restricts access to actions that are sensitive to the back button,
> > reload button, or bookmarks.