Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Renamed to Session Storage. added SessionAttribute section at bottom (somebody had removed it from the Persistent Page Data page). Tweaked the new Pitfalls section.
Wiki Markup
{float:right|background=#eee}
{contentbylabel:title=Related Articles|showLabels=false|showSpace=false|space=@self|labels=persistence}
{float}

Session Storage

...

Often, you will have a situation where you have a bit of data that is needed across multiple pages. Perhaps you are creating a multi-page wizard, or perhaps you have an object that tracks the user's identify once logged in, or maybe a shopping cart.

Ordinary persistent page data is not appropriate Persistent Page Data won't work for this, since persistent fields apply are available only to a specific page and aren't , not shared across multiple pages.

Tapestry provides two mechanisms for storing such data: Session State Objects and Session Attributes. When deciding between the two, it's best to use Session State Objects for complex objects, and Session Attributes for simple types.

Session State Objects

With Instead, you want to use a Session State Object (an SSO).With an SSO, the value is automatically stored outside the page; with the default storage strategy, it is stored in the session. Such a value is global to all pages for the same user, but is stored seperately separately for different users.

A field holding an SSO is marked with the @SessionState annotation.

Example:

Code Block
java
java
public class MyPage
{
  @SessionState
  private MyStateShoppingCart myStateshoppingCart;
  
  . . .
}

Any other component or page that declares a field of the same type, regardless of name, and marks it with the SessionState annotation will share the same value. It's that simple. However, using @SessionState safely requires care:

Warning

DO NOT USE THIS @SessionState FOR SIMPLE TYPES! Only use @SessionState it on variables that are of a custom-built class designed expressly for this purpose! Read more below. See the Pitfalls section below.

The first time you access an SSO, it is created automatically. Typically, the SSO will have a public no-args constructor ... but you may inject dependencies into the SSO via its constructor, as you can with a Tapestry IoC service implementation.

For Tapestry 4 Users: a big change here is that you don't need to provide any configuration for the SSO before using it, nor do you provide a logical name. Tapestry 5 uses the class name to identify the SSO, so there's no need for a logical name.

Assigning a value to an SSO field will store that value. Assigning null to an SSO field will remove the SSO (reading the field subsequently will force a new SSO instance to be created).

Pitfalls

With @SessionState, you are creating a session-wide data storage area that is tied to the type (class) of the variable you annotate. It is not specifically tied to the variable name you useditself, or even to the class in which that variable was definedannotated. As with all session data, there is the serious possibility of collisions, not just within your application but with other modules/libraries.:

Code Block
titleExample of Data Collision -- Don't do thisDo This!
  @SessionState
  private String myStateuserName;         // Unsafe -- String is not a custom type

  ... then, later in this class or any other:

  @sessionState
  private String myOtherStateuserCity;     // This overwrites thevalue myStatein valueuserName, because it's also a String!

For these reasonsThe simple rule is, NEVER USE use @SessionState FOR SIMPLE-TYPE VARIABLESfor simple-type variables. It is ALWAYS worth taking the time to build a special class to hold your session state information, because it . Doing so will force you to consolidate that information into a single, logical unit that can't be accidentally accessed by other classes.

For Tapestry 4 Users: a big change here is that you don't need to provide any configuration for the SSO before using it, nor do you provide a logical name. Tapestry 5 uses the class name to identify the SSO, so there's no need for a logical name.

The first time you access an SSO, it is created automatically. Typically, the SSO will have a public no-args constructor ... but you may inject dependencies into the SSO via its constructor, as you can with a Tapestry IoC service implementation.

Assigning a value to an SSO field will store that value. Assigning null to an SSO field will remove the SSO (reading the field subsequently will force a new SSO instance to be created).(Alternatively, see the Session Attribute section below.)

Check for Creation

Scalable web applications do not create the server-side session needlessly. If you can avoid creating the session, especially on first access to your web application, you will be able to handle an order of magnitude more users. So, if you can avoid creating the SSO, you should do so.

...

In this case, the myState field will be null if the MyState SSO does not exist, but will be non-null if it has been created (either by assigning a value to the field, or by a different SSO field where create is true).

Persistence Strategies

Each SSO is managed according to a persistence strategy. The default persistence strategy, "session", stores the SSOs inside the session. The session is created as needed.

Clustering Issues

The clustering strategy for Application State Objects in release 5.0 now applies to all session-persisted objects. See the persistent page data notes Persistent Page Data for more details.

Configuring SSOs

Generally, you will configure an SSO need to configure your Session State Object if you need want to change it from the default persistence strategy to other than the default. (Right now there's only one built in strategy, but more will be coming in the future.)

Alternately, you will configure an SSO so that you can can configure a Session State Object in order to control how it is instantiated. You may need to inject some values into the SSO when it is first created, or otherwise initialize it. In this second case, you may provide an ApplicationStateCreator object, which will be called upon to create the SSO as necessary. This is also the technique to use when you want your SSO to be represented by an interface rather than a class: you need to provide a creator that knows about the class that implements the interface.

Contributions A Session State Object is configured using contributions to the ApplicationStateManager service are used to configure an SSO. From your application's module:

...

Note: You might be confused by the name "_Application_StateManager" and "_Application_StateCreator"; these reflect a difference in naming between 5.0 and 5.1; SSOs were originally called "Application State Objects", but that naming implied they were stored in the ServletContext, as application global to all users. The new SessionState annotation was introduced, but the existing services need to keep thier names as-is.

Session Attributes

As an alternative to SSOs, Tapestry provides a Session Attribute mechanism, which lets you store data in the session by name (rather than type). It is particularly useful when integrating Tapestry with legacy applications that directly manipulate the HttpSession.

Code Block
titleThe Old Way

public class Page {
    @Inject
    private Request request;
    
    public User getUser() {
        return (User) request.getSession(true).getAttribute("loggedInUserName");
    }
}

Since 5.2 this can be accomplished just by annotating a page or component property with @SessionAttribute. This annotation is used to map a property of a page or component to value stored in session. Unlike Session State Objects, the name (not the type) of the annotated property is used as the name of the session attribute to look for. You can also provide a name using the annotation's value parameter.

Code Block
titleThe New Way

public class Page {
    @SessionAttribute
    private User loggedInUserName;
}

Pitfalls

As with SSOs, when using Session Attributes you are creating a session-wide data storage area that has the serious possibility of data collisions, not just within your application but with other modules/libraries. To avoid problems, you should qualify the session attribute name with a package-like naming convention. For example, use something like "com.mycompany.username" instead of just "username".

It's best to define the session attribute name as constant, and use that in the annotation's value parameter, rather then defaulting to the instance variable name. This will help prevent subtle runtime errors due to misspellings.