55 Using State Management in a Fusion Web Application

This chapter describes the Fusion web application state management facilities for ADF application modules to support stateful applications on the web.

This chapter includes the following sections:

55.1 About Fusion Web Application State Management

Most real-world business applications need to support multi-step user tasks. Transactional web sites tend to use a step-by-step style user interface to guide the end user through a logical sequence of pages to complete these tasks. When the task is done, the user can save or cancel everything as a unit. However, the HTTP protocol is built on the concept of individual, stateless requests, so the responsibility of grouping a sequence of user actions into a logical, stateful unit of work falls to the application developer.

The limitations of the HTTP protocol have led developers to invent solutions involving a "shadow" set of database tables with no constraints and with all of the column types defined as character-based. Using such a solution becomes very complex very quickly. Ultimately, some kind of generic application state management facility is needed to address these issues in a more generic and workable way. The solution comes in the form of ADF Business Components, which implements this for you out of the box.

State management enables you to easily create web applications that support multi-step use cases without falling prey to the memory, reliability, or implementation complexity problems. Oracle ADF simplifies the process of implementing and maintaining session state through the use of application module pools, which are configurable resource managers that release application module caches safely. By default, the ADF Business Components project enables one application module pool for each application module definition in your application.

In Fusion web applications, state management is provided at these levels:

  • Save For Later feature in the ADF Controller layer implemented in task flows, as described in Using Save Points in Task Flows.

    Save For Later is activated at the ADF Controller layer and automatically saves a "snapshot" of the current UI and controller states, and delegates to the ADF Model layer to save its state as well. Save for Later saves an incomplete transaction without enforcing validation rules or submitting the data. You can use it to save data and state information about a region, view port, or portlet. Later, you can restore application state and data associated with a save point that supports both explicit saves (initiated by the user) or implicit saves (without user action). The end user can resume working on the same transaction with the same data that was originally saved when the application was exited.

  • Application state management facility in the ADF Model layer implemented in application modules.

    Through the application state management facility, the ADF runtime automatically manages the application state of each user session and supports highly scalable applications. An application module supports passivating (storing) its pending transaction state to an XML document, which is stored either in-memory or in the database in a single, generic table, keyed by a unique passivation snapshot ID. It also supports the reverse operation of activating pending transaction state from one of these saved XML snapshots. This passivation and activation is performed automatically by the application module pool when needed, and is controlled by configuration properties that you can specify. Understanding what happens behind the scenes is essential to make the most efficient use of this important feature, as explained in Managing When Passivation and Activation Occurs.

  • Application state management support for a server farm, or clustered server environment with failover.

    If you deploy in a multiple application server environment with failover enabled, then subsequent end-user requests may be handled by any server in your server farm or cluster. To support this scenario, the application state management facility passivates the application module at the end of every request. In the event failover occurs, where the user session is redirected to another server node, the session cookie can reactivate the pending application state from the database-backed XML snapshot, regardless of which server handles the request. Thus application module passivation and activation provides built-in support for cluster failover without additional configuration. For more information about ADF support for failover, see Configuring High Availability for Fusion Web Applications.

The ADF Business Components application module lets you implement completely stateless applications or support a unit of work that spans multiple browser pages. Figure 55-1 illustrates the basic architecture of the state management facility to support these multi-step scenarios.

Figure 55-1 ADF Provides Generic, Database-Backed State Management

This image is described in the surrounding text

The ADF binding context is the one object that lives in the HttpSession for each end user. It holds references to lightweight application module data control objects that manage acquiring an application module instance from the pool during the request (when the data control is accessed) and releasing it to the pool at the end of each request. The data control holds a reference to the application module handle that identifies the user session. In particular, serialized session objects of the application module state created or modified in the pending transaction are not saved in the HttpSession using this approach. This minimizes both the session memory required per user and eliminates the network traffic related to session replication if the servers are configured in a cluster.

55.1.1 What You May Need to Know About Multi-Step Tasks

In a typical search-then-edit scenario, the end user searches to find an appropriate row to update, then may open several different pages of related master-detail information to make edits before deciding to save or cancel work. Consider another scenario where the end user wants to book a vacation online. The process may involve the end user's entering details about:

  • One or more flight segments that comprise the journey

  • One or more passengers taking the trip

  • Seat selections and meal preferences

  • One or more hotel rooms in different cities

  • Car they will rent

Along the way, the user might decide to complete the transaction, save the reservation for finishing later, or abandon the transaction before saving.

It's clear these scenarios involve a logical unit of work that spans multiple web pages. You've seen in previous chapters how to use JDeveloper's JSF page navigation diagram to design the page flow for these use cases, but that is only part of the puzzle. The pending changes the end user makes to business domain objects along the way — Trip, Flight, Passenger, Seat, HotelRoom, Auto, etc. — represent the in-progress state of the application for each end user. Along with this, other types of "bookkeeping" information about selections made in previous steps comprise the complete picture of the application state.

55.1.2 What You May Need to Know About the Stateless HTTP Protocol

While it may be easy to imagine the multi-step application scenarios, implementing them in web applications is complicated by the stateless nature of HTTP, the hypertext transfer protocol. Figure 55-2 illustrates how an end user's visit to a site comprises a series of HTTP request/response pairs. However, HTTP affords a web server no way to distinguish one user's request from another user's, or to differentiate between a single user's first request and any subsequent requests that user makes while interacting with the site. The server gets each request from any user always as if it were the first (and only) one they make.

Figure 55-2 Web Applications Use the Stateless HTTP Protocol

This image is described in the surrounding text

55.1.3 What You May Need to Know About Session Cookies

As shown in Figure 55-3, the technique used to recognize an ongoing sequence of requests from the same end user over the stateless HTTP protocol involves a unique identifier called a cookie. A cookie is a name/value pair that is sent in the header information of each HTTP request the user makes to a site. On the initial request made by a user, the cookie is not part of the request. The server uses the absence of the cookie to detect the start of a user's session of interactions with the site, and it returns a unique identifier to the browser that represents this session for this user. In practice, the cookie value is a long string of letters and numbers, but for the simplicity of the illustration, assume that the unique identifier is a letter like "A" or "Z" that corresponds to different users using the site.

Web browsers support a standard way of recognizing the cookie returned by the server that allows the browser to identify the following:

  • the site that sent the cookie

  • how long it should remember the cookie value

On each subsequent request made by that user, until the cookie expires, the browser sends the cookie along in the header of the request. The server uses the value of the cookie to distinguish between requests made by different users.

Cookies can be set to live beyond a single browser session so that they might expire in a week, a month, or a year from when they were first created, while a session cookie expires when the user closes the browser.

Figure 55-3 Tracking State Using a Session Cookies and Server-Side Session

This image is described in the surrounding text

Java EE-compliant web servers provide a standard server-side facility called the HttpSession that allows a web application to store Java objects related to a particular user's session as named attribute/value pairs. An object placed in this session Map on one request can be retrieved by the application while handling a subsequent request during the same session.

The session remains active while the user continues to send new requests within the timeframe specified by the <session-timeout> element in the web.xml file. The default session length is typically around 30 minutes, depending on the container.

55.1.4 What You May Need to Know About Using HttpSession

The HttpSession facility is an ingredient in most application state management strategies, but it can present performance and reliability problems if not used judiciously. First, because the session-scope Java objects the application creates are held in the memory of the Java EE web server, the objects in the HTTP session are lost if the server should fail.

As shown in Figure 55-4, one way to improve the reliability is to configure multiple Java EE servers in a cluster. By doing this, the Java EE application server replicates the objects in the HTTP session for each user across multiple servers in the cluster so that if one server goes down, the objects exist in the memory of the other servers in the cluster that can continue to handle the users requests. Since the cluster comprises separate servers, replicating the HTTP session contents among them involves broadcasting the changes made to HTTP session objects over the network.

Figure 55-4 Session Replication in a Server Cluster

This image is described in the surrounding text

You can begin to see some of the performance implications of overusing the HTTP session:

  • The more active users, the more HTTP sessions will be created on the server.

  • The more objects stored in each HTTP session, the more memory you will need. Note that the memory is not reclaimed when the user becomes inactive; this only happens with a session timeout or an explicit session invalidation. Session invalidations don't always happen because users don't always logout.

  • In a cluster, the more objects in each HTTP session that change, the more network traffic will be generated to replicate the changed objects to other servers in the cluster.

At the outset, it would seem that keeping the number of objects stored in the session to a minimum addresses the problem. However, this implies leveraging an alternative mechanism for temporary storage for each user's pending application state. The solution comes in the form of ADF Business Components, which implements this for you out of the box.

55.2 Managing When Passivation and Activation Occurs

The passivation/activation cycle supports application module pooling and was designed originally to maintain a reasonable number of caches in memory, as well as to support failover in high-availability, clustered server environments. This process however requires the application module state to be written to and read from the database, and is itself an expensive operation. In legacy system, comprised of 32-bit platforms, where generally smaller amounts of memory were available than on today’s middle-tier servers, the passivation of application module instances helped compensate for memory limitations.

In today’s systems, where RAM generally is not a limitation, passivation/activation provides little benefit by reducing middle-tier server memory usage. Today the best use of the passivation/activation cycle built into application module pooling is to support failure conditions in a clustered server environment.

The following scenario depicts when the automatic passivation and activation of application module state occurs. This background may help you to understand later how application module pooling can be tuned to limit the use of passivation/activation.

  1. At the beginning of an HTTP request, the application module data control handles the beginrequest event by checking out an application module instance from the pool.

    The application module pool returns an unreferenced instance. An unreferenced application module is one that is not currently managing the pending state for any other user session.

  2. At the end of the request, the application module data control handles the endrequest event by checking the application module instance back into the pool in "managed state" mode.

    That application module instance is now referenced by the data control that just used it. And the application module instance is an object that still contains pending transaction state made by the data control (that is, entity object and view object caches; updates made but not committed; and cursor states), stored in memory. As you'll see below, the instance is not dedicated to this data control, just referenced by it.

  3. On a subsequent request, the same data control — identified by its application module handle — checks out an application module instance again.

    Due to the "stateless with user affinity" algorithm the pool uses, the pool returns the exact same application module instance, with the state still there in memory.

The default configuration of application module pooling dictates, at peak usage times, with a high number of users simultaneously accessing the site, application module instances must be sequentially reused by different user sessions. In this case, the application pool will recycle a currently referenced application module instance for use by another session, as follows:

  1. The application module data control for User A's session checks an application module instance into the application pool at the end of a request. Assume this instance is named AM1.

  2. The application module data control for User Z's new session requests an application module instance from the pool for the first time, but there are no unreferenced instances available. The application module pool then:

    • Passivates the state of instance AM1 to the database.

    • Resets the state of AM1 in preparation to be used by another session.

    • Returns the AM1 instance to User Z's data control.

  3. On a subsequent request, the application module data control for User A's session requests an application module instance from the pool. The application module pool then:

    • Obtains an unreference instance.

      This could be instance AM1, obtained by following the same tasks as in Step 2, or another AM2 instance if it had become unreferenced in the meantime.

    • Activates the appropriate pending state for User A from the database.

    • Returns the application module instance to User A's data control.

In summary, the process of passivation, activation, and recycling preserves the state referenced by the data control across requests without requiring a dedicated application module instance for each data control. Both browser users in the above scenario are carrying on an application transaction that spans multiple HTTP requests, but the end users are unaware whether the passivation and activation is occurring in the background. They just continue to see the pending changes. While the pending changes never need to be saved into the underlying application database tables until the end user is ready to commit the logical unit of work. The application module pool makes a best effort to keep an application module instance "sticky" to the current data control whose pending state it is managing. This is known as maintaining user session affinity.

The best performance is achieved by minimizing the need for passivation/activation cycle in the deployed Fusion web application. You can control this by changing the default configuration parameters that specify the size of the application module pool, control the resource cleanup behavior of the pool manager, and tune the JDBC connection disconnect behavior used by application module instance at the end of a user request. For more information about design time configuration parameters that let you tune application module pools to reduce passivation, see What You May Need to Know About Application Module Pooling Configuration Parameters.

55.2.1 How to Confirm That Fusion Web Applications Use Optimistic Locking

Oracle recommends using optimistic locking, the default mode for web applications. Pessimistic locking should not be used for web applications as it creates pending transactional state in the database in the form of row-level locks. If pessimistic locking is set, state management will work, but the locking mode will not perform as expected. Behind the scenes, every time an application module is recycled, a rollback is issued in the JDBC connection. This releases all the locks that pessimistic locking had created.

Performance Tip

Always use the default mode optimistic locking for web applications. Only optimistic locking is compatible with the application module unmanaged release level mode, which allows the application module instance to be immediately released when a web page terminates. This provides the best level of performance for web applications that expect many users to access the application simultaneously.

To ensure your configuration uses optimistic locking, open the Business Components page of the adf-config.xml overview editor and confirm that Locking Mode (corresponding to the jbo.locking.mode property) is set to optimistic or optupdate. To open the adf-config.xml editor, in the Application Resources window, expand Descriptors and ADF META-INF folders and double-click the file node.

Optimistic locking (optimistic) issues a SELECT FOR UPDATE statement to lock the row, then detects whether the row has been changed by another user by comparing the change indicator attribute — or, if no change indicator is specified, the values of all the persistent attributes of the current entity as they existed when the entity object was fetched into the cache.

Optimistic update locking (optupdate) does not perform any locking. The UPDATE statement determines whether the row was updated by another user by including a WHERE clause that will match the existing row to update only if the attribute values are unchanged since the current entity object was fetched.

55.2.2 What You May Need to Know About Pending Changes Across HTTP Requests

The ADF state management mechanism relies on passivation and activation to manage the state of an application module instance. Implementing this feature in a robust way is only possible if all pending changes are managed by the application module transaction in the middle tier. The most scalable strategy is to keep pending changes in middle-tier objects and not perform operations that cause pending database state to exist across HTTP requests. This allows the highest leverage of the performance optimizations offered by the application module pool and the most robust runtime behavior for your application.

Caution:

When the jbo.doconnectionpooling configuration parameter is set to true — typically in order to share a common pool of database connections across multiple application module pools — upon releasing your application module to the application module pool, its JDBC connection is released back to the database connection pool and a ROLLBACK will be issued on that connection. This implies that all changes which were posted but not committed will be lost. On the next request, when the application module is used, it will receive a JDBC connection from the pool, which may be a different JDBC connection instance from the one it used previously. Those changes that were posted to the database but not committed during the previous request are lost.

The jbo.doconnectionpooling configuration parameter is set by checking the Disconnect Application Module Upon Release property on the Pooling and Scalability tab of the Edit Configuration dialog.

55.2.3 What You May Need to Know About Release Levels and postChanges() Method

The transaction-level postChanges() method exists to force the transaction to post unvalidated changes without committing them. This method is not recommended for use in web applications unless you can guarantee that the transaction will definitely be committed or rolled back during the same HTTP request. Failure to heed this advice can lead to strange results in an environment where both application modules and database connections can be pooled and shared serially by multiple different clients.

If for some reason you need to create a transactional state in the database in a particular request by invoking the postChanges() method or by calling a PL/SQL stored procedure, but you cannot issue a commit or rollback by the end of that same request, then you must release the application module instance with the reserved level from that request until a subsequent request when you either commit or rollback.

Note:

Use as short a period of time as possible between creation of transactional state in the database and performing the concluding commit or rollback. This ensures that reserved level doesn’t have to be used for a long time, as it has adverse effects on application’s scalability and reliability.

Once an application module has been released with reserved level, it remains at that release level for all subsequent requests until release level is explicitly changed back to managed or unmanaged level. So, it is your responsibility to set release level back to managed level once commit or rollback has been issued.

55.3 Testing to Ensure Your Application Module is Activation-Safe

If you have not explicitly tested that your application module functions when its pending state gets activated from a passivation snapshot, then you may encounter an unpleasant surprise in your production environment when heavy system load tests this aspect of your system for the first time.

55.3.1 How To Disable Application Module Pooling to Test Activation

As part of your overall testing plan, you should adopt the practice of testing your application modules with the jbo.ampool.doampooling configuration parameter set to false. This setting completely disables application module pooling and forces the system to activate your application module's pending state from a passivation snapshot on each page request. It is an excellent way to detect problems that might occur in your production environment due to assumptions made in your custom application code.

Note:

It is important to reenable application module pooling after you conclude testing and are ready to deploy the application to a production environment. The configuration property jbo.ampool.doampooling set to false is not a supported configuration for production applications and must be set to true before deploying the application.

For example, if you have transient view object attributes you believe should be getting passivated, this technique allows you to test that they are working as you expect. In addition, consider situations where you might have introduced:

  • Private member fields in application modules, view objects, or entity objects

  • Custom user session state in the Session user data hashtable

Your custom code likely assumes that this custom state will be maintained across HTTP requests. As long as you test with a single user on the JDeveloper Integrated WebLogic Server, or test with a small number of users, things will appear to work fine. This is due to the "stateless with affinity" optimization of the ADF application module pool. If system load allows, the pool will continue to return the same application module instance to a user on subsequent requests. However, under heavier load, during real-world use, it may not be able to achieve this optimization and will need to resort to grabbing any available application module instance and reactivating its pending state from a passivation snapshot. If you have not correctly overridden passivateState() and activateState() (as described in Managing Custom User-Specific Information During Passivation) to save and reload your custom component state to the passivation snapshot, then your custom state will be missing (i.e. null or back to your default values) after this reactivation step. Testing with jbo.ampool.doampooling set to false allows you to quickly isolate these kinds of situations in your code.

For more information about configuring Business Component configuration properties, How to Set Configuration Properties Declaratively.

55.3.2 What You May Need to Know About the jbo.ampool.doampooling Configuration Parameter

The jbo.ampool.doampooling configuration property corresponds to the Enable Application Module Pooling option in the Pooling and Scalability tab of the Edit Configuration dialog. By default, this checkbox is checked so that application module pooling is enabled. Whenever you deploy your application in a production environment the default setting of jbo.ampool.doampooling to true is the way you will run your application. But, as long as you run your application in a test environment, setting the property to false can play an important role in your testing. When this property is false, there is effectively no application pool. When the application module instance is released at the end of a request it is immediately removed. On subsequent requests made by the same user session, a new application module instance must be created to handle it and the pending state of the application module must be reactivated from the passivation store.

For more information about configuring Business Component properties, How to Set Configuration Properties Declaratively.

55.3.3 What You May Need to Know About State Management and Data Consistency

When an entity object is updated concurrently by more than one user and one of those users loses affinity with their application module during the update, it is possible to experience data corruption if a change indicator has not been defined for the entity object.

Consider a scenario where two users (User A and User B) access the same entity object at the same time.

  • After querying, User B changes the value of an attribute (for example, changing the Dept attribute value from Accounting to Research) and then commits.

  • Meanwhile, User A has also queried and sees the same record where the value of the Dept attribute is Accounting.

  • User A loses affinity with the application module that was used to query, resulting in passivation.

  • User A then changes the Dept attribute value from Accounting to Sales and commits, resulting in activation.

  • When activation occurs, the new value of the Dept attribute is activated but the browser is unaware of the data change, and the commit proceeds without issue.

To avoid this type of scenario, define a change indicator attribute for all entity objects. For more information, see How to Protect Against Losing Simultaneously Updated Data.

55.3.4 What You May Need to Know About State Management and Subclassed Entity Objects

If your application employs subclassed entity objects, the key attribute of new entities must be prepopulated. If the key is not prepopulated, passivation and activation will fail. You can prepopulate the key attribute by overriding the create() method or by using the DBSequence type that will assign a temporary negative value to the key before the real value is fetched from the database after commit. For more information, see How to Get Trigger-Assigned Primary Key Values from a Database Sequence.

55.4 Controlling Where Model State Is Saved

By default, passivation snapshots are saved in the database, but you can configure it to use the file system as an alternative.

When saving to the database, the passivated XML snapshot is written to a BLOB column in a table named PS_TXN, using a connection specified by the jbo.server.internal_connection property. Each time a passivation record is saved, it is assigned a unique passivation snapshot ID based on the sequence number taken from the PS_TXN_SEQ sequence. The application module handle held by the application module data control in the ADF binding context remembers the latest passivation snapshot ID that was created on its behalf and remembers the previous ID that was used.

55.4.1 How to Control the Schema Where the State Management Table Resides

The ADF runtime recognizes a configuration property named jbo.server.internal_connection that controls which database connection and schema should be used for the creation of the PS_TXN table and the PS_TXN_SEQ sequence. If you don't set the value of this configuration parameter explicitly, then the state management facility creates the temporary tables using the credentials of the current application database connection.

To keep the temporary information separate, the state management facility uses a different connection instance from the database connection pool, but the database credentials are the same as the current user. Since the framework creates temporary tables, and possibly a sequence if they don't already exist, the implication of not setting a value for the jbo.server.internal_connection is that the current database user must have CREATE TABLE, CREATE INDEX and CREATE SEQUENCE privileges. Since this is often not desirable, Oracle recommends always supplying an appropriate value for the jbo.server.internal_connection property, providing the credentials for a state management schema where table and schema be created. Valid values for the jbo.server.internal_connection property in your configuration are:

  • A fully qualified JDBC connection URL like:

    jdbc:oracle:thin:username/password@host:port:SID

  • A JDBC datasource name like:

    java:/comp/env/jdbc/YourJavaEEDataSourceName

Performance Tip

When creating the PS_TXN table, use securefiles to store LOB data (the content column), and create a primary column index on the PS_TXN table as global, partitioned reverse key index. The securefile configuration delivers superior performance over the basicfile configuration when working with LOB data. The reverse key index helps by reducing contention that can happen when the rate of inserts is high.

55.4.2 How to Configure the Passivation Store

Passivated information can be stored in several places. You can control it by configuring an option in the application module configuration. The choices are database or a file stored on local file system:

  • File

    This choice may be the fastest available, because access to the file is faster then access to the database. This choice is good if the entire middle tier is either installed on the same machine or has access to a commonly shared file system, so passivated information is accessible to all. Usually, this choice may be good for a small middle tier where one Oracle WebLogic Server domain is used. In other words this is a very suitable choice for small middle tier such as one Oracle WebLogic Server instance with all its components installed on one physical machine. The location and name of the persistent snapshot files are determined by jbo.tmpdir property if specified. It follows usual rules of ADF property precedence for a configuration property. If nothing else is specified, then the location is determined by user.dir if specified. This is a default property and the property is OS specific.

  • Database

    This is the default choice. While it may be a little slower than passivating to file, it is by far the most reliable choice. With passivation to file, the common problem might be that it is not accessible to Oracle WebLogic Server instances that are remotely installed. In this case, in a cluster environment, if one node goes down the other may not be able to access passivated information and then failover will not work. Another possible problem is that even if file is accessible to the remote node, the access time for the local and remote node may be very different and performance will be inconsistent. With database access, time should be about the same for all nodes.

To set the value of your choice in design time, set the property jbo.passivationstore to database or file. The value null will indicate that a connection-type-specific default should be used. This will use database passivation for Oracle or DB2, and file serialization for any others.

55.4.3 What Happens at Runtime: What Model State Is Saved and When It Is Cleaned Up

The information saved by application model passivation is divided in two parts: transactional and nontransactional state. Transactional state is the set of updates made to entity object data – performed either directly on entity objects or on entities through view object rows – that are intended to be saved into the database. Nontransactional state comprises view object runtime settings, such as the current row index, WHERE clause, and ORDER BY clause.

The information saved as part of the application module passivation snapshot includes the following.

Transactional State
  • New, modified, and deleted entities in the entity caches of the root application module for this user session's (including old/new values for modified ones).

Nontransactional State
  • For each active view object (both statically and dynamically created):

    • Current row indicator for each row set (typically one)

    • New rows and their positions. (New rows are treated differently then updated ones. Their index in the view object is traced as well.)

    • ViewCriteria and all related parameters such as view criteria row, etc.

    • Flag indicating whether or not a row set has been executed

    • Range start and Range size

    • Access mode

    • Fetch mode and fetch size

    • Any view object-level custom data

      Note:

      Transient view object attributes can be saved if they are selected for passivation at design time. However, use this feature judiciously because this results in a snapshot that will grow in size with the number of rows that have been retrieved. See How to Manage the State of View Objects for information on enable the passivation of transient attributes.

    • SELECT, FROM, WHERE, and ORDER BY clause if created dynamically or changed from the View definition

Note:

If you enable ADF Business Components runtime diagnostics, the contents of each XML state snapshot are also saved. See How to Enable ADF Business Components Debug Diagnostics for information on how to enable diagnostics.

Other information, such as user-specific information or instant variables in ADF Business Components objects, is not saved as part of the passivation snapshot. To save this type of information, use the techniques described in Managing Custom User-Specific Information During Passivation.

55.5 Cleaning Up the Model State

Under normal circumstances, the ADF state management facility provides automatic cleanup of the passivation snapshot records.

55.5.1 How to Clean Up Temporary Storage Tables Resulting from Passivation

JDeveloper supplies the adfbc_purge_statesnapshots.sql script to help with periodically cleaning up the application module state management table. You can find this file in the oracle_common subdirectory of your Oracle Middleware installation directory (for example, ORACLE_HOME\oracle_common\common\sql).

Persistent snapshot records can accumulate over time if the server has been shutdown in an abnormal way, such as might occur during development or due to a server failure. Running the script in SQL*Plus will create the BC4J_CLEANUP PL/SQL package. The two relevant procedures in this package are:

  • PROCEDURE Session_State(olderThan DATE)

    This procedure cleans up application module session state storage for sessions older than a given date.

  • PROCEDURE Session_State(olderThan_minutes INTEGER)

    This procedure cleans up application module session state storage for sessions older than a given number of minutes.

You can schedule periodic cleanup of your ADF temporary persistence storage by submitting an invocation of the appropriate procedure in this package as a database job.

You can use an anonymous PL/SQL block like the one shown in the following example to schedule the execution of bc4j_cleanup.session_state() to run starting tomorrow at 2:00 am and each day thereafter to cleanup sessions whose state is over 1 day (1440 minutes) old.

SET SERVEROUTPUT ON
DECLARE
  jobId    BINARY_INTEGER;
  firstRun DATE;
BEGIN
  -- Start the job tomorrow at 2am
  firstRun := TO_DATE(TO_CHAR(SYSDATE+1,'DD-MON-YYYY')||' 02:00',
              'DD-MON-YYYY HH24:MI');
   -- Submit the job, indicating it should repeat once a day
  dbms_job.submit(job       => jobId,
                  -- Run the ADF Purge Script for Session State
                  -- to cleanup sessions older than 1 day (1440 minutes)
                  what      => 'bc4j_cleanup.session_state(1440);',
                  next_date => firstRun,
                  -- When completed, automatically reschedule
                  -- for 1 day later
                  interval  => 'SYSDATE + 1'
                 );
  dbms_output.put_line('Successfully submitted job. Job Id is '||jobId);
END;
.
/

55.5.2 What Happens at Runtime: When a Passivation Record Is Updated

When a passivation record is saved to the database on behalf of a session cookie, this passivation record gets a new, unique snapshot ID. The passivation record with the previous snapshot ID used by that same session cookie is deleted as part of the same transaction. In this way, assuming no server failures, there will only ever be a single passivation snapshot record per active end-user session.

55.5.3 What Happens at Runtime: When a Passivation Snapshot is Removed on Unmanaged Release

The passivation snapshot record related to a session cookie is removed when the application module is checked into the pool with the unmanaged state level. This can occur when:

  • Your code specifically calls resetState() on the application module data control.

  • Your code explicitly invalidates the HttpSession, for example, as part of implementing an explicit "Logout" functionality.

  • The HttpSession times out due to exceeding the session timeout threshold for idle time and failover mode is disabled (which is the default).

In each of these cases, the application module pool also resets the application module referenced by the session cookie to be "unreferenced" again. Since no changes were ever saved into the underlying database tables, once the pending session state snapshots are removed, there remains no trace of the unfinished work the user session had completed up to that point.

55.5.4 What Happens at Runtime: Passivation Snapshot Retained in Failover Mode

When the failover mode is enabled, if the HttpSession times out due to session inactivity, then the passivation snapshot is retained so that the end user can resume work upon returning to the browser.

After a break in the action, when the end user returns to the browser and continues to use the application, it continues working as if nothing had changed. The session cookie is used to reactivate any available application module instance with the user's last pending state snapshot before handling the request. So, even though the users next request will be processed in the context of a new HttpSession (perhaps even in a different application server instance), the user is unaware that this has occurred.

Note:

If an application module was released with Reserved level then when the HttpSession times out, the user will have to go through an authentication process, and all unsaved changes are lost.

55.6 Managing the HttpSession in Fusion Web Applications

Since HTTP is a stateless protocol, the server receives no implicit notice that a client has closed the browser or gone away for the weekend. Therefore any Java EE-compliant server provides a standard, configurable session timeout mechanism to allow resources tied to the HTTP session to be freed when the user has stopped performing requests. You can also programmatically force a timeout.

55.6.1 How to Configure the Implicit Timeout Due to User Inactivity

You configure the session timeout threshold using the <session-timeout> tag in the web.xml file. The default value is typically around 30 minutes, depending on the container.

55.6.2 How to Code an Explicit HttpSession Timeout

To end a user's session before the session timeout expires, you can call the invalidate() method on the HttpSession object from a backing bean in response to the user's click on a Logout button or link. This cleans up the HttpSession in the same way as if the session time had expired. Using JSF and ADF, after invalidating the session, you must perform a redirect to the next page you want to display, rather than just doing a forward. The following example shows sample code to perform this task from a Logout button.

public String logoutButton_action() throws IOException{
  ExternalContext ectx = FacesContext.getCurrentInstance().getExternalContext();
  HttpServletResponse response = (HttpServletResponse)ectx.getResponse();
  HttpSession session = (HttpSession)ectx.getSession(false);
  session.invalidate();
  response.sendRedirect("Welcome.jspx");
  return null;
}

As with the implicit timeouts, when the HTTP session is cleaned up this way, it ends up causing any referenced application modules to be marked unreferenced.

55.6.3 What Happens at Runtime: How Application Modules are Handled When the HTTP Times Out

When the HttpSession times out, the BindingContext goes out of scope, and along with it, any data controls that might have referenced application modules released to the pool in the managed state level. The application module pool resets any of these referenced application modules and marks the instances unreferenced again.

55.7 Managing Custom User-Specific Information During Passivation

It is fairly common practice to add custom user-defined information in the application module in the form of member variables or some custom information stored in oracle.jbo.Session user data hashtable. The ADF state management facility provides a mechanism to save this custom information to the passivation snapshot as well, by overriding the passivateState() method and the activateState() methods in the ApplicationModuleImpl class.

Note:

Similar methods are available on the ViewObjectImpl class and the EntityObjectImpl class to save custom state for those objects to the passivation snapshot as well.

55.7.1 How to Passivate Custom User-Specific Information

You can override passivateState() and activateState() to ensure that custom application module state information is included in the passivation/activation cycle. The following example shows how this is done.

In the example, jbo.counter contains custom values you want to preserve across passivation and activation of the application module state. Each application module has an oracle.jbo.Session object associated with it that stores application module-specific session-level state. The session contains a user data hashtable where you can store transient information. For the user-specific data to "survive" across application module passivation and reactivation, you need to write code to save and restore this custom value into the application module state passivation snapshot.

/**
 * Overridden framework method to passivate custom XML elements
 * into the pending state snapshot document
 */
public void passivateState(Document doc, Element parent) {
  // 1. Retrieve the value of the value to save
  int counterValue = getCounterValue();
  // 2. Create an XML element to contain the value
  Node node = doc.createElement(COUNTER);
  // 3. Create an XML text node to represent the value
  Node cNode = doc.createTextNode(Integer.toString(counterValue));
  // 4. Append the text node as a child of the element
  node.appendChild(cNode);
  // 5. Append the element to the parent element passed in
  parent.appendChild(node);
}
/**
 * Overridden framework method to activate  custom XML elements
 * into the pending state snapshot document
 */
public void activateState(Element elem) {
  super.activateState(elem);
  if (elem != null) {
    // 1. Search the element for any <jbo.counter> elements
    NodeList nl = elem.getElementsByTagName(COUNTER);
    if (nl != null) {
      // 2. If any found, loop over the nodes found
      for (int i=0, length = nl.getLength(); i < length; i++) {
        // 3. Get first child node of the <jbo.counter> element 
        Node child = nl.item(i).getFirstChild();
        if (child != null) {
          // 4. Set the counter value to the activated value
          setCounterValue(new Integer(child.getNodeValue()).intValue()+1);
          break;
        }
      }
    }
  }
}
/*
 * Helper Methods
 */
private int getCounterValue() {
  String counterValue = (String)getSession().getUserData().get(COUNTER);
  return counterValue == null ? 0 : Integer.parseInt(counterValue);
}
private void setCounterValue(int i) {
  getSession().getUserData().put(COUNTER,Integer.toString(i));
}
private static final String COUNTER = "jbo.counter";

55.7.2 What Happens When You Passivate Custom Information

As the example in How to Passivate Custom User-Specific Information illustrates, when activateState() is overridden, the following steps are performed:

  1. Search the element for any jbo.counter elements.

  2. If any are found, loop over the nodes found in the node list.

  3. Get first child node of the jbo.counter element.

    It should be a DOM Text node whose value is the string you saved when your passivateState() method above got called, representing the value of the jbo.counter attribute.

  4. Set the counter value to the activated value from the snapshot.

When passivateState() is overridden, it performs the reverse job by doing the following:

  1. Retrieve the value of the value to save.

  2. Create an XML element to contain the value.

  3. Create an XML text node to represent the value.

  4. Append the text node as a child of the element.

  5. Append the element to the parent element passed in.

Note:

The API's used to manipulate nodes in an XML document are provided by the Document Object Model (DOM) interfaces in the org.w3c.dom package. These are part of the Java API for XML Processing (JAXP). See the Javadoc for the Node, Element, Text, Document, and NodeList interfaces in this package for more details.

55.7.3 What You May Need to Know About Activating Custom Information

The activateState() method is called at the end of the activation process after the view objects have been activated. Most of the time this is where you want to place the application module state activation logic. However, if your application module activation logic needs to set up custom state information before the ADF state management facility activates the view objects (for example, you might need to write custom code to allow the view objects to internally reference custom values at execution time), then the prepareForActivation() method in the ApplicationModuleImpl class would be the right place because it fires at the beginning of the activation process.

55.8 Managing the State of View Objects During Passivation

By default, all view objects are marked as passivation-enabled, so their state will be saved. However, view objects that have transient attributes do not have those attributes passivated by default. You can change how a view object is passivated, and even which attributes are passivated, using the Tuning page of the view object overview editor.

55.8.1 How to Manage the State of View Objects

Each view object can be declaratively configured to be passivation-enabled or not. If a view object is not passivation enabled, then no information about it gets written in the application module passivation snapshot.

Performance Tip

There is no need to passivate read-only view objects that are not used to display values in the UI but are simply used in re-entrant methods for calculation or derivation of values. This eliminates the performance overhead associated with passivation and activation and reduces the CPU usage needed to maintain the application module pool.

Before you begin:

It may be helpful to have an understanding of passivation in view objects. For more information, see Managing the State of View Objects During Passivation.

To set the passivation state of a view object:

  1. In the Applications window, double-click the view object for which you want to set the passivation state.
  2. In the overview editor, click the General navigation tab.
  3. On the General page, expand the Tuning section and select Passivate State to make sure the view object data is saved.
  4. Optionally, you can select Including All Transient Attributes to passivate all transient attributes at this time, but see What You May Need to Know About Passivating Transient View Objects for additional information.

55.8.2 What You May Need to Know About Passivating View Objects

The activation mechanism is designed to return your view object to the state it was in when the last passivation occurred. To ensure that, Oracle ADF stores in the state snapshot the values of any bind variables that were used for the last query execution. These bind variables are in addition to those that are set on the row set at the time of passivation. The passivated state also stores the user-supplied WHERE clause on the view object related to the row set at the time of passivation.

55.8.3 How to Manage the State of Transient View Objects and Attributes

Because view objects are marked as passivated by default, a transient view object — one that contains only transient attributes — is marked to be passivation enabled, but only passivates its information related to the current row and other nontransactional state.

Performance Tip

Transient view object attributes are not passivated by default. Due to their nature, they are usually intended to be read-only and are easily re-created. So, it often doesn't make sense to passivate their values as part of the XML snapshot. This also avoids the performance overhead associated with passivation and activation and reduces the CPU usage needed to maintain the application module pool.

Before you begin:

It may be helpful to have an understanding of passivation in view objects. For more information, see Managing the State of View Objects During Passivation.

You will also need to perform the following task:

  • Open the application in JDeveloper.

To individually set the passivation state for transient view object attributes:

  1. In the Applications window, double-click the view object that contains the transient attributes for which you want to specify the passivation state.
  2. In the overview editor, click the Attributes navigation tab.
  3. On the Attributes page, select the transient attribute you want to passivate and click the Details tab.
  4. In the Details page, select the Passivate checkbox.

55.8.4 What You May Need to Know About Passivating Transient View Objects

Passivating transient view object attributes is more costly resource-wise and performance- wise, because transactional functionality is usually managed on the entity object level. Since transient view objects are not based on an entity object, this means that all updates are managed in the view object row cache and not in the entity cache. Therefore, passivating transient view objects or transient view object attributes requires special runtime handling.

Usually passivation only saves the values that have been changed, but with transient view objects passivation has to save entire row. The row will include only the view object attributes marked for passivation.

55.8.5 How to Use Transient View Objects to Store Session-level Global Variables

Using passivation, you can use a view object to store one or more global variables, each on a different transient attribute. When you mark a transient attribute as passivated, the ADF Business Components framework will remember the transient values across passivation and activation in high-throughput and failover scenarios. Therefore, it is an easy way to implement a session-level global value that is backed up by the state management mechanism, instead of the less-efficient HTTP Session replication. This also makes it easy to bind to controls in the UI if necessary.

There are two basic approaches to store values between invocations of different screens, one is controller-centric, and the other is model-centric.

To implement this task in the ADF Controller layer using the controller-centric approach involves storing and referencing values using attributes in the pageFlow scope. This approach might be appropriate if the global values do not need to be referenced internally by any implementations of ADF Business Components. For more information about pageFlow scope, see What You May Need to Know About Memory Scope for Task Flows.

The model-centric approach involves creating a transient view object, which is conceptually equivalent to a nondatabase block in Oracle Forms.

Before you begin:

It may be helpful to have an understanding of passivation in view objects. For more information, see Managing the State of View Objects During Passivation.

You will also need to perform the following tasks:

  1. Open the application in JDeveloper.

  2. Create a new view object using the View Object wizard, as described in How to Create an Entity-Based View Object.

    1. On Page 1 of the wizard, select the option for Data Source: Programmatic.

    2. On Page 2, click New to define the transient attribute names and types the view object should contain.

    3. On Page 3, set the Updatable option to Always for each attribute.

    4. Click Finish and the newly created view object appears in the overview editor.

  3. Add an instance of the view object with transient attributes to your application module's data model, as described in Adding Master-Detail View Object Instances to an Application Module.

To store session-level global variables using the model-centric approach:

  1. Disable any entries from being performed in the view object:

    1. In the overview editor, on the General page, expand the Tuning section.

    2. In the Retrieve from the Database group, select the No Rows option.

  2. Make sure data in the view object is not cleared out during a rollback operation. To implement this, you enable a custom Java class for the view object and override two rollback methods.

    1. In the overview editor, click the Java navigation tab, and click the Edit icon for the Java Classes section.

    2. In the Select Java Options dialog, select Generate View Object Class and click OK.

    3. In the overview editor, click the link next to View Object Class in the Java Classes section.

    4. From the main menu, choose Source > Override Methods.

    5. In the Override Methods dialog, select the beforeRollback() and afterRollback() methods to override, and click then OK.

    6. In both the beforeRollback() and afterRollback() methods, comment out the call to super in the Java code.

  3. Create an empty row in the view object when a new user begins using the application module:

    1. In the Applications window, double-click the application module.

    2. Generate an application module Java class if you don't have one yet, as described in How to Generate a Custom Class for an Application Module.

    3. Override the prepareSession() method of the application module, as described in How to Override a Built-in Framework Method.

    4. After the call to super.prepareSession(), add code to create a new row in the transient view object and insert it into the view object.

Now you can bind read-only and updatable UI elements to the "global" view object attributes just as with any other view object using the Data Controls panel.