If you are using instance variables in your Application Module (AM) class, as part of some algorithm in your application and you expect these variables to persist from one user request to another so that the algorithm works as expected, passivation (and activation) support for these variables must be added. This FAQ will show you how to do this and at the same time build a "mini sub-framework" to avoid duplication of the basic passivation-activation code that you must write for all your AMs in your project.
Main Theme
Some Introductory Information
Application Module pooling is the process of acquiring an AM from and releasing it back to a pool. This pool of AMs contains a limited number of AMs defined in a configuration file. When all AMs have been associated with user sessions, an already-used AM must be re-used. For this to happen, the existing data associated with the AM must be saved. The passivation process saves information used by the AM into the passivation store - usually a database, while the activation process does exactly the opposite, restores the state of the AM from the passivation store. The information is saved as an XML document. Take a look at FAQ #14 - How to test an Application Module for activation-safe in ADF 11g for additional information regarding the passivation and activation processes.
You can add custom passivation and activation logic in your AM class by overriding the ApplicationModuleImpl methods passivateState() and activateState() respectively. The passivateState() method should create the necessary XML elements for each AM instance variable that you need to passivate. The activateState() method should detect the specific XML elements that identify the instance variables in the passivated XML document and restore them back into the instance variables.
A "mini sub-framework" for passivating/activating Application Module instance variables
If you are using multiple AMs, you can avoid code duplication of this basic implementation of the passivateState() and activateState() methods by putting it in a base AM class. Then derive all of your other AMs from this base AM. The base AM class can then provide activation/passivation "triggers" to which the derived AMs can respond. The implementation of passivateState() in the base AM should look like this:
As you can see, after calling super.passivateState() to allow the framework to do its own processing, we call onStartPassivation() to signal derived AMs that a passivation process is taking place. Derived AMs should override onStartPassivation() in order to provide a list of the identifiers - each identifier tied to a specific instance variable - to be passivated. Below is an example implementation of onStartPassivation() by a derived AM class.
In this case a single passivation identifier call EMPLOYEE_LIST_ID is returned to indicate a list of Employees class instance variable.
passivateState() then loops for all passivation identifiers, creates an XML node for each identifier in the XML passivation document and calls onPassivate() passing the passivation identifier as an argument to it. A derived AM will override this method in order to return the passivation data for the specific passivation identifier. An example implementation is shown below:
In this case the passivation identifier is checked and if it matches the expected identifier, the passivation data is supplied.
Finally, passivateState() calls onEndPassivation() to indicate the end of the passivation process.
The reverse process of activation is similar. activateState() calls onStartActivation() to get the activation identifiers. It then loops looking for each activation identifier in the activated XML document. If the identifier is found, calls onActivate() supplying the activation data along with its activation identifier as arguments. The derived AM can identify the instance variable being activated - based on the activation identifier, and restore it.
To test the framework, bring up the Edit Business Components Configuration dialog and disable Application Module Pooling as it shown below. You do this by right-clicking on the AM in the Application Navigator and selecting Configurations... Then you click on Edit... to edit the specific configuration.
This will in effect initiate a passivation-activation cycle for each request when the application is running. Finally save the configuration, enable debug logging on the console and run the application. You should see your AM class instance variables being activated.
To further explore this framework, the source code is supplied below.
Conclusion
If you are using class instance variables in the BC layer and you want their values to persit from one request to another, then passivating-activating these variables becomes necessary. The ADF BC framework allows you to do this by overriding the passivateState() and activateState() methods.
Until the next time, keep on JDeveloping!
Code
http://jdeveloperfaq.googlecode.com/files/JDeveloperFAQNo21.rar
Hi, useful article, thanks.
ReplyDeleteAny idea why when you disable Application Module Pooling it does a passivation-activation cycle for each request? I would have thought with no pooling no passivation would be needed - each user session would get its own App Module instance.
Cheers,
Kevin.
Hi,
ReplyDeleteBy disabling the Application Module Pooling there is no pool in effect. So when the application module instance is released at the end of a request, there is no pool to release it to, which forces the framework to passivated it.
On a subsequent request made by the same session, since there is no pool, the pending state of the application module must be reactivated from the passivation store.
I hope it is clear. Take a look at section 40.10.1 Understanding the jbo.ampool.doampooling Configuration Parameter at http://download.oracle.com/docs/cd/E14571_01/web.1111/b31974/bcstatemgmt.htm#CHDGAIFA
Hi Nick,
ReplyDeleteAh, ok - my thinking was that if there was no pool then each user session would get a dedicated app module instance - if there was no pool to release it to, it would not be released.
Thanks for the link to the docs - I notice it says that "jbo.ampool.doampooling set to false is not a supported configuration for production application", so the app module pool is an integral part and must always be used.
My app relies heavily on an existing PL/SQL infrastructure that uses package states. This means that a user session must always use the same database session. I guess I need to have a large AM pool to reduce the chance of an app module instance other than the original being picked up for the session - and I need to detect when this does happen and inform the user the session has expired.
Cheers,
Kevin.
Hi,
ReplyDeletehaving no application pool does not mean that the application won't be released. Remember that the underlying protocol (HTTP) is stateless, so the application module has to be re-associated with each request every time. The question is from to get the associated AM: from the pool or the passivation store? If you have no application pool it will have to be the passivation store.
Anyway, take a look at this article by Steve Muench regarding some of your questions. http://www.oracle.com/technetwork/developer-tools/jdev/index-097578.html
Also Andrejus Baranovskis has this post where he examines the relationship of AM pools and database connection pools: http://andrejusb.blogspot.com/2010/02/optimizing-oracle-adf-application-pool.html
Hope that they help is some way.
Hi Nick,
ReplyDeleteThanks for the clarification and the references.
Cheers,
Kevin.
Thank you for reading!
ReplyDeleteCheers,
Nick
Cool!
ReplyDelete