Tuesday, January 29, 2013

Method - createInstanceFromResultSet() for ADF BC Database Fetch Monitoring

ADF application performance tightly coupled with database interaction performance - data fetching from database. It might be useful to monitor and locate VO's with large data fetch, this would help to restrict size of fetched data collections. There is one special method in ADF BC - createInstanceFromResultSet(), you can override it in VO Impl class - method is invoked for each row fetched from database. I will explain how it works, I will be using ADF BC last() operation - this operation is perfect example of possible slow performance in ADF.

Download sample application - ADFScrollingToLastApp.zip. Employees VO Impl class contains overridden method for  createInstanceFromResultSet():

It is enough just to log method invocation - one invocation per on row fetched.

Sample application contains basic custom method to call last() operation - imagine in our use case we need to retrieve last row for certain calculations:

Run ADF BC tester and open Employees VO. 2 rows are fetched initially:

Invoke our custom method calling last() operation - all the rows from the very first one till the last one will be fetched. This is why last() operation is known for poor performance - before returning actual last row, it fetches entire rowset. It may cause serious problems for large data sets, be careful:

So, in this case createInstanceFromResultSet() gave us very accurate info about how many rows were fetched.

Let's run the same application and access ADF UI. Form data is loaded and first row is displayed:

In the background we can see that 25 rows were fetched during initial form access:

This is correct - its how is set RangeSize, equal to 25:

Imagine if by mistake you set RangeSize = -1, it will fetch all rows, this can be critical for large datasets.

Go to the last row - press Scroll To Last button:

All the rows are fetched, before last row is retrieved:

If your ADF application performs slow, override createInstanceFromResultSet() and check how many rows are fetched. May be all rows are fetched during initial access.

Sunday, January 27, 2013

Data Access Optimization in ADF with Oracle Coherence

ADF BC is accessing database each time user is loading new page or accessing new Web session. Once data is retrieved, data is cached usually for the duration of the current session. If there are lots of users accessing the same data  - we may encounter performance bottleneck in querying database each time for new user access. To optimize this, we can use Oracle Coherence - data will be loaded and cached in the middle-tier, it will be served for all users without accessing database each time. Data in Coherence cache can be updated, removed and synchronized back with the database - but this is out of scope for current post. I would like to explain today how to apply Coherence for ADF project in really simple and understandable way - it can be as a startup for your more advanced research and performance tuning.

Here you can download ADF (11g R2) sample application with Coherence cache enabled - ADFCoherenceApp.zip. This sample is quite straightforward - it implements ADF BC for Employees database table:

There are two different UI's implemented in ViewController - one is standard based on ADF BC Data Control, another one is based on POJO Data Control retrieving data from Coherence Cache. In the next post I plan to stress test both task flows with JMeter and to see performance boost by Coherence:

Coherence task flow contains default Method Call activity. Method Call is responsible to load Employees data from ADF BC into Coherence cache. Once data will be loaded to Coherence cache, ADF BC will not be accessed by any subsequent session, data always will be retrieved from Coherence cache:

You can see that from the source code of initialization Method call activity. ADF BC is accessed if only Coherence cache size is equal zero - Employees VO is queried and Coherence cache is populated:

I will describe now how to setup Coherence library for ADF application (I recommend to read this article on OTN - Creating Oracle Coherence Caches in Oracle JDeveloper). Firstly add Coherence Runtime library to your project:

Once library is added - coherence-cache-config.xml file will be created automatically. Config file is empty by default, I have defined basic cache config for EmployeesCache used in this sample app:

In the project Run options, add Java Option to point to coherence-cache-config.xml file (you will need to modify this path in the sample application, according to your environment):

POJO class exposed as Data Control contains a method where we are accessing Coherence cache - EmployeesCache and populating Data Control:

Data collection is loaded on ADF UI as a table:

During first access to Coherence cache - Coherence server is starting, we are reading data from ADF BC and populating it into cache:

This happens in ADF task flow (for Coherence) initialization  Method Call. When fragment is loaded, data already exists in the cache:

Other users who are accessing the same data, will retrieve it directly from Coherence cache. ADF BC Application Module will not be even created and no database access will be established - data will be loaded directly from the cache - this should optimize page load time significantly:

Wednesday, January 23, 2013

Immediately Removing New Row Without Validation in ADF

Once new row is inserted but not yet saved and if validation rules are defined for the attributes - it may cause problems removing this new row (let's say user changes his mind and decides to remove unwanted row). Of course user can invoke Rollback, but this is not what is always desired. User should be able to remove row with one single click - by pressing Delete button. And this is really easy to achieve in ADF, but not always obvious. You need to set Immediate = true as property for Delete button, it will call then Delete operation ignoring any validations for the current row.

Here you can download working sample application - NewRowRemoveApp.zip.

I will describe first how it works by default. User inserts new record and without providing all required data decides to remove this row by pressing Delete button. Submit happens and ADF brings all failed validation messages - this is not what user expects when simply trying to remove row:

Situation will change with Immediate = true property for Delete button on ADF UI - this allows to skip validation invocation and call Delete operation quietly:

Test again with Immediate = true. Insert new record, try to commit or move to another record to force validation messages:

Press Delete button - problematic record will be removed instantly without reporting validation errors on Delete:

Friday, January 18, 2013

How To Implement Gapless Sequence in ADF BC

We all know how to insert new records using ADF BC. But if you have a sequence in the DB for primary key attribute and there is requirement to make sure assigned sequence values are always gapless - few extra steps are needed. When you hit Create button, ADF will assign sequence value immediately - often user may rollback transaction and sequence value will be lost. In order to prevent gaps in the sequence we can use two sequences - one temporary for Create and then real sequence will be applied during commit time from doDML method.

Download sample application - CreateSequenceApp.zip. You can access and run this sample deployed on Oracle Cloud instance - CreateSequenceApp on Oracle Cloud live.

There are two ADF BC framework methods overridden on EO level (you can do the same in generic class) - create() and doDML(). Firstly from create() method we are getting temporary value from the dummy sequence and assigning it:

Then finally when user decides to commit transaction and insert new row, in doDML() method we are substituting temporary key value with real value from the gapless sequence:

Sequence names are retrieved from attribute custom property:

Both sequences are defined in Oracle Cloud DB:

You can test it directly on Oracle Cloud live application instance - press Create button to insert new record. Temporary value 7 is assigned (temporary value can be hidden for new records, I made it visible only for the demo):

Type all mandatory values and commit:

Gapless sequence value 208 will be assigned:

Do a few more tests - insert new record but don't commit and press Undo:

Do the same thing twice, you should see that temporary sequence value is increasing with each time:

Insert new record again and complete transaction, you will see that next gapless value 209 is assigned:

Tuesday, January 15, 2013

Fragment Template to Fragment Communication with ADF Contextual Events

This post is extended version for my previous post - After Commit Call for Centralized Transaction Management. Previously posted application works as it should, but you may face a bit more complex use case when fragment template will be assigned with page definition file. If there will be page definition file for the fragment template - it will not work to call method by managed bean reference and invoke operation from fragment page definition. The thing is - page definition context will be changed from fragment to fragment template and operations declared in the fragment page definition will be unaccessible. However, we can implement communication between fragment template and fragment itself through ADF Contextual Events functionality.

Here is sample application with ADF Contextual Events enabled - GlobalTransactionControlApp_v3.zip. This sample implements generic communication solution based on contextual events. Global commit/rollback actions are handled from the fragment template, after commit we want to invoke refresh for the iterators declared in our fragment page definition. Each fragment in the application, is designed to pass iterator names through page template tag (we are going to use these names in generic method):

Multiple iterator names can be passed with dash separator:

Global method for commit operation in fragment template is reading template parameter value with iterator names and invoking contextual event to notify subscribers to invoke generic after commit method:

Contextual event is defined to send iterator names using payload parameter. I personally prefer to define contextual event code manually in the source code:

Contextual event subscriber method is reading payload value with iterator names to be refreshed and re-executing each of them in the loop:

You must generate data control on top of contextual event subscriber - this is needed to be able to register it in the same page definition file where our iterators are defined:

Once data control will be generated, we can add contextual event subscriber method to the fragment page definition file (same where we have iterators). Provide value payLoad for the method parameter, this is system keyword, it will pass through payload variable value from incoming contextual event:

Contextual event subscriber was defined manually in the source code:

Department - Employee fragment is defined to pass both iterator names to be refreshed after commit:

We can see that from the log - SQL is executed for Departments and Employees:

Employee fragment is passing only one iterator to be refreshed:

We can see that from the log - contextual event is triggering only one iterator refresh in the context of currently opened fragment:

Friday, January 11, 2013

ADF Mobile - Device Native Database Access and Usage

This will be a third update for my sample ADF Mobile application. Read about previous versions from here - ADF Mobile - Geo Location Synchronization. This update is focused primarily on device native database access and operations. I'm using located GPS points as data source and populating on device SQL Lite database with locations retrieved from GPS. Logged data review functionality is implemented as well - user can view altitude graph and load logged path as point layer on top of Google Maps.

Added features:

1. ADF Mobile Springboard support demonstrating multiple ADF Task Flow usage and access

2. ADF Mobile UI validation behavior and conditional logic

3. Device native database access and usage

4. Data visualization with ADF DVT components

5. Multi-point layer display on top of Google Maps

6. iPhone right/left swipe actions

Download sample application code - PosLogApp.zip. This sample contains two ADF Task Flows, both of them are loaded using ADF Mobile Springboard - Tracker (logs GPS positions) and Routes (logs and displays route data):

Same as in the previous version - Tracker logs current GPS position. Once Tracker is activated it will log GPS position change continuously until it will be stopped by the user:

In the same ADF Task Flow - Tracker, we can view current GPS position on the Google Maps:

Second ADF Task Flow - Routes allows to enter new route name and date (current date by default). When Start is activated - it will log each GPS position received from the Tracker into native device database until user will stop route logging:

From this screen user have option to view a list of previously recorded routes - Show button:

List is clickable - user can click on the selected route to view information about it. Information about logged GPS position for the selected route is retrieved from database - in the first screen we can see number of GPS positions logged. 1033 positions were logged for this route (maximum is set to be 10000):

We can view logged altitude information for the route - I was driving uphill and then downhill:

Route path itself can be visualized on top of Google Maps from logged GPS points:

If you want to delete logged route, simply swipe to the right and press Delete button:

You want to know about implementation details? All right - here is ADF Task Flow view for the router part:

Page with route name and date is default, next we can view route list and remove routes with refresh through the method call. We can view details - number of logged GPS points, altitude graph and route path constructed from logged GPS points.

Here is the database structure, two tables simple master/detail - ROUTES and POINTS. This script is executed during initial application activation on the device. Basically it creates *.db file with SQL Lite database on the device:

SQL file must be loaded programmatically, from custom lifecycle listener class. We must define this class in adfmf-application.xml file:

Application lifecycle method - start(), will be executed automatically and it will load database structure initially:

Connection to the database is constructed by retrieving reference to the SQL Lite database file on device:

Here you can see code to insert information about new route - regular SQL syntax and PreparedStatement:

This code snipped shows retrieval of GPS logged positions and constructing array of such points (later displayed in altitude graph and path on top of Google Maps):

In order to be able to load Google Maps in ADF Mobile, you must set Google Maps key in adf-config.xml file. Sample application is provided with dummy key, you must set your own:

Xcode Organizer tool displays installed applications on iPhone, I can see database file for my application. Organizer gives option to download application contents together with database file:

We can extract downloaded archive and access database file:

Performance of device native database is pretty good, I recorded around 10000 GPS location points in total - browsing through this dataset and loading is smooth: