Oracle® Application Development Framework Developer's Guide For Forms/4GL Developers 10g (10.1.3.1.0) Part Number B25947-01 |
|
|
View PDF |
The information saved by passivation is divided in two parts: transactional and non-transactional 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. Non-transactional state comprises view object runtime settings, such as the current row index, WHERE clause, and ORDERBY clause.
The information saved as part of the application module passivation "snapshot" includes the following.
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).
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 VO is traced as well)
ViewCriteria and all its 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
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. See Section 5.5.3.2, "Enabling ADF Business Components Debug Diagnostics" for more information on how to enable diagnostics. |
By default, passivation snapshots are saved in the database, but you can configure it to use the file system as an alternative.
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 ADF session cookie 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.
The ADF runtime recognizes a configuration property named jbo.server.internal_connection
that controls which database connection/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 will use a different connection instance from the database connection pool, but the database credentials will be the same as the current user. Since the framework creates temporary tables, and possibly a sequence if they don't already exists, 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:
someuser
/
somepassword
@
host
:
port
:
SID
A JDBC datasource name like:
java:/comp/env/jdbc/YourJ2EEDataSourceName
Passivated information can be stored in several places. You can control it programmatically or 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 as access to the file is faster then access to the database. This choice is good if the entire middle tier (one or multiple Oracle Application Server installation(s) and all their OC4J instances) is either installed on the same machine or have 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 Application Server is used. In other words this is very suitable choice for small middle tier such as one Oracle Application Server 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 Application 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.
To set the storage programmatically use the method setStoreForPassiveState()
of interface oracle.jbo.ApplicationModule
. The parameter values that you can pass are:
PASSIVATE_TO_DATABASE
PASSIVATE_TO_FILE
Under normal circumstances, the ADF state management facility provides automatic cleanup of the passivation snapshot records.
When a passivation record is saved to the database on behalf of a session cookie, as described above, 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.
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.
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 continue their work when they return to their browser.
After a break in the action, when the end user returns to his browser and continues to use the application, it continues working as if nothing had changed. In failover mode, the ADF runtime saves an additional browser cookie that the ADF runtime uses to track the latest passivation snapshot ID for each client running an application in failover mode. 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. The additional browser cookie is used to reactivate any available application module instance with the user's last pending state snapshot before handling the request.
Note: If an application module was released with reserved level then theHttpSession times out, the user will have to go through authentication process, and all unsaved changes are lost. |
Since HTTP is a stateless protocol, the server receives no implicit notice that a client has closed his browser or gone away for the weekend. Therefore any J2EE-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.
You configure the session timeout threshold using the session-timeout tag in the web.xml
file. The default value is 35 minutes. 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.
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. Example 28-5 shows the code used by the SRLogout.java
backing bean in the SRDemo application to perform this task.
Example 28-5 Programatically termin
// In SRLogout.java, backing bean for the logout.jspx page 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("SRWelcome.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.
JDeveloper supplies the bc4jcleanup.sql
script in the ./BC4J/bin
directory to help with periodically cleaning up the state management table. 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 procedures 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 Example 28-6 to schedule the execution of bc4j_cleanup.session_state()
to run starting tomorrow at 2:00am and each day thereafter to cleanup sessions whose state is over 1 day (1440 minutes) old.
Example 28-6 Scheduling Periodic Cleanup of the State Management Table
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 BC4J Cleanup 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; . /