Showing posts with label User Data. Show all posts
Showing posts with label User Data. Show all posts

Wednesday, August 15, 2012

Solution to Control Global Editable/Readonly Mode in ADF BC

There is requirement, when we need to disable/enable editable data access globally per user session for entire application. When implementing this requirement, you should keep in mind ADF BC passivation/activation - data should stay in correct state even between activation/passivation iterations. Global control for editable mode in ADF BC can based on - Solution for Sharing Global User Data in ADF BC. Sample application in this post, is based on that article. Described solution for global editable/readonly mode is generic across all Task Flows, EO's and AM's, there is no dependency to specific Task Flow, EO or AM.

Download sample application - UserDataADFBCSample_v2.zip. Sample architecture is described here:


Editable/Readonly access is set from session scope, user can trigger it by certain action. We should not access session scope directly in ADF BC, its why we override Data Control class - CustomADFBCDataControl (read more here) and pass session scope value into ADF BC User Data. CustomADFBCDataControl class contains overriden executeIteratorBindingIfNeeded() method responsible to prepare iterator binding, this method is called each time when data collection is accessed, it allows to pass correct editable or readonly flag and store it in User Data. Custom User Data is passivated/activated with ADF BC lifecycle, same data is accessed from generic EO implementation class isAttributeUpdateable(int) method.

Solution is tested with disabled AM pooling, to proof it works under stress load:


isAttributeUpdateable(int) method checks User Data variable custom_ReadOnlyMode and decided to render editable or readonly data:


executeIteratorBindingIfNeeded() method updates User Data variable custom_ReadOnlyMode, each time when data is accessed - this allows to force editable or readonly rendering mode on any EO:


By default form is rendered in editable mode, in my sample:


User changes sessions scope variable to readonly, ADF BC custom User Data is updated - page refresh happens and EO isAttributeUpdateable(int) method marks all attributes to be readonly:


This change happens globally, across all Task Flows, AM's and EO's. Locations tab renderes read-only data from Locations Task Flow:


Switch back to editable - data is rendered editable again:


In the log you can see that, two AM were initialized - Locations and Departments. Data access (readonly/editable) variable was passed each time when data was accessed from iterator:


This allows to mark EO attributes in generic EO class for editable/readonly access.

Sample application implements different Task Flow for each tab, this means editable/readonly access is independent of Task Flow and control directly in ADF BC level through custom User Data variable initialized from Data Control class on data access generically:


Wednesday, May 23, 2012

Solution for Sharing Global User Data in ADF BC

This post will describe solution how to share custom user data in ADF BC. There is a way to access HTTP session scope directly from ADF BC - Bad Practice for Session Scope Access in ADF BC, while you can follow this approach in exceptional cases, this is not recommended from enterprise architecture point of view. Mainly because you may face snow ball effect - there will be more and more ADF BC and ADF UI dependency introduced over time and at the end will make system not maintainable. Solution is generic and will cover following requirements:

1. Custom user data will be initialized for every AM instance automatically
2. Custom user data will be preserved between passivation/activation requests

Download sample application - UserDataADFBCSample.zip. Implemented solution is based on such architecture:


Where on the higher level we have user preferences variables stored in HTTP session. For each Data Control, where we want associated Application Module to be initialized with custom variables for User Data, is defined custom Data Control factory class. Data Control factory class is calling generic Application Module implementation class, where it invokes generic method to populate User Data. Every AM extends from generic Application Module implementation class - this means it gets User Data set. There are standard methods for passivation and activation overriden inside generic Application Module implementation class - this allows to preserve custom User Data between passivation/activation requests.

From developer perspective, in order to use this solution for specific AM - only one configuration thing is required - set custom Data Control factory class, the rest will be handled by implemented generic classes. Select Data Control usage from DataBindings.cpx:


Set Data Control factory class to our custom class:


I will describe now, what happens behind the scenes - how this solution works based on proposed architecture.

1. Make sure Model project is configured with Base Classes, specifically in this case with generic Application Module class (responsible for extended activation/passivation process and User Data initialization):


Generic Application Module class overrides ADF BC framework passivateState(...) method - its the place, where we store custom variables from User Data along with standard ADF BC data into PS_TXN table:


activateState(...) method is overriden as well - to initialize activated custom variables back into User Data:


Generic User Data initialization method is pretty simple in this example, it stores only one variable:


Every AM should extend from generic class. For test purpose, I have implemented custom method to print User Data entry - you will see that it will be preserved across passivation/activation requests thanks to the Application Module implementation generic class - CustomAMImpl:



2. There is custom Data Control factory class defined. This class substitutes Data Control class with our own custom class:


Custom Data Control class overrides only one method - setSessionCookie(...). This method is called by the framework only once, when Data Control gets initialized (Data Control may get initialized multiple times during the same session, if you are using isolated Task Flows (not recommended)). From setSessionCookie(...) method, we are getting instance of generic Application Module implementation class (by casting Data Provider) and initializing User Data with user preferences variable value stored in HTTP session scope. This is completely generic, because action is done through generic class without touching underlying AM for selected Data Control:


3. Time to test. You should test with AM pooling OFF - means will simulate stress test environment and force AM instance to passivate/activate on each request:


With jbo detail logging enabled, we can see that passivation request stores into PS_TXN table along with standard ADF BC data, our own custom User Data also (because overriden method in CustomAMImpl):


Tags for custom entry and other data is declared inside CustomAMImpl. There is limitation of max 10 entries for custom User Data passivation is set, you can increase it (its safer to control this, in case someone will start to enter hundreds of entries into User Data):


When fragment is loaded, based on our test - passivation/activation happens (AM pooling is OFF). With default implementation of User Data - values will be lost, but now CustomAMImpl takes care for this. Press Print Uset Data Entry to test if variable from User Data is retrieved correctly:


Variable data is printed:


4. Task Flow isolated mode. This solution works for Task Flow isolated mode:


When task flow is opened in isolated mode - setSessionCookie(...) method from Data Control class gets invoked again:


This mean, there is new fresh AM instance created of the same Application Module. But, because setSessionCookie(...) method is called - user data gets initialized for that new instance. User data can be initialized not only during login process, but during session lifetime as well.

We can test this by opening isolated task flow:


Print user data variable:


Data is printed successfully:


5. Test without passivation/activation for custom User Data. Comment out custom code for User Data passivation inside CustomAMImpl class:


There will be no custom data added into passivation package:


User data will be NULL, after passivation/activation request: