You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@struts.apache.org by David Geary <sa...@tri-lakesonline.net> on 2000/12/20 16:09:28 UTC

Proposal: RetroFit Struts with the Delegation Event Model

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.

Re: Proposal: RetroFit Struts with the Delegation Event Model

Posted by David Geary <sa...@tri-lakesonline.net>.
"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.


Re: Proposal: RetroFit Struts with the Delegation Event Model

Posted by David Geary <sa...@tri-lakesonline.net>.
"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.


Re: Proposal: RetroFit Struts with the Delegation Event Model

Posted by Ted Husted <ne...@husted.com>.
On 12/26/2000 at 6:57 PM Craig R. McClanahan wrote:
> 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).

I'm not sure of all the implications of this suggestion, but fools rush
in ...

As David mentioned in a followup, the Barracuda/Enhydra gang is working
on an event framework, with the premise that they can then hook it up
to a presentation framework, including Struts and their own. So, I
wonder if the event framework could be another component, that could be
used with or without Struts -- like the custom tag library. The event
framework might link up to the ActionServlet, like a datasource. If a
listener had something to say to Struts, it could post it in the form
of a HTTP Request to the event queue. Struts would fire it's own events
into the queue (if installed), and listen for HTTP Requests posted by
others. 



-- Ted Husted, Husted dot Com, Fairport NY USA.
-- Custom Software ~ Technical Services.
-- Tel 716 425-0252; Fax 716 223-2506.
-- http://www.husted.com/



Re: Proposal: RetroFit Struts with the Delegation Event Model

Posted by "Craig R. McClanahan" <Cr...@eng.sun.com>.
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.


Re: Proposal: RetroFit Struts with the Delegation Event Model

Posted by David Geary <sa...@tri-lakesonline.net>.
These are excellent questions, which I've answered below.

Some of you have suggested the Barracuda (formerly Rocks) event model,
which I'm looking into. You can read more about it at
http://xmlc.enhydra.org/EventHandler/.

Ted Husted wrote:

> Could the model be extended to optionally log events as they are fired,
> for debugging?

It could, but you could implement a listener that logged events without
extending the event model.

> Could the listeners be loaded via a configuraton file, like Struts
> actions?

They could, and in most cases, they probably should.

> Could a ACTION_PERFORM be added to completely supercede the current
> control flow?

Augment, yes. Supercede, no.

Basically, the way this works is that Struts, meaning, for the moment,
ActionServlet, fires events when it does something interesting. I used the
Action.perform method for something interesting -- the action servlet fires
events to actions just before, and immediately after the servlet calls
Action.perform for every action. Those actions fire events to register
listeners.

You can think of Struts as a garden hose. The water running through the
hose represents Struts events, such as actions being told to perform. I
want to poke holes in that hose, so that developers can siphon those
events.

> In the Form Resubmission example:
>
> Are the "sensitive" forms essential those that should only be submitted
> once per session (if at all)?

No. Sensitive forms are forms with sensitive actions. In the example, the
form in
Figure 2 is a sensitive form because it's action is new-account-action.
That
action is sensitive to form resubmissions.

Every sensitive action must be submitted by a sensitive form for the
form resubmission example to work.

> Could this handle the common occurence of a payment form being
> submitted more than once, resulting in multiple payments?

That's exactly what the form resubmission example does, other than
providing
a simple example of how you'd use the proposed delegation event model.


david


Re: Proposal: RetroFit Struts with the Delegation Event Model

Posted by Ted Husted <ne...@husted.com>.
Could the model be extended to optionally log events as they are fired,
for debugging?

Could the listeners be loaded via a configuraton file, like Struts
actions?

Could a ACTION_PERFORM be added to completely supercede the current
control flow?

In the Form Resubmission example:

Are the "sensitive" forms essential those that should only be submitted
once per session (if at all)?

Could this handle the common occurence of a payment form being
submitted more than once, resulting in multiple payments?

(It's been my observation that with many applications sometimes the
request makes it through, but the response never comes back, and
there's no clear way to tell if your order has been received.)

-- Ted Husted, Husted dot Com, Fairport NY USA.
-- Custom Software ~ Technical Services.
-- Tel 716 425-0252; Fax 716 223-2506.
-- http://www.husted.com/



Re: Proposal: RetroFit Struts with the Delegation Event Model

Posted by "Craig R. McClanahan" <Cr...@eng.sun.com>.
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.


Re: Proposal: RetroFit Struts with the Delegation Event Model

Posted by Robert Leland <Ro...@freetocreate.org>.
Also the Enhydra project did a survey of competitors strengths and
weaknesses,
and developed a eventhandler model with proof of concept code.

   http://xmlc.enhydra.org/EventHandler/

It is an excellent example of taking lessons learned from other projects
including struts and to improve on it.