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/RolloverScope The comment on the change is: Added use cases and code samples ------------------------------------------------------------------------------ Because of issues related to using session, many developers abstain from using it, and prefer fighting with double submit problems and POSTDATA messages instead. They rarely split input and render tasks into two actions; when they do, they use in-server forwarding instead of redirection, because request object does not survive between requests. This is unfortunate because Redirect-After-Post pattern is a simple and proven solution for creating user-friendly and error-resistant interfaces. - Starting from Struts 1.4 it will be possible to store data related to a multi-request conversation in the Rollover Scope. + Starting from Struts 1.4 it will be possible to store a multi-request conversation data in the Rollover Scope. inline:rollover.gif == Rollover scope in a nutshell == - Physically, a rollover scope is a map stored within session scope. One session object can store several associated rollover scopes. + A rollover scope is essentially a map stored within session scope. One session object can store several associated rollover scopes. Rollover scope can be used in the following ways: * Directly from application code by calling methods of RolloverScope class. [not tested] @@ -45, +45 @@ == Using rollover scope to store an action form == - In a Struts application a rollover scope can be used in action mapping definition just as request and session scopes. To declare a rollover scope for an action form specify {{{scope="rollover"}}} in action mapping definition. Below is an example of a standard redirect-after-post pattern: one mapping for submitting user data from browser, another mapping for rendering a web page. A rollover scope is used to store form bean in between requests. The input action inherits from !EventDispatchAction and is used as event processor. Render action uses login/logout state to render an appropriate view. + In a Struts application a rollover scope can be used in action mapping definition just as request and session scopes. To declare a rollover scope for an action form specify {{{scope="rollover"}}} in action mapping definition. Below are examples of a standard redirect-after-post pattern: one mapping for submitting user data from browser, another mapping for rendering a web page. A rollover scope is used to store form bean in between requests. - Events are defined in 'parameter' attribute (see !EventDispatchAction for details). Notice that 'scope' has 'rollover' value. A removal strategy of with lifetime of one request is defined for rollover scope - perfect for most redirect-after-post use cases. + === Rollover-scoped action form, example 1 === - Besides automatic removal based on removal strategy, a rollover scope can be removed based on action outcome. In the sample below, if an input action chooses 'cancel' or 'userhome' outcomes, then rollover scope is removed when action finishes. + This is the simplest way of configuring the rollover scope: just declaring the scope as "rollover". The input action inherits from !EventDispatchAction and is used as event processor. Events are defined in 'parameter' attribute (see !EventDispatchAction for details). Notice that 'scope' has 'rollover' value. A removal strategy of with lifetime of one request is defined for rollover scope - perfect for most redirect-after-post use cases. Render action uses login/logout state to render an appropriate view. + By default, removal strategy is by request count, and maximum lifetime is one request. This means, that when you navigate from this action, the "loginform" action form will mature, and on a next request it will be removed from session. + + {{{<action path = "/logininputaction" + type = "org.apache.struts.samples.login.LoginInputAction" + name = "loginform" + scope = "rollover" + validate = "false" + parameter = "initEvent=init,loginEvent=login,cancelEvent=cancel,logoutEvent=logout"> + + <forward name = "render" path = "/loginrenderaction.do" redirect = "true"/> + <forward name = "userhome" path = "/userhome.do" redirect = "true"/> + <forward name = "cancel" path = "/main.do" redirect = "true"/> + </action> + - {{{<action path = "/logininputaction" + <action path = "/loginrenderaction" - type = "org.apache.struts.samples.login.LoginInputAction" + type = "org.apache.struts.samples.login.LoginRenderAction" name = "loginform" scope = "rollover" - validate = "false" + validate = "false"> + + <forward name = "notloggedin" path = "/logindialog/logincomponent-login.jsp"/> + <forward name = "loggedin" path = "/logindialog/logincomponent-logout.jsp"/> + </action>}}} + + === Rollover-scoped action form, example 2 === + + This is more complex example which defines the removal strategy and limit explicitly using action mapping properties. Properly "rolloverStrategy" specifies strategy by timout, and property "rolloverLimit" specifies maximum lifetime of the idle rollover scope - 3 minutes. + + {{{<action path = "/logininputaction" + type = "org.apache.struts.samples.login.LoginInputAction" + name = "loginform" + scope = "rollover" + validate = "false" parameter = "initEvent=init,loginEvent=login,cancelEvent=cancel,logoutEvent=logout"> + + <set-property key="rolloverStrategy" value="timeout" /> + <set-property key="rolloverLimit" value="3" /> + + <forward name = "render" path = "/loginrenderaction.do" redirect = "true"/> + <forward name = "userhome" path = "/userhome.do" redirect = "true"/> + <forward name = "cancel" path = "/main.do" redirect = "true"/> + </action> + + <action path = "/loginrenderaction" + type = "org.apache.struts.samples.login.LoginRenderAction" + name = "loginform" + scope = "rollover" + validate = "false"> <set-property key="rolloverStrategy" value="rcount" /> <set-property key="rolloverLimit" value="1" /> + + <forward name = "notloggedin" path = "/logindialog/logincomponent-login.jsp"/> + <forward name = "loggedin" path = "/logindialog/logincomponent-logout.jsp"/> + </action>}}} + + === Rollover-scoped action form, example 3 === + + In addition to explicit removal strategy (by request, maximum lifetime is 5 requests to a session), this configuration specifies conditions for immediate removal of the rollover scope. They are defined with "rolloverRelease" property, which contains action outcomes for which the rollover scope should be deallocated. In the sample below, if the input action chooses 'cancel' or 'userhome' outcomes, then rollover scope is removed when action finishes. If the render action displays user info page a.k.a. logout page, then rollover scope is not needed either. + + {{{<action path = "/logininputaction" + type = "org.apache.struts.samples.login.LoginInputAction" + name = "loginform" + scope = "rollover" + validate = "false" + parameter = "initEvent=init,loginEvent=login,cancelEvent=cancel,logoutEvent=logout"> + + <set-property key="rolloverStrategy" value="rcount" /> + <set-property key="rolloverLimit" value="5" /> <set-property key="rolloverRelease" value="cancel,userhome" /> <forward name = "render" path = "/loginrenderaction.do" redirect = "true"/> @@ -74, +134 @@ validate = "false"> <set-property key="rolloverStrategy" value="rcount" /> - <set-property key="rolloverLimit" value="1" /> + <set-property key="rolloverLimit" value="5" /> <set-property key="rolloverRelease" value="loggedin" /> <forward name = "notloggedin" path = "/logindialog/logincomponent-login.jsp"/> <forward name = "loggedin" path = "/logindialog/logincomponent-logout.jsp"/> </action>}}} + + === Rollover-scoped action form, example 4 === + + In previous samples the rollover scope was not given name explicitly, so action form name was used as rollover scope name: "loginform". It is possible to give an explicit name to the rollover scope, using "rolloverId" property, for example: + + {{{<action path = "/logininputaction" + type = "org.apache.struts.samples.login.LoginInputAction" + name = "loginform" + scope = "rollover" + validate = "false" + parameter = "initEvent=init,loginEvent=login,cancelEvent=cancel,logoutEvent=logout"> + + <set-property key="rolloverId" value="MyLoginRollover" /> + + <forward name = "render" path = "/loginrenderaction.do" redirect = "true"/> + <forward name = "userhome" path = "/userhome.do" redirect = "true"/> + <forward name = "cancel" path = "/main.do" redirect = "true"/> + </action> + + <action path = "/loginrenderaction" + type = "org.apache.struts.samples.login.LoginRenderAction" + name = "loginform" + scope = "rollover" + validate = "false"> + + <set-property key="rolloverId" value="MyLoginRollover" /> + + <forward name = "notloggedin" path = "/logindialog/logincomponent-login.jsp"/> + <forward name = "loggedin" path = "/logindialog/logincomponent-logout.jsp"/> + </action>}}} + + === Rollover-scoped action form, example 5 === + + It is possible to have several instances of rollover scope per action. Consider a typical CRUD application, it displays a list of customer orders. Every order line contains !View and !Edit links. If you click on !Edit link, then an order edit form is displayed. To select a specific order, an order id is passed to the server as HTTP parameter. Say, you want to open several order forms in different windows. You would like to right-click the !Edit link and select "Open in separate browser window". This would be hard to do with saving state in the session, but is easy with rollover scope. + + All you need to do is to define your !Edit link so that it contained parameter with the same name as value of "rolloverId" parameter. The value of this HTTP parameter will be concatenated with base rollover id to form a complete name. This is easier to show with code sample. + + {{{<action path = "/orderinputaction" + type = "org.apache.struts.samples.OrderInputAction" + name = "orderform" + scope = "rollover" + validate = "false" + parameter = "editEvent=edit,viewEvent=view> + + <set-property key="rolloverId" value="orderId" /> + + <forward name = "render" path = "/orderrenderaction.do" redirect = "true"/> + <forward name = "cancel" path = "/orderlist.do" redirect = "true"/> + </action> + + {{{<action path = "/orderrenderaction" + type = "org.apache.struts.samples.OrderRenderAction" + name = "orderform" + scope = "rollover" + validate = "false"> + + <set-property key="rolloverId" value="orderId" /> + + <forward name = "edit" path = "/order/editorder.jsp"/> + </action>}}} + + The mapping above is similar to mapping in Example 4. Now the crucial part: the Edit links. They may look like this: + + {{{ + orderrenderaction?itemId=1701 + }}} + + When this request is submitted, rollover scope manager will look up "rolloverId" property in the action mapping, it has value "orderId". Then it will look up "orderId" request parameter and read its value, it is "1701". Then it will concatenate parameter name and value, producing "orderId1701", this will be the name of rollover scope for order 1701. + + As you see, for this particular use case the natural ID makes a perfect rollover scope name. For other use cases you may need to create artificial IDs. == Changes to Struts core classes == Several core classes have been updated to accommodate usage of rollover scope. Two new commands have been added to default chain.