Return-Path: Mailing-List: contact struts-dev-help@jakarta.apache.org; run by ezmlm Delivered-To: mailing list struts-dev@jakarta.apache.org Received: (qmail 96297 invoked from network); 27 Dec 2000 19:36:30 -0000 Received: from unknown (HELO dc-mx06.cluster0.hsacorp.net) (209.225.8.16) by h29.sny.collab.net with SMTP; 27 Dec 2000 19:36:30 -0000 Received: from [208.243.240.57] (HELO tri-lakesonline.net) by dc-mx06.cluster0.hsacorp.net (CommuniGate Pro SMTP 3.4b1) with ESMTP id 16529829; Wed, 27 Dec 2000 12:35:57 -0700 Message-ID: <3A4A459A.6D7759C1@tri-lakesonline.net> Date: Wed, 27 Dec 2000 12:40:10 -0700 From: David Geary X-Mailer: Mozilla 4.7 [en] (WinNT; I) X-Accept-Language: en,fr,zh-CN,de-CH,zh-TW,hr,cs MIME-Version: 1.0 To: struts-dev@jakarta.apache.org CC: "struts-user@jakarta.apache.org" Subject: Re: Proposal: RetroFit Struts with the Delegation Event Model References: <3A40CBA8.63483839@tri-lakesonline.net> <3A495AB5.F384E530@eng.sun.com> Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit X-Spam-Rating: h29.sny.collab.net 1.6.2 0/1000/N "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.