Saturday, July 22, 2017

ADF Goes Client Side - UI Performance Boost with JavaScript

If you would like to boost ADF UI performance, you should look into client side validation and formatting options possible to be done in ADF UI. Today I will describe how you can implement client side converter, to format number value on client side, without making request to the server. Same approach could be used to implement client side validators. You can raise error message and it will be assigned to UI field in the same way, just like any standard ADF error message. While this approach is documented long ago in Oracle ADF developer guide - How To Create Client Side Converter, it is not well known and not often used.

Client side converter is attached to ADF UI field through JSF tag, it points to custom converter ID (make sure autoSubmit=false is set, we don't want request to the server on value change):

Custom converter is defined in Faces Configuration file, it points to custom converter class:

Converter class is responsible to load JavaScript file, where number formatting logic is implemented. Also we have an option to pass initialization parameters:

Example of client side converter logic (to format numbers) code in JS:

Formatting happens on the client, no request to the server is done. User enters value and navigates out of the field - value is formatted:

If fractional part is incorrect, error raised from converter is displayed same as any other error in ADF - attached to the field:

If value is invalid - error is displayed too, this simple validation error comes from JS converter. Request is processed on the client, no call to the server:

Server side formatter in ADF BC is still required. When data is fetched from DB, ADF BC server side formatter is applied to transform data to correct format (this happens when data is fetched and doesn't affect end user performance):

Custom number formatter implemented in ADF BC (read more about it: Generic BigDecimal Formatter in ADF

Download sample application -

Sunday, July 16, 2017

ADF BC - Create View Object From Query with Custom Implementation Class

I had a request to explain how to create dynamic ADF BC VO from SQL statement and set custom VO implementation class for newly created VO instance. Custom VO implementation class extends from ADF BC ViewObjectImpl and overrides super method:

There is a method createViewObjectFromQueryStmt, in previous ADF versions this method had two parameters - VO instance name and SQL statement. In current ADF 12c - there is a second signature of the same method, which contains option to specify VO implementation class name. Dynamic VO from SQL with VO implementation class:

ADF BC custom methods can be tested with ADF BC tester:

Overridden method from custom VO implementation class is called:

Download sample application -

Monday, July 10, 2017

ADF 12c BC Proxy User DB Connection and Save Point Error

If you are modernising Oracle Forms system, high chance you need to rely on DB proxy connection. Read more about it in my previous post for ADF 11g - Extending Application Module for ADF BC Proxy User DB Connection. It works in the same way for ADF 12c, but there is issue related to handling DB error, when DB proxy connection is on. DB error is propagated to ADF but is being substituted by save point error (as result - user would not see original error from DB). It seems like related to JDBC driver in 12c. The workaround is to override ADF SQL builder class and disable save point error propagation (there might be better ways to workaround it).

Proxy connection is established from prepareSession method in generic AM Impl class:

If I would change salary value to negative and save data - DB constraint error would fire (negative not allowed). Unfortunately, end user would not see that error - he gets message about failed save point:

Workaround -  we can disable save point error propagation. Override SQL Builder class and add try/catch block in rollbackToSavepoint method. If error happens, do nothing:

You must register SQL Builder class with AM. Add jbo.SQLBuilderClass property in bc4j.xcfg, pointing to the class:

You should be able to see DB errors after this change is applied:

However, there is one drawback of this workaround to keep in mind. When data is posted to DB, ADF executes lock statement. If update fails, normally ADF would execute rollback to save point and lock will be removed. But not in the case of DB proxy, now rollback to save point is failing - this means lock will stay:

If user would fix data and try to save again - lock error will be returned:

Error during lock:

To bypass lock issue, you should enable DB pooling for AM instance. In this case, after each request DB connection will be returned back to the pool and lock will be released automatically:

Download sample application -

Thursday, July 6, 2017

Working with Location and Permissions in JET Hybrid

What if you want to access mobile device location data from JET Hybrid application? This can be achieved with Cordova Geolocation plugin. But you want it to be nicely done and want to make sure application is granted with permission to access location information. Use Cordova Permissions plugin for that.

You could add Cordova plugin to JET app by executing this command:

cordova plugin add 

If this command doesnt work for any reason, you could add plugin information directly into config.xml file (check Geertjan post about the same - Plugging into Devices with Oracle JET on Cordova (Part 1)):

In JS function, before calling location API - we call permissions API to check if app is already granted permission to read location data. In hasPermission method, in case of success - location data is accessed. In case of no permission, request for permission is sent. If request is satisfied - location is accessed (and permission is granted at the same time):

Location data is retrieved through callback:

This is how it works. On very first location access, when permission is not granted yet - we request permission through permission API:

When permission is granted, location is displayed:

Download sample application from GitHub repository - rslocationapp.

Tuesday, July 4, 2017

JDeveloper Patch for Transient Expression Compilation Infinite Loop

If you are using JDeveloper or, probably you run into transient expression compilation infinite loop issue. Infinite loop happens when you open ADF BC project and navigate to VO, which contains Groovy expressions. JDeveloper starts to print repeating message in the log - compiling TransientExpression and soon at some point JDeveloper window closing down without any feedback:

For those of you, who are not aware - there is a patch for this issue. Patch can be downloaded from Oracle Support, search for Patch: 25218838 (there is one for JDEV and another one for JDEV

If its your first time applying Oracle patch - no worries, process is very simple and smooth. Extract download patch zip archive first. Next setup ORACLE_HOME environment variable, point to root folder of JDEV install:

Once environment variable is set, run OPatch by executing opatch apply from the directory where patch archive was extracted. You can reference OPatch by direct path:

Hopefully fix provided by this patch will be included into next JDEV version by default.

Saturday, July 1, 2017

Oracle JET Router State Control

In my use case there is requirement to navigate to JET module, without displaying it in menu structure. Navigation is a simple task, more complex is to make sure current menu item will be unselected after navigation to invisible module (so that later we are able to re-open it).

There is accounts module in JET sample app (download it from GitHub - JETModularArchitecture):

Accounts module is included into router, but not included into array of visible menu items:

On button click (dashboard module) we call function, where router navigation happens to accounts module:

Let's see how this functionality works. I click on Accounts button to call a function and force router navigation:

Accounts module will be opened (through router navigation), but currently highlighted menu item will stay as it was (Dashboard). This makes it impossible to navigate back to Dashboard (unless we navigate to another module and then navigate to Dashboard):

Solution for this problem is to change selection property of ojNavigationList component in HTML:

Instead of pointing to router stateId directly, we should point to computed function. This will help to control currently selected menu item. But we still need to update router state, when menu item is selected. For that reason I'm using beforeSelect property, which points to function, where router state is changed:

Selection property value is calculated by function, which returns NULL, if accounts is currently selected module. This allows to remove focus from Dashboard menu item, after navigation to accounts. Once focus is removed, we can click back on Dashboard and navigate. For all other modules - current router state ID is returned. Select handler call router API to navigate to the module:

Navigation to accounts module from dashboard. Dashboard menu item doesn't remain focused anymore:

Navigation back to dashboard work too:

Tuesday, June 27, 2017

Oracle JET Modular Architecture Example

One of my favourite parts in Oracle JET - modular code structuring support. This allows to split application functionality into modules and reusable functions. In this post I will show how you could leverage Oracle JET modular architecture not only by implementing common code functions, but also by managing data maintained in common modules.

Let's jump to the example (download or browse through sample code on GitHub repository - JETModularArchitecture). Sample application is based on JET template. I have created two common modules - dashboardChartHelper and dashboardTableHelper. These modules define data structure to be displayed in the dashboard and provide API to manage this data from consuming module:

Both helper modules are imported into consuming module - dashboard:

Dashboard module defines variables (chart and table data), which are initialized from variables assigned with data structures in helper modules:

There are two wrapper functions, calling API functions from helper modules. API provides data manipulation logic, which changes chart data structure. Wrapper functions are invoked from dashboard UI:

Here is the implementation of API functions - data array changes:

What is great about such structuring - data and data changes logic can be encapsulated in common helper module. As soon as we have observable variable defined in consuming module (dashboard), which points to the method helper method (where data is changed) - changes are automatically visible on UI.

Here is the data coming from helper modules rendered in dashboard module:

Press on Add Group E button, this will call helper module API function to update data array cached in that module. In turn observable variable in dashboard module will be updated and data displayed on UI will be refreshed:

Press on Remove Group E button - chart data will be changed again:

Sunday, June 25, 2017

ADF BC Attribute - Collection Storage Mode Property

I would like to describe one interesting property for ADF BC attribute. This property is called Storage. There are two possible values: row (default) and collection. By default attribute value is saved in row storage, but alternatively it can be saved in collection storage. ADF BC implements collection storage using map which comes from session scope. This allows to keep value even between ADF BC requests, this is ideal for transient attributes.

Sample application ( implements VO transient attribute to keep checkbox value:

VO is configured for Range Paging support. While user will navigate through UI table pages - VO range paging will re-execute and this will force VO replace rows (which will result in loosing transient attribute values):

This is how it will look like. User will select checkbox and then navigate to another table page:

After navigating back - checkbox value will be lost (range paging mode will re-execute VO rowset to bring rows belonging to current page):

To force transient attribute value to stay, go to Properties window for the attribute and scroll down to the last section. Select attribute called Storage:

Change value to collection. This will force ADF BC to store value for this attribute in session map:

Transient attribute value will stay, even when VO is re-executed in range paging mode and VO rowset is refetched: