Dear Wiki user, You have subscribed to a wiki page or wiki category on "Struts Wiki" for change notification.
The following page has been changed by MichaelJouravlev: http://wiki.apache.org/struts/StrutsManualActionClasses ------------------------------------------------------------------------------ The goal of an Action class is to process a request and return an ActionForward object that identifies where control should be transferred (e.g. a JSP page, Tile definition, Velocity template, or another Action) to provide the appropriate response. == Action As Simple Service == + In its simplest form Action class handles all incoming requests with one callback method, {{{execute()}}}. Two overloaded versions of this method are available. Choosing one or another depends on your servlet environment. A non-HTTP execute() method has been provided for applications that are not specifically geared towards the HTTP protocol, but most projects will only use the HTTP version since the majority of teams using the framework are focused on building web applications: - When used in "servlet mode", an Action class handles all incoming requests in one callback method, execute(). Two versions of this method are available. Choosing one or another depends on your servlet environment. - - A non-HTTP execute() method has been provided for applications that are not specifically geared towards the HTTP protocol, but most projects will only use the HTTP version since the majority of teams using the framework are focused on building web applications: {{{public ActionForward execute(ActionMapping mapping, ActionForm form, @@ -17, +15 @@ HttpServletResponse response) throws Exception;}}} - Action class is stateless, it looks very much like a a fancy servlet or a web service. But in some ways Action class is more limited than a regular servlet. + HTTP specification recommends to use POST for non-idempotent requests, and to use GET for requests that can be repeated several times with the same outcome. In simpler terms, POST requests should be generally used to modify application state, while GET requests should be used to render a view. - A servlet can handle GET and POST requests in different manner by providing two separate methods, doGet() and doPost(). HTTP specification recommends to use POST request for non-idempotent requests, namely to modify application state. GET reqeust should not affect model state and should be used for requests that can be safely repeated more than one time. + An interactive web application implements the above HTTP requirements using a two-phase input/output protocol. On ''render phase'' (or output phase) the browser requests a view from the server. On ''input phase'' (or submit phase) the browser sends input data to an application, usually by submitting an HTML form. - Action class provides only one method to deal with all incoming requests, therefore actions rarely distinguish a ''render request'' from a ''submit request''. This often leads to convoluted and unmaintable code that can break from an occasional page refresh or from clicking on a Back button. + In Struts the two-phase request/response approach is traditionally implemented with setup/submit pattern and two kinds of actions: + * setup action (pre-action, output action, render action) prepares output data before displaying a JSP page; + * submit action (post-action, input action, accept action) accepts user input. + + This pattern is a de-facto standard, because unlike servlet, that processes GET and POST requests with {{{doGet()}}} and {{{doPost()}}} methods respectively, Action class handles all incoming requests with only one {{{execute()}}} method. inline:setup_submit.gif - Despite its shortcomins, the "servlet mode" is one of more popular uses of Action class.A typical Action class will often implement logic like the following in its execute method: + A typical Setup Action will often implement logic like the following in its {{{execute}}} method: - * Validate the current state of the user's session (for example, checking that the user has successfully logged on). If the Action class finds that no logon exists, the request can be forwarded to the presentation page that displays the username and password prompts for logging on. This could occur because a user tried to enter an application "in the middle" (say, from a bookmark), or because the session has timed out, and the servlet container created a new one. - * If validation is not complete, validate the form bean properties as needed. If a problem is found, store the appropriate error message keys as a request attribute, and forward control back to the input form so that the errors can be corrected. + * Make sure that a user is allowed to see the content of a web page that corresponds to the action. + * Load needed data from database, set up an form bean or arbitrary request- or session-scoped objects. + * Return an appropriate ActionForward object that identifies the presentation page to be used to generate the response. + + A typical Submit Action will often implement logic like the following in its {{{execute}}} method: + + * Validate the form bean properties as needed. If a problem is found, store the appropriate error message keys as a request attribute, and forward control to the input form so that the errors can be corrected. - * Perform the processing required to deal with this request (such as saving a row into a database). This can be done by logic code embedded within the Action class itself, but should generally be performed by calling an appropriate method of a business logic bean. + * Perform the processing required to deal with this request (such as saving a row into a database). This can be done by logic code embedded within the Action class itself, but should generally be performed by calling an appropriate method of a business logic bean. - * Update the server-side objects that will be used to create the next page of the user interface (typically request scope or session scope beans, depending on how long you need to keep these items available). + * Update the server-side objects that will be used to create the next page of the user interface (typically request scope or session scope beans, depending on how long you need to keep these items - * Return an appropriate ActionForward object that identifies the presentation page to be used to generate this response, based on the newly updated beans. Typically, you will acquire a reference to such an object by calling findForward on either the ActionMapping object you received (if you are using a logical name local to this mapping), or on the controller servlet itself (if you are using a logical name global to the application). + * Return an appropriate ActionForward object that identifies the presentation page to be used to generate this response, based on the newly updated beans. Typically, you will acquire a reference to such an object by calling findForward on either the ActionMapping object you received (if you are using a logical name local to this mapping), or on the controller servlet itself (if you are using a logical name global to the application). + + It is common to have several actions of either kind for one logical business object. For example, if you deal with {{{Customer}}} object, you are likely to define two setup actions: {{{viewCustomer.do}}} and {{{editCustomer.do}}} and three submit actions: {{{addCustomer.do}}}, {{{updateCustomer.do}}} and {{{deleteCustomer.do}}}. + + Setup action loads data from database and queues it into one or more arbitrary objects located in the request or session scope. Submit action processes input data and redisplays the same data entry form if errors has been found in the input. If input does not contain errors, submit action forwards to a success page. + + This standard setup/submit pattern is not perfect: + * The focus is a page, not a web resource in general. + * One Action deals with only one message, like {{{updateCustomer.do}}} deals with "Update Customer" event, {{{deleteCustomer.do}}} deals with "Delete Customer" event. + * One logical web resource is defined with several action mappings in the {{{struts-config.xml}}} file as well as with several Java classes. + * Output data is often scattered in an uncontrolled manner throughout request and session scope. + * In case of error the data entry form is redisplayed by a submit action; that opens a whole can of worms: + * If input data is invalid and autovalidation is turned on, a submit action class is never get called and cannot affect the workflow. + * One page is represented with two different URLs in the browser. + * An attempt to refresh a page after it has been redisplayed causes double submit. + * Success page often corresponds to a logically different resource, this leads to a spaghetti code both in Java code as well as in {{{struts-config.xml}}} file. + == Action As Event Dispatcher == - Behavior object handles a group of related messages (events, commands). These messages often correspond to one business object, like classic Create, Retrieve, Update and Delete messages that identify basic operations on persistent objects in a database-driven application. Behavior object usually changes application state based on incoming message. Application state can be stored in database, flat file or in a scoped object like !HttpSession or !HttpServletRequest. + Event Dispatcher handles a group of related messages (events, commands). These messages often correspond to one business object, like classic Create, Retrieve, Update and Delete messages that identify basic operations on persistent objects in a database-driven application. Event Dispatcher usually changes application state based on incoming message. Application state can be stored in a database, a flat file or in a scoped object like !HttpSession or !HttpServletRequest. - A behavior object has methods that correspond to incoming messages. Therefore an Action that manages a persistent object will likely have create(), retrieve(), update() and delete() methods. Each method is triggered with a specific parameter sent in a request. + An event dispatcher has methods that correspond to incoming messages. Therefore an Action that manages a persistent object will likely have {{{create}}}, {{{retrieve}}}, {{{update}}} and {{{delete}}} methods. Each method is triggered with a specific parameter sent in a request. inline:event_dispatcher.gif - Struts Extras package allows to define behavior objects with one of subclasses of Action class: DispatchAction, LookupDispatchAction, MappingDispatchAction, EventDispatchAction. These subclasses decode an event from incoming request and dispatch it to a corresponding event handler. The exact way of defining events depends on specific Action subclass. + Struts defines several dispatch classes like !DispatchAction, !LookupDispatchAction, !MappingDispatchAction, !EventDispatchAction. These subclasses decode an event from incoming request and dispatch it to a corresponding event handler. The exact way of defining events depends on specific Action subclass. - Starting from Struts 1.4 dispatching functionality is implemented into base Action class. Now there is a standard way of defining events for a behavioral Action by using <event> elements in an action mapping: + In an interactive application dispatch actions usually handle one I/O phase, either input events or render requests. Thus the task of handling input and output is conveniently split into separate Actions. + Automatic validation should be turned off in {{{struts-config.xml}}} file to ensure that a handler is always called for an incoming event. - {{{<action path = "/loginSubmit" - type = "samples.login.LoginAction" - name = "loginform" - scope = "session" - validate = "false"> - <event name="loginEvent" handler="login"/> - <event name="logoutEvent" handler="logout"/> - <forward name = "render" path = "/loginRender.do" /> - </action>}}} - - Behavior Actions usually handle submit requests only. To display a view they transfer control to a rendering Action. Thus the task of handling input and output is split into separate Actions. == Action As Web Resource Manager == + + Many Struts users think in terms of simple actions and pages. It may be beneficial to think it terms of more generic web resources. A ''web resource'' is a representation of a business object that an interactive application works with. For example, a Customer resource can be displayed in "View" and "Edit" modes, and can accept "New", "Edit", "Delete" and "Save" messages. Would not it be simpler to represent this entity with only one Action? + + This is possible with a dispatching Action. You can differentiate input and render phases either by request type (GET vs. POST) or by presence of an event parameter in the request. + + A Web Resource Manager does the following: + * handles different commands and events corresponding to a web resource (submit phase), and + * selects an appropriate view based on current state of web resource (render phase). inline:web_resource.gif == Action As Stateful Web Resource Manager == - In Microsoft parlance, a code-behind class implements the logic of one web resource. Since ASP.Net uses Page Controller paradigm, a web resource is usually a page. Struts web resources are not constrained by one page, one resource can have several corresponding views defined in one or in several JSP files. + A stateful web resource stores its data in a session scope, usually in a session-scoped form bean associated with Action. This allows to use redirection without losing the data. Redirection can be used to render a view: submit action redirects to setup action instead of forwarding to it. - A code-behind Struts Action: - * handles different commands and events corresponding to a web resource (submit phase), and - * selects an appropriate view based on current state of web resource (render phase). + If you can live with session-scoped data, your web application will sport much cleaner and user-friendly interface: + + * A web resource will have only one URL, the setup URL. Submit URL will be cleared by browser right after redirection. + * Navigating back and forward, as well as reloading a view is safe and does not cause resubmit. + * Several unsuccessful submit attempts to not accumulate in browser page history. inline:stateful_web_resource.gif - - {{{<action path = "/login" - type = "samples.login.LoginAction" - name = "loginform" - scope = "session" - validate = "false"> - <event name="loginEvent" handler="login"/> - <event name="logoutEvent" handler="logout"/> - <forward name = "notloggedin" path = "/logint.jsp" /> - <forward name = "loggedin" path = "/logout.jsp" /> - </action>}}} == Action As Web Component Manager ==