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.

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.

If session object is used to store state, it may open a potential source of memory leak, because application has to explicitly remove user objects from session. Also, opening several windows for same-type object like a product in a traditional CRUD application may not be possible, because session stores state corresponding only to one product.

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 a multi-request conversation data in the Rollover Scope.

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:

A rollover scope can be configured for automatic garbage collection. Two techniques are possible:

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.

To store data in a scope or to read data from a scope use appropriate methods of Map interface. Rollover scope is just an enhanced Map. If "writeThrough" flag was set at scope creation time, then all data written to the rollover scope is duplicated in the request object.

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.

One mapping is used for submitting login and password from the browser, another mapping use used for rendering either "Not logged in"...

login.gif

...or "logged in" page.

logout.gif

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"

</action>

<action path = "/loginrenderaction"

</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"

</action>

<action path = "/loginrenderaction"

</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"

</action>

<action path = "/loginrenderaction"

</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"

</action>

<action path = "/loginrenderaction"

</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"

</action>

{{{<action path = "/orderrenderaction"

</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.

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.

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.

New: org.apache.struts.chain.commands.ReleaseRolloverData

This command must be specified in chain config after ExecuteCommand and ExecuteAction commands; it immediately releases current rollover scope if current action is configured so with "rolloverRelease" property.

Updated: org.apache.struts.chain.contexts.ActionContext

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.

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.

Updated: struts-config_1_4.dtd

RequestScope enity now allows "rollover" value along with "request" and "session".

Source code and samples

The changes related to rollover scope have not been committed to main Struts 1.x codebase yet. You can try a simple example of rollover scope usage by downloading this sample application: rollover.war . Source code and build script are included.

RolloverScope (last edited 2012-03-08 17:51:49 by Bill McCarty)