Saturday, February 4, 2012

Many-to-Many Relationship Implementation in ADF BC and ADF Faces UI

Is not always obvious how to implement Many-to-Many use case. I will describe how to represent Many-to-Many data structure in ADF BC and how to display it effectively with ADF Faces. With my next post I will provide another sample application to describe how to create and delete rows for Many-to-Many.

Sample application - ManyToManyADFBC.zip is based on custom DB schema, SQL scripts are included. Database schema is taken from old 2007 post - Many-to-Many mapping in Oracle TopLink:


When you generate ADF BC, it doesn't generate Many-to-Many associations for you by default - you will need to recreate them manually:


Define * to * cardinality for Blogs -> Readers relationship, select intersection key from central mapping table:


Do the same * to * cardinality for Readers -> Blogs relationship, select intersection key from central mapping table:


Define View Link for Blogs -> Readers based on existing Association:


Create View Link for Readers -> Blogs based on existing Association:


Application Module should contain Data Model for Blogs -> Readers and Readers -> Blogs:


While Application Module contains 4 View Object instances - ADF UI implements only 2 tables. One table displays Readers data, another Blogs:


Both tables are pointing to detail View Object instances. Detail data is refreshed from master View Object instance. Current row for master View Object instance is set through choice list selection. Once choice list selection is updated and master current row is changed - ADF UI automatically updates corresponding detail View Object collection:


As you will find in Page Definition, Readers table is populated from detail View Object instance:


Master choice list is mapped with master View Object instance - it controls Readers table:


One more cross selection event is implemented by overriding table selection listeners for both Readers and Blogs tables:


Listener gets currently selected row from the table and passing row key to update current row for master collection on other association end (it will update choice list).

This is how it looks on ADF UI - two tables from Many-to-Many relationship, together with master data choice lists:


User can change Reader choice list selection, Blogs table data will be updated:


When user is changing row selection in the table itself, for example Blogs - this triggers refresh for Blog choice list on the left and Readers table as well:


Same logic with Readers table - row selection triggers refresh for Reader choice list on the right and corresponding Blogs table:


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 (11.1.1.5.0). 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 11.1.1.5. 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: