I like the basic idea of event listeners for Struts events.  About the
particular proposal, though, I've got some comments and questions I would like
feedback on.

* 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().

* 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.

* 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).

    * SIngle registrations of ActionListeners that receive all types of
      events, or separate registrations for separate event families?

* 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.

    * 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).

    * 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.

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.

Reply via email to