Dear Wiki user, You have subscribed to a wiki page or wiki category on "Struts Wiki" for change notification.
The "RolloverScope" page has been changed by Bill McCarty: http://wiki.apache.org/struts/RolloverScope?action=diff&rev1=12&rev2=13 Attention! This page describes a feature that has not been implemented yet! = Rollover Scope for Struts 1.x = - - Traditionally web applications use to store state information either in in the HTTP request or in the HTTP session. + Traditionally web applications use to store state information either in in the HTTP request or in the HTTP session. If request object is used, state is usually serialized to HTML page as part of HTML FORM. In this case moving back and forward along page history changes the state. Consider implementing an online store checkout service, using request object to store state. After a customer payed for goods, he can click Back button and pay again. To prevent this kind of error a token or similar facility should be used. @@ -17, +16 @@ {{attachment:rollover.gif}} == Rollover scope in a nutshell == - 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] * By passing rollover scope to saveXXX() and loadXXX() methods of Action class. [not tested] * By specifying rollover scope for an action form in struts-config.xml file. [implemented] A rollover scope can be configured for automatic garbage collection. Two techniques are possible: + * Specifying removal strategy (by timeout or by request count) and a limiting value (number of requests or time to live) at scope creation time. Scope will be removed when its lifetime counter exceeds limiting value. * Specifying a release property in action mapping; when action forwards to a release target, the rollover scope is destroyed. == Using rollover scope explicitly from application code == - (Not all statements of this section are backed up by actual code) To obtain an instance of a rollover scope use {{{RolloverScope.getInstance}}} static method. If the scope you are accessing does not exist and "create" flag is true, new scope will be created. When a rollover scope is accessed explicitly, its content is not copied to request scope. @@ -40, +39 @@ To remove rollover scope from the session object use {{{RolloverScope.remove}}} method. If "writeThrough" flag was set at scope creation time, rollover data is removed from request scope as well. == Using saveXXX methods of Action class == - TBD == 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 example of a typical use case implemented with two action mappings: a Log In component. + 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 example of a typical use case implemented with two action mappings: a Log In component. One mapping is used for submitting login and password from the browser, another mapping use used for rendering either "Not logged in"... {{attachment:login.gif}} - ...or "logged in" page. + ...or "logged in" page. {{attachment:logout.gif}} - A rollover scope is used to store form bean in between requests. + A rollover scope is used to store form bean in between requests. === Rollover-scoped action form, example 1 === - 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"/> + . 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 = "/loginrenderaction" - type = "org.apache.struts.samples.login.LoginRenderAction" - name = "loginform" - scope = "rollover" - validate = "false"> - <forward name = "notloggedin" path = "/logindialog/logincomponent-login.jsp"/> - <forward name = "loggedin" path = "/logindialog/logincomponent-logout.jsp"/> + . type = "org.apache.struts.samples.login.LoginRenderAction" name = "loginform" scope = "rollover" 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" /> + . 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"/> - <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" /> + . 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"/> - <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" /> + . 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"/> <forward name = "userhome" path = "/userhome.do" redirect = "true"/> <forward name = "cancel" path = "/main.do" redirect = "true"/> - <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="5" /> - <set-property key="rolloverRelease" value="loggedin" /> + . type = "org.apache.struts.samples.login.LoginRenderAction" name = "loginform" scope = "rollover" validate = "false"> + <set-property key="rolloverStrategy" value="rcount" /> <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"/> - <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"> + . 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" /> + <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"/> - <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"> + . type = "org.apache.struts.samples.login.LoginRenderAction" name = "loginform" scope = "rollover" validate = "false"> - <set-property key="rolloverId" value="MyLoginRollover" /> + <set-property key="rolloverId" value="MyLoginRollover" /> + <forward name = "notloggedin" path = "/logindialog/logincomponent-login.jsp"/> <forward name = "loggedin" path = "/logindialog/logincomponent-logout.jsp"/> - <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. + 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> + . type = "org.apache.struts.samples.OrderInputAction" name = "orderform" scope = "rollover" validate = "false" parameter = "editEvent=edit,viewEvent=view> - <set-property key="rolloverId" value="orderId" /> + <set-property key="rolloverId" value="orderId" /> + <forward name = "render" path = "/orderrenderaction.do" redirect = "true"/> <forward name = "cancel" path = "/orderlist.do" redirect = "true"/> - <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"> + . type = "org.apache.struts.samples.OrderRenderAction" name = "orderform" scope = "rollover" validate = "false"> - <set-property key="rolloverId" value="orderId" /> + <set-property key="rolloverId" value="orderId" /> + <forward name = "edit" path = "/order/editorder.jsp"/> - <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: + 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. @@ -225, +164 @@ Several core classes have been updated to accommodate usage of rollover scope. Two new commands have been added to default chain. === New: org.apache.struts.scope.RolloverScope === - Instances of this class store rollover-scoped data; the class implements Map. Static methods of this class obtain/create /remove a rollover scope instance. + Instances of this class store rollover-scoped data; the class implements Map. Static methods of this class obtain/create /remove a rollover scope instance. === New: org.apache.struts.chain.commands.LoadRolloverData === This command must be specified in chain config file before !CreateActionForm command; it matures all rollover scopes corresponding to current session, removes aged scopes, looks up for a rollover scope corresponding to an action mapping and loads rollover data into request scope to ensure that JSP tags perform properly. @@ -237, +176 @@ Added {{{getRolloverScope}}} method along with "rollover" literal to be used in action mapping definition. === Updated: org.apache.struts.chain.contexts.ActionContextBase === - Method {{{getScope}}} now looks up for rollover scope along with standard J2EE scopes. + Method {{{getScope}}} now looks up for rollover scope along with standard J2EE scopes. === Updated: org.apache.struts.chain.contexts.WebActionContext === Defines getRolloverScope method, which obtains/creates rollover scope based on current webcontext and request scope. This method is used by {{{ActionContextBase.getScope}}} method.