Designing web applications for multiple browser pages

In conventional web browsers without tab support, each browser instance has a single page. Each browser page is associated with its own session object while connected to a web application running on a web server. In this case, the conversational state between client and server (contextual data) stored in the session belongs to a single page. In addition to session attributes and resources, this also includes any stateful session beans.
In web browsers with multiple tabs and windows, all pages that connect to the same web application share the same session object and beans. This requires the server to keep track of every page and store the contextual data for each page separately. In general, when handling a request for multiple page support, a web application must use page identifiers to access contextual data from the session or any other available storage area. Managed beans also require special attention; stateful session beans must use the page identifier to access the current state for each page.
However, web applications designed to work with a single page per session do not automatically support multiple page identifiers. When you enable the page identifier feature in JViews, you can design and run web applications that handle browser instances with multiple tabs or pages.

Enabling the JViews page identifier

The JViews page identifier feature is disabled by default. You enable JViews page identifiers by setting the context parameter ilog.views.faces.PAGEID_SUPPORT to true in the web.xml file:
<context-param>
   <param-name>ilog.views.faces.PAGEID_SUPPORT</param-name>
   <param-value>true</param-value>
</context-param>
Any value other than true disables this feature.

Multiple-page support on the server side

Once enabled, the JViews page identifier feature automatically detects and tracks all pages accessing the web application by checking the request for the IlvServletPageIdUtil.PAGE_ID parameter (jviewsPageId). If no such request is found, JViews considers the request to be coming from a new page and creates a new identifier for it.
The web application must then check for the page identifier to create and store any contextual data (data files and data sources, selection state, the IlvManagerView instance, and so on).
The following public API methods are available to retrieve the page identifier in a JSF application:
  • IlvFacesPageIdUtil.getPageId() returns the page identifier associated with the current request. A new identifier is created if none exists. This method must be invoked whenever the page identifier is required.
  • IlvFacesPageIdUtil.setSessionAttributeWithPageId(String,Object) stores an attribute for the session by appending the current page identifier to the given user key. This method must be invoked when storing page-specific data (contextual data) in the session.
  • IlvFacesPageIdUtil.getSessionAttributeWithPageId(String) retrieves an attribute from the session based on the user key and the current page identifier. This method must be invoked when retrieving page-specific data (contextual data) from the session.
The following public API methods are available to retrieve the page identifier in a non-JSF page:
  • IlvServletPageIdUtil.getPageId(HttpServletRequest) returns the page identifier from the given HTTP request. A new identifier is created if none exists. This method must be invoked whenever the page identifier is required.
  • IlvServletPageIdUtil.setSessionAttributeWithPageId(HttpServletRequest,String,Object) stores an attribute for the session associated with the given HTTP request, appending the current page identifier to the given user key. This method must be invoked when storing page-specific data (contextual data) in the session.
  • IlvServletPageIdUtil.getSessionAttributeWithPageId(HttpServletRequest,String) retrieves an attribute from the session associated with the given HTTP request, based on the user key and the current page identifier. This method must be invoked when retrieving page-specific data (contextual data) from the session.
These API methods enable access to the JViews page identifier feature and provide a way to store and retrieve session attributes that are uniquely assigned to a browser page.

Multiple-page support on the client side

The page identifier is stored on the client side and submitted to the server as a parameter (jviewsPageId) of every HTTP request.
The JViews JavaScript library provides the following API method to access the page identifier on the client side:
  • IlvPageId.instance.getPageId() returns the page identifier. This method must be invoked whenever the page identifier is required on the client side.
Normally this method is not needed, but if the web application implements JavaScript routines that submit HTTP requests to the server, you must ensure that these requests include a jviewsPageId parameter with the page identifier. Requests from additional form tags must also include this parameter when submitted.
Declaring a JViews Maps for Defense component using the tag library
Consider the following MapView component declaration:
<h:form id="form">
  <jvmf:mapView id="mapId" 
     style="width:500px;height:300px"
     data="/data/map.ivl" />
</h:form>
In the above example, the MapView component is not associated with any managed bean. In this scenario, JViews automatically stores the page contextual data (IlvManagerView and the map.ivl data file). When the JViews Maps for Defense JSF component is declared in this way, it will support multiple browser pages automatically.
Associating a managed bean to the MapView data attribute
The following example associates the data tag attribute of the MapView with the dataSource property of the dataBean bean:
<h:form id="form">
  <jvmf:mapView id="mapId" 
     style="width:500px;height:300px"
     data="#{dataBean.project}" />
</h:form>
In this case, JViews automatically stores the corresponding IlvManagerView instance according to the page identifier, but you must ensure that the managed bean (dataBean) does the same for the project property. You can do this by converting it to a request bean that creates or retrieves the project from the session, as follows:
  // Holds the map project in the request bean
  private String mapProject = null;

  // The constructor tries to fetch the map project from the session, 
  // creating a new one if needed
  public DataBean() {
    // Try to retrieve the map project from the session
    mapProject = (String)IlvFacesPageIdUtil.getSessionAttributeWithPageId("mapProjectId");
    if (null == mapProject) {
      // Map project not found in session, create a new one and then store it
      mapProject = createMapProject();
      IlvFacesPageIdUtil.setSessionAttributeWithPageId("mapProjectId", mapProject);
    }
  }

  // The getter just returns the map project reference
  public String getProject() {
    return mapProject;
  }
The code snippet shows a possible request bean implementation where the getter method (getProject) uses the IlvFacesPageIdUtil API to persist the map project object in the session object. IlvFacesPageIdUtil relies on the page identifier to determine the page from which the request is submitted. Although this example describes the data tag attribute, the solution can be used for other tag attributes as well.
Binding a managed bean to the GoogleView tag
The following example illustrates how to bind the GoogleView to a managed bean. In this scenario you must ensure that the GoogleView instance is properly stored per browser page:
<h:form id="form">
  <jvmf:googleView binding="#{dataBean.view}" 
     id="gMapId" 
     key="#{keyBean.googleKey}"
     level="7"
     style="width: 750px; height: 350px"
 </h:form>
As described in the previous example, the dataBean bean is converted to a request bean that is capable of retrieving the GoogleView instance from the session, using the page identifier:
  // Holds the Google View JSF component in the request bean
  private IlvFacesGoogleViewComponent googleView = null;

  // The constructor tries to fetch the Google View JSF component 
  // from the session, creating a new one if needed
  public DataBean() {
    // Try to retrieve the Google View JSF component from the session
    googleView = (IlvFacesGoogleViewComponent)IlvFacesPageIdUtil.getSessionAttributeWithPageId("viewId");
    if (null == googleView) {
      // Google View JSF component not found in session, 
      // create a new one and then store it
      googleView = new IlvFacesGoogleViewComponent();
      IlvFacesPageIdUtil.setSessionAttributeWithPageId("viewId", googleView);
    }
  }

  // The getter just returns the component reference
  public IlvFacesGoogleViewComponent getView() {
    return googleView;
  }

  // The setter can be ignored because the bean relies on the session
  // to retrieve the component
  public void setView(IlvFacesGoogleViewComponent googleView) {
    // does nothing
  }