Saturday, September 27, 2014

ADF LOV component provides filtering option with STARTSWITH operation. This is used to check if similar value exists in the LOV, if such value exists - LOV popup is opened with all the suggested values. If user types 10 and there are 100, 1000 in the LOV, instead of accepting value 10 - LOV popup will be opened and all three values 10, 100, 1000 will be displayed. While this is useful, there is no option to turn off such functionality. My post describes a solution, that can be used to disable suggested LOV filtering.

Here you can see, how it works by default. User types 10, there is 100 in the LOV. LOV popup is opened with both suggested values. This can be annoying for advanced user, who already know the code and they don't need to use LOV popup to select it:

We can check generated SQL statement. Original LOV SQL was updated with LIKE and this was applied for bind variable value. This is how suggested LOV values are retrieved:

LIKE is applied for DepartmentID (LOV key), even we have set it explicitly in the LOV View Criteria to use EQUALS operator:

The solution is to override buildViewCriteriaClauses method in VO Impl class. ADF executes additional View Criteria to retrieve suggested LOV values, this criteria name is "__lov__filterlist_vcr___". We can intercept this View Criteria and replace all STARTSWITH operators with EQUALS:

With this fix applied, user could type 10 and there is no LOV popup opened anymore with suggested values:

Same SQL as before is generated, but LIKE is changed to =, this is the result of our fix:

Tuesday, September 23, 2014

This post applies for multiple ADF regions, based on the same Data Control. I will show you can avoid using ADF Contextual Events to synchronise two ADF regions, when both are based on the same Data Control and this Data Control is shared between the two.

Sample application contains two ADF Task Flows, both are using the same VO instance from shared Data Control:

Two ADF regions are implemented based on these TF's, one implements table component and another a form:

Data from both regions is synchronised automatically. Based on row selection in the table, form data from another region stays in synch:

Backwards synch works as well - change Salary attribute value in the form:

Salary attribute in the corresponding row from the table will be updated:

In order for this to work, make sure to set ChangeEventPolicy=ppr for Employees iterator in the first fragment page definition (this will ensure update when data is changed in the form and we want to see synch in the table):

Set the same property for the iterator in the second fragment page definition (this will ensure form data to be in synch when row selection is changed in the table):

Saturday, September 20, 2014

There is known limitation in ADF 11g, related to accessing application in the same session from multiple browser tabs. While working with multiple browser tabs, eventually user is going to consume all view tokens, he will get timeout error once he returns back to the previous browser tab. Unused browser tab is producing timeout, because ADF 11g is sharing the same cache of view tokens for all browser tabs in the same session. This means the recent mostly used browser tab is going to consume all view tokens, other browser tab would loose the last token and screen state will be reset. This behaviour is greatly improved in ADF 12c with separate view token cache supported per each browser tab. If your application is designed to allow user access through multiple browser tabs in the same session, you should upgrade to ADF 12c for better performance.

I'm going to post results of a test with 11g and 12c. Firstly I'm going to present ADF 11g case and then ADF 12c.

ADF 11g view token usage:

Sample application contains one regular button, with PartialSubmit=false, to generate new view token on every submit:

Max Tokens parameter in web.xml is set to 2, to simulate token usage:

To see the debug output for view tokens usage on runtime, you should set special parameter - org.apache.myfaces.trinidadinternal.DEBUG_TOKEN_CACHE=true

On runtime try to open two browser tabs. You are going to see two view tokens consumed and reported in the log:

Press Test View Token button in the second tab, this would consume another view token. Remember, we have set maximum two tokens, no in the log it says - removing/adding token. This means, we have consumes both available tokens (for both tabs) and token from the first tab is replaced:

Go back to the first browser tab, try to press the same Test View Token button and you are going to get time out error - view token is lost and you need to reload the tab:

ADF 12c view token usage:

Sample application in the same way as in 11g, also implemented simple button set with PartialSubmit=false. This would force to use new view token on each submit:

Max Tokens parameter in web.xml, again is set to 2:

Two browser tabs are opened and two view tokens are consumed:

Press Test View Token in second browser tab, you are not going to see in the log information about removing/adding token (differently to 11g). This means, view token from the first browser tab still remains in the cache, second browser tab maintains its own view token cache:

Go back to the first browser tab, press Test View Token button - application works fine, no time out error as it was in 11g:

Tuesday, September 16, 2014

This may not be as straightforward as it sounds - to define a format for a number attribute in ADF BC. Especially if you are going to have large number (more than 15 digits). Most likely you are going to experience precision/scale and rounding issues, for BigDecimal and Number type attributes with format mask applied. Sounds frustrating? Yes it is. I hope my blog post will help you to implement proper number formatting.

Firstly I'm going to demonstrate, how it works by default. I'm going to use format mask for 18 digits and 2 decimals. Format mask for a number attribute of BigDecimal type would fail with invalid precision/scale error - this is surprising, especially knowing that attribute is defined with (20, 2) precision and scale:

There is one more issue to handle - automatic rounding for large numbers (more than 15 digits). In this example I'm trying to enter a number with 18 digits:

Suddenly number is rounded, immediately after focus is moving out of the field - this is bad, user can't enter correct large number:

Let's see, how we can fix it. I will explain solution, based on Salary attribute with (20, 2) precision and scale. By default, this attribute is generated with BigDecimal type, I'm going to keep the same type, no need to change to oracle.jbo.domain.Number:

I have set format mask on the VO level, for the same Salary attribute. There is another issue - maximum length of the attribute value user can enter on UI. Maximum length property is reading precision and calculates maximum number of characters, however this is wrong when format mask is applied:

There will be extra comma and dot signs coming from the format, so I would advice to set Display Width to be equal to the attribute precision plus maximum number of commas and dots (26 in my case). Later we would need to change maximumLength property on the UI to use Display Width instead of precision:

Once you set a format mask, formatter class name is assigned for the attribute. Formatter class name is saved in resource bundle, for each formatted attribute separately:

You must change formatter class name to the custom one, don't use DefaultNumberFormatter class - out of the box formatter doesn't work with BigDecimals. Sample application comes with custom Number formatter class. Format method is overridden to support BigDecimal, standard formatter method breaks BigDecimal number by converting it to Double (this is the reason for rounding error) - this is fixed in the sample:

Method to parse is changed as well (this is fixing precision/scale number for the BigDecimal). Instead of calling parse method from super, I'm calling custom method:

I'm setting a property to enable BigDecimal parsing, this allows to work correctly with precision/scale:

In addition, precision and scale are checked by custom method - if there is invalid number of digits, user will be informed:

I was mentioning it earlier - maximumLength property on the UI is updated to point to Display Width set on the VO attribute UI Hints:

We can do a test now, type long number with 18 digits (remember (20, 2) precision/scale for Salary attribute):

Correct format will be applied:

Type decimal part and now there will be no error about precision/scale - it will be successfully accepted:

You can see from the log how format is being applied, it works well with custom formatter class provided in the sample application - ADFFormattingApp.zip:

Wednesday, September 10, 2014

ADF BC allows to define triggers to listen for row changes on VO level. We can listen for row updates, inserts and deletes. This can be useful, if you would like to invoke specific audit method or call custom methods to populate dependent transient VO's with updated data.

To enable such triggers, you must add a listener for VO, this can be done during VO creation from standard create method:

ADF BC API methods, such as rowInserted, rowUpdated, rowDeleted can be overridden. These method will be invoked automatically by the framework, when change happens. You can check rowUpdated method, I'm getting changed attribute names (actually it calls this method for each change separately). Getting changed value from current row, also retrieving posted value:

CountryId attribute is set to be refreshed on update/insert, this means change event should be triggered as well, even we would not change this attribute directly:

We should do a test now. Change two attributes - Street Address and State Province, press Save button:

Method rowUpdated is invoked two times, first time for Street Address change (method is invoked before data is posted to DB):

Second time is invoked for State Province change. This means, we can't get all changes in single call, each change is logged separately. It would be much more useful to get all changes in the current row through a single call:

After data is posted, Country ID attribute is updated - this changed is tracked successfully: