Tuesday, January 31, 2012

Data Control and Session Scope Use Case to Control Web Browser Tabs

Sometimes there are unique requirements, for example we want to allow access to application screens only from single browser tab. If application screen is accessed within the same session, but from another browser tab - access should be disabled. In order to implement such requirement, we need to track if application was already accessed from the same session - if yes, access id disabled. We can implement this by combination of Data Control and Session Scope usage.

Blog reader was asking what is the difference storing variable value in Session Scope or in Data Control - ADF Region Communication - Region Refresh Through Contextual Event. One of the main differences - Session Scope is available across session and is accessible from multiple browser tabs. Data Control is always reset for the new browser tab. Also you need to be careful not to reset Data Control, by entering into Isolated Scope TF.

Download completed sample application to control Web browser tabs access - SessionDataControlApp.zip.

In order to test this sample, login as redsam/welcome1 user:

Open application screen:

Open second browser tab (it depends on browser settings, but in most of the cases different tabs are using same session) and access application screen - access will be restricted, by custom login implemented inside sample application:

Application screen from the first tab is working well:

How this is implemented? Well - there is simple POJO class, which stores session Id variable:

ADF Data Control is generated on top of this class:

There is UI template, it contains switcher component, with two facets - noaccess and content. Based on custom logic - page content is rendered inside content facet, if application screen is accessed within the same session from another browser tab - noaccess facet is rendered:

Facet rendering logic is calculated inside managed bean method:

UI template is enabled with Page Definition and can access Data Control methods:

Custom method checks if Session Scope variable was initialized. If no - means its first access. It sets both Session Scope and Data Control variables. If Session Scope variable was already initialized - it checks if Data Control variable is empty. In case of empty Data Control variable - user is accessing application within the same session from another browser tab - access will be restricted.

Saturday, January 21, 2012

How to Access Session Scope in ADF BC

Often we need to access environment variables across all layers of ADF (Model and View-Controller). Usually we store environment variables in session scope, this makes them accessible for the duration of user session. Its common practice to retrieve values from session scope in View-Controller layer. However, is not so common to access session scope from ADF BC. Somehow there is such preconception, that its not possible to access session scope variables from ADF BC. But its possible (no need to add JSF libraries into Model) and I will explain in this short post how to do this.

If we want to store custom variable values in ADF BC, we can use UserData object - adf.userSession.userData. But this approach is not attractive - UserData is reset during passivation/activation and all custom variable values will be lost. Instead, we can store custom variable values in session scope. Download sample application - SecurityFormLogin_v2.zip.

Sample application populates environment variables during Login action (it sets server name and port):

Environment variables are stored in HTTP session, means accessible from session scope.

Now main topic of this post, you can access HTTP session from ADF BC by getting session scope from ADF context:

In this example, we are retrieving server name environment variable from HTTP session scope inside ADF BC prepareSession(Session) overridden method.

Let's test if value is not lost during passivation/activation. To test this, simply disable Application Module Pooling option from Application Module configuration:

Open sample application, login as redsam/welcome1:

Now, because AM pooling is disabled - on each request (button click) passivation/activation happens:

Tuesday, January 17, 2012

What Else Can Go Wrong when Extending WebCenter Spaces

If you are extending WebCenter Spaces with custom ADF Task Flows - make sure to deploy custom ADF Task Flows project library as ADF Library JAR, not just as a simple JAR (more about this - Wrong Deployment Profile to Extend WebCenter Spaces). What else to keep in mind? Well there is one thing, you don't want to get when extending WebCenter Spaces - java.lang.IllegalStateException: Application was not properly initialized at startup, could not find Factory: javax.faces.application.ApplicationFactory. This exception will be thrown, if WebCenter Spaces workspace (one we are using to deploy custom code to WebCenter Spaces) contains invalid web.xml file. I will describe in this post how this configuration file can become invalid and how to fix it.

First, let's refer to Oracle documentation specific to this subject - Using WebCenter Spaces Extension Samples ( Documentation says that web.xml entries should be commented out if we don't want to deploy session timeout related custom code - means web.xml should be empty in the most of the cases:

Use empty web.xml from WebCenter Spaces workspace to extend WebCenter Spaces with custom library:

Once we are familiar with the process of extending WebCenter Spaces, what we would do now? Most likely develop one more ADF library and add it to the project inside WebCenter Spaces workspace to be uploaded to WebCenter server:

Once new library is imported, we can deploy entire package to the WebCenter server:

And here comes exception - could not find Factory: javax.faces.application.ApplicationFactory:

Obviously WebCenter Spaces extending failed, if we try to access WebCenter Spaces - it shows similar exception:

You can follow predefined procedure to rollback custom WebCenter Spaces library, if extending fails. But why it would break?

Double check web.xml:

Its not empty anymore, contains lots of entries:

How come these entries were added? Simply JDeveloper is synchronizing web.xml each time, when you add new ADF Library:

It adds ADF library configuration entries, however these entries are not required when extending WebCenter Spaces and are causing extending failure. Each time after you import new ADF library, make sure web.xml stays clean:

Extended WebCenter Spaces is fixed and works:

Saturday, January 14, 2012

ADF Performance Marathon - 22 Hours Stress Test

My goal was to test how scalable is ADF framework classical stack and if it can run for longer periods of time under constant runtime access. Experiment results - yes, ADF is scalable framework. There are people who complain about ADF performance, please leave your comment if you are not happy with ADF - this post is dedicated to you. But before leaving your comment - please think about house construction process. Even when using good quality tools and materials, still there is no guarantee that materials will be assembled correctly and house construction result will be as expected. What I mean is - ADF application performance depends a lot how you are building your application, if you are following ADF best practices. Would you hire builders to build your house without previous house construction experience? Think same about ADF - would you hire developers without ADF experience to build ADF application? Yes, it happens quite often - people are building ADF applications without ADF experience. However, they tend to forget this fact, because its easier to blame framework at the end.

I was running performance test with standard Oracle ADF sample application - Oracle Fusion Order Demo Application For JDeveloper Performance test was executed with JMeter, download test script - AMTest_Long.jmx.

ADF BC was configured to support 50 concurrent users (Referenced Pool Size = 50), but stress test was executed with 200 users to show ADF scalability with larger number of users.

Performance test details:

- Duration: 22 hours
- Online concurrent users: 200
- Action frequency per user: ~20 requests, break for 1 minute after each 20 requests
- ADF Framework: 11g PS4, ADF BC, ADF Task Flows, ADF Faces
- ADF BC Tuning: Referenced Pool Size = 50, Database Pooling enabled, DB Passivation disabled
- Hardware: 7 GB RAM, 4 Processors
- JVM tuning: Sun JVM defaults

During this stress test each user selects different items and then is browsing shopping cart details:

In order to run long stress test, start JMeter in command line mode (otherwise JMeter will get out of memory exception after hour or so). jmeter -n -t jmeter_script:

Stress test was started around 4 PM, January 13th:

- Active sessions: 200
- Request processing time: 70 ms (0.07 second)
- Requests per minute: ~600
- AM active instances: 50
- AM passivations per minute: ~200 (this allows to support larger number of users, than configured by Referenced Pool Size)

There are 50 Active AM instances, but only 4 DB connections are used as maximum:

In order to minimize DB connections usage, I have enabled DB pooling and set to store passivation data in memory instead of using database PS_TXN table, based on my previous tests - Stress Testing Oracle ADF BC Applications - Do Connection Pooling and TXN Disconnect Level.

Stress test was finished around 2 PM, January 14th:

- Active sessions: 200
- Request processing time: 100 ms (0.1 second, increased because different set of requests applied)
- Requests per minute: ~600
- AM active instances: 50
- AM passivations per minute: ~200

This shows almost no change in ADF application runtime performance, even after 22 hours of continuos runtime access - good news.

There was no change in DB connections usage - it stays low:

There are no warnings in WebLogic status:

FOD application module settings were tuned to support only 50 concurrent users, but it was working well with 200:

DB connection pooling was enabled along with virtual memory for passivation:

JMeter script was configured with 200 online users:

Two main loop controllers were defined, first loop controller triggers 50 loops for ~20 requests with wait time of 1 minute after all requests are executed from current iteration:

Second loop controller runs forever, this allows to execute really long ADF application stress test:

Sunday, January 8, 2012

Master-Detail with One Iterator

I will show slightly different approach how to implement Master-Detail relationship just with one iterator in ADF Bindings. Detail row collection will be fetched directly through View Link Accessor. I guess such approach is especially good, when you need to display Master-Detail data in the same table and want to declare just one iterator in ADF Bindings.

Sample application implements View Link between Locations and Departments, Data Model contains Master-Detail relationship based on View Link:

Drag & drop Locations table into ADF UI, Locations iterator will be defined in ADF Bindings:

Add child Departments collection to the Locations iterator and set DepartmentName attribute to be visible:

We can reference detail rows by pointing to View Link Accessor (by View Link Accessor name - DepartmentsView). Iterator will retrieve all detail rows available and display Department Name for each:

DepartmentsView is View Link Accessor name (pointing to Departments and generated in Locations), you can double check this in View Link definition wizard:

Here how it looks on UI, one Location row comes with collection of Departments rows:

Sample application - MasterDetailInlineTableApp.zip.

Saturday, January 7, 2012

Wrong Deployment Profile to Extend WebCenter Spaces

If you are running WebCenter Spaces portal (PS3/PS4), most likely you are looking how to extend this portal with your custom code and ADF Task Flows. There is one technical document available on OTN for this subject - Using WebCenter Spaces Extension Samples ( Document is well written and explains required technical steps. However, there is one mistake in Adding New Projects to the Sample Workspace chapter:

It says that we should define JAR deployment profile for our custom project:

This is incorrect, it should be ADF Library Deployment profile. JAR deployment profile is not able to package ADF specific artifacts and deployed library will be invalid (unless you are extending with plain Java classes, which is highly unlikely).

So, let's create new project with custom ADF artifacts to extends WebCenter Spaces:

Project contains one ADF Task Flow and JSF Fragment:

Select JAR deployment profile as per documentation:

Deploy and check JAR content - we can find only ADF Task Flow XML and Manifest file, this library is invalid:

Add ADF Library Deployment profile:

Now deployment package is correct and contains all required ADF artifacts:

We can add imported ADF Task Flow into WebCenter Spaces Resource Catalog registry:

Later this ADF Task Flow can be added into WebCenter Composer screens, etc.: