Andrejus Baranovski
We are Hiring - Red Samurai ADF and JET Team
Do you want to join Red Samurai force and become one of our technical experts? The moment is now - we are looking for additional team player.
You should be strong in ADF, highly preferable to be confident with JavaScript and Oracle JET. It never hurts to be fluent in PL/SQL and understand Oracle Forms. You will work in Red Samurai projects focused on ADF and JET.
What would be your position? You name it - you will get a chance to wear multiple hats - expert ADF or JET developer, performance tuning specialist or the guy who solves unsolvable.
Don't wait - drop me email: abaranovskis at redsamuraiconsulting dot com
You should be strong in ADF, highly preferable to be confident with JavaScript and Oracle JET. It never hurts to be fluent in PL/SQL and understand Oracle Forms. You will work in Red Samurai projects focused on ADF and JET.
What would be your position? You name it - you will get a chance to wear multiple hats - expert ADF or JET developer, performance tuning specialist or the guy who solves unsolvable.
Don't wait - drop me email: abaranovskis at redsamuraiconsulting dot com
ADF Hidden Gem - Export Collection Listener
How many times you complained about ADF export collection listener generated output? There are two options for the output - CSV and excelHTML. Both of them are not really Excel friendly (Excel complains, each time when such file is opened) and produced output lacks formatting. Luckily there is a way to specify custom formatter for ADF export collection listener and set your own output type. In this way we can produce better and customized output file for Excel, you can construct Excel document with different formatting and layout.
Demo application with custom formatter for ADF export collection listener is available on GitHub repository - rs-export-xls. This was implemented by Red Samurai Consulting colleague - Fedor Zymarev. You are free to check-out the source code - use it in your projects and add new formatting features. It would be great, if you could contribute to the community and commit any improvements - simply request merge approval into master on GitHub repository.
Demo runs with the entry page, which contains ADF UI table with Export all/selected rows options:
This is how Excel output looks like. There is a placeholder for worksheet title, header is highlighted and format is recognized by Excel:
You only need to set custom type (RSExcelExport) for ADF export collection listener, instead selecting excelHTML available by default:
Custom type is not available in the list of options, you should type it:
Format type is defined with Java class registered in oracle.adf.view.rich.export.FormatHandler text file, located in ADF META-INF/services folder:
Formatting implementation logic take place in RSExcelFormatHandle class. We are using Apache POI, Java API for Microsoft documents, to prepare native format for Excel:
Demo application with custom formatter for ADF export collection listener is available on GitHub repository - rs-export-xls. This was implemented by Red Samurai Consulting colleague - Fedor Zymarev. You are free to check-out the source code - use it in your projects and add new formatting features. It would be great, if you could contribute to the community and commit any improvements - simply request merge approval into master on GitHub repository.
Demo runs with the entry page, which contains ADF UI table with Export all/selected rows options:
This is how Excel output looks like. There is a placeholder for worksheet title, header is highlighted and format is recognized by Excel:
You only need to set custom type (RSExcelExport) for ADF export collection listener, instead selecting excelHTML available by default:
Custom type is not available in the list of options, you should type it:
Format type is defined with Java class registered in oracle.adf.view.rich.export.FormatHandler text file, located in ADF META-INF/services folder:
Formatting implementation logic take place in RSExcelFormatHandle class. We are using Apache POI, Java API for Microsoft documents, to prepare native format for Excel:
Oracle JET Input Search with ADF BC REST
LOV is popular component in ADF, it allows to seach for data entry in the list, select it and assign to the attribute. I was researching, how similar concept can be implemented in Oracle JET, based on data from ADF BC REST service. JET Input Search component seems to be useful for LOV like behavior implementation.
Job ID field is implemented with Input Search. It is based on value/label pair, user enters label and in the background selected value is returned and assigned to the attribute:
Watch this recording, to see how was it is. Search is performed on client side and value selection is instant:
List is being filter when user types value (you can configure it, to start filtering after user enters certain number of charachters):
Try to select a value from the list and update record:
In the background it is using key value SA_REP for update, we can track it in ADF BC log, where actual DB update takes place (through REST PATCH):
Let's take a look into implementation. In HTML I'm using ojInputSearch component with value and options properties. Property options provides list entries and property value holds selected value key:
Options are defined in JavaScript as observableArray, this allows to synch collection data to the UI. There is collection for options and REST service URL (pointing to Jobs ADF BC REST resource):
Data structure is defined by parseJob function, it contains JobId and JobTitle attributes - this will help to map REST response into JET collection:
JET collection is configured with REST URL for Jobs, unlimited fetch size (to fetch list of all jobs) and data structure mapping for REST resource:
Main part - we need to populate JET collection with data, this can be done by executing fetch method (see JET API documentation). In the success callback (executed asynchronously), we can access returned collection and push all entries into observableArray variable, attached to Input Search UI component:
Make sure to set RangeSize = -1 in ADF BC REST service resource definition for Jobs. This will enforce ADF BC to return all rows:
Download sample application (archive contains ADF BC REST sample and JET implementation with NetBeans, you must add JET runtime distribution to run JET sample) - JETCRUDApp_v7.zip.
Job ID field is implemented with Input Search. It is based on value/label pair, user enters label and in the background selected value is returned and assigned to the attribute:
Watch this recording, to see how was it is. Search is performed on client side and value selection is instant:
List is being filter when user types value (you can configure it, to start filtering after user enters certain number of charachters):
Try to select a value from the list and update record:
In the background it is using key value SA_REP for update, we can track it in ADF BC log, where actual DB update takes place (through REST PATCH):
Let's take a look into implementation. In HTML I'm using ojInputSearch component with value and options properties. Property options provides list entries and property value holds selected value key:
Options are defined in JavaScript as observableArray, this allows to synch collection data to the UI. There is collection for options and REST service URL (pointing to Jobs ADF BC REST resource):
Data structure is defined by parseJob function, it contains JobId and JobTitle attributes - this will help to map REST response into JET collection:
JET collection is configured with REST URL for Jobs, unlimited fetch size (to fetch list of all jobs) and data structure mapping for REST resource:
Main part - we need to populate JET collection with data, this can be done by executing fetch method (see JET API documentation). In the success callback (executed asynchronously), we can access returned collection and push all entries into observableArray variable, attached to Input Search UI component:
Make sure to set RangeSize = -1 in ADF BC REST service resource definition for Jobs. This will enforce ADF BC to return all rows:
Download sample application (archive contains ADF BC REST sample and JET implementation with NetBeans, you must add JET runtime distribution to run JET sample) - JETCRUDApp_v7.zip.
Oracle JET 2.0.1 - Upgrade for CRUD Sample
Oracle JET 2.0.1 was released in April and I decided to upgrade my CRUD sample (based on ADF BC REST services) implemented with previous JET version. There is migration guide, describing main points to consider, while moving to the next JET version. I'm going to add few more points. Hopefully you will find it useful.
Here is CRUD sample UI running with Oracle JET 2.0.1 and interacting with ADF BC REST:
This sample comes with advanced validation rule integration, which is being enforced in ADF BC and propagated to JET UI (read my previous posts about this):
As per migration guide, you must update reference for Alta CSS (2.0.1):
Next update module names in main.js:
CRUD app is rendering paged table out of collection table datasource. Somehow it worked previously, without specifying ojs/ojcollectiontabledatasource module, now it must be specified, otherwise there is error about constructor:
There are changes on UI side. I have wrapped table/form into div with oj-flex-items-pad (includes padding). Table and form and places into separate div's, with oj-flex-item class:
To enable table pagination, I moved it out of div, in previous version it worked inside separate div:
Form group elements are wrapped into oj-flex/oj-flex-item class, this creates better layout for form items:
Update method was changed to handle ADF BC validation error in slightly different way. I'm showing error first and next resetting collection data - to return original value into the table.
Download sample application - JETCRUDApp_v6.zip.
Here is CRUD sample UI running with Oracle JET 2.0.1 and interacting with ADF BC REST:
This sample comes with advanced validation rule integration, which is being enforced in ADF BC and propagated to JET UI (read my previous posts about this):
As per migration guide, you must update reference for Alta CSS (2.0.1):
Next update module names in main.js:
CRUD app is rendering paged table out of collection table datasource. Somehow it worked previously, without specifying ojs/ojcollectiontabledatasource module, now it must be specified, otherwise there is error about constructor:
There are changes on UI side. I have wrapped table/form into div with oj-flex-items-pad (includes padding). Table and form and places into separate div's, with oj-flex-item class:
To enable table pagination, I moved it out of div, in previous version it worked inside separate div:
Form group elements are wrapped into oj-flex/oj-flex-item class, this creates better layout for form items:
Update method was changed to handle ADF BC validation error in slightly different way. I'm showing error first and next resetting collection data - to return original value into the table.
Download sample application - JETCRUDApp_v6.zip.
Red Samurai ADF Performance Audit Tool Major Update v 5.0
Our ADF performance audit tool is growing and getting more advanced with each release. Current major update v 5.0 brings ADF Click History integration and allows to track ADF UI client request time. This allows to understand, how long it takes to execute action from user perspective. Combined with ADF BC performance monitoring, we could give precise answer about performance bottlenecks from top to bottom.
New features in v 5.0:
1. Redesigned UI with ADF 12.2.1 Responsive template. UI is aligned with Alta UI best practices
2. Group by ECID functionality. We can track request action from top to bottom. This includes client request time (time needed to complete action on UI, including network traffic time). Executed SQL queries and any issues in ADF BC performance for the given request
3. ADF Click history logging. Click history data about UI performance is being intercepted and logged into Red Samurai DB, for analysis. This gives a database of all user requests and time for each request
4. User request statistics visualization dashboard
Here you can see sample data from ADF demo application. User request statistics are presented to help in understanding system performance. Time for each user request is visualized, along with detail information about request (component ID, name, type, etc.). Average times are presented, along with user statistics and top actions. We display total requests count vs. recent requests, to visualize the load on the system:
Each of the logged requests can be tracked down by ECID. Here we can see a list of VO's invoked during UI request and any slow performance behavior happening in ADF BC:
In the next updated v 6.0, we are going to implement UI behavior analysis, to extract most common UI usage patterns in ADF application.
New features in v 5.0:
1. Redesigned UI with ADF 12.2.1 Responsive template. UI is aligned with Alta UI best practices
2. Group by ECID functionality. We can track request action from top to bottom. This includes client request time (time needed to complete action on UI, including network traffic time). Executed SQL queries and any issues in ADF BC performance for the given request
3. ADF Click history logging. Click history data about UI performance is being intercepted and logged into Red Samurai DB, for analysis. This gives a database of all user requests and time for each request
4. User request statistics visualization dashboard
Here you can see sample data from ADF demo application. User request statistics are presented to help in understanding system performance. Time for each user request is visualized, along with detail information about request (component ID, name, type, etc.). Average times are presented, along with user statistics and top actions. We display total requests count vs. recent requests, to visualize the load on the system:
Each of the logged requests can be tracked down by ECID. Here we can see a list of VO's invoked during UI request and any slow performance behavior happening in ADF BC:
In the next updated v 6.0, we are going to implement UI behavior analysis, to extract most common UI usage patterns in ADF application.
Optimize ADF HTTP Response Size with ChangeEventPolicy
You should read this post, if you are looking how to reduce ADF HTTP response size. This can be important for ADF application performance tuning, to improve PPR request response time. By default in ADF 12.2.1, iterator is assigned with ChangeEventPolicy = ppr. This works great for UI component bindings refresh, no need to set individual partial triggers. On other side, this generates extra content in ADF HTTP response and eventually increases response size. I would recommend to use ChangeEventPolicy = ppr, only when its really needed - dynamic forms, with unknown refresh dependencies. Otherwise set ChangeEventPolicy = none, to generate smaller response.
I will demonstrate below the difference for ADF HTTP response with ChangeEventPolicy=ppr/none. First let's take a look into page load response size:
Page contains list component and form. Both are based on two different iterators, set with ChangeEventPolicy = ppr. This generates AdfPage.PAGE.updateAutoPPRComponents calls for each UI item, referencing attributes from the iterator. In complex screens, this adds significant amount of extra text to the response, could increase size even by half:
Partial response also contains same calls added to the response. Select list item:
Each item from the form will be referenced by JavaScript call, to register it for refresh:
Let's change it to ChangeEventPolicy = none. Set it for both iterators:
We should set refresh dependencies manually. Form block must be set with PartialTrigger, referencing list component - to refresh, when list selection changes:
Next/Previous buttons dependency also must be set, to change form row:
With manual refresh dependency changes, there are no extra calls added to ADF HTTP response, reducing overall response size:
Same applies for PPR response:
Download sample application - ChangeEventPolicyPPRApp.zip.
I will demonstrate below the difference for ADF HTTP response with ChangeEventPolicy=ppr/none. First let's take a look into page load response size:
Page contains list component and form. Both are based on two different iterators, set with ChangeEventPolicy = ppr. This generates AdfPage.PAGE.updateAutoPPRComponents calls for each UI item, referencing attributes from the iterator. In complex screens, this adds significant amount of extra text to the response, could increase size even by half:
Partial response also contains same calls added to the response. Select list item:
Each item from the form will be referenced by JavaScript call, to register it for refresh:
Let's change it to ChangeEventPolicy = none. Set it for both iterators:
We should set refresh dependencies manually. Form block must be set with PartialTrigger, referencing list component - to refresh, when list selection changes:
Next/Previous buttons dependency also must be set, to change form row:
With manual refresh dependency changes, there are no extra calls added to ADF HTTP response, reducing overall response size:
Same applies for PPR response:
Download sample application - ChangeEventPolicyPPRApp.zip.
ADF 12c Custom Property Groovy and AllowUntrustedScriptAccess Annotation
To execute Groovy expression in ADF 12c (to call Java method from Groovy), you must specify trusted mode. Read more about it in my previous post - ADF BC 12c New Feature - Entity-Level Triggers. Setting mode to trusted, works in most of the cases. It doesn't work if we want to execute Groovy expression (calling Java method in ViewRow or Entity class) for custom property. In a case of custom property and Groovy calling custom method, we need to annotate Java class with AllowUntrustedScriptAccess. This makes a trick and Groovy expression can call custom method.
To demonstrate the use case, I was using mandatory property. This is standard property to control if attribute is required or no. By default, mandatory property is static, but we can make it dynamic with Groovy expression. I have implemented a rule, where Salary attribute is required, if value is more than 5000:
Salary is not required, if value is less than 5000. This is just example, you can implement more complex logic:
There is a method in ViewRow class, to calculate required property for Salary attribute:
Method is callable from custom property Groovy expression, as we have set annotation AllowUntrustedScriptAccess for ViewRow class. Annotation definition, must list all allowed methods:
Here you can see Groovy expression, to call ViewRow class method - mandatory. Expression is assigned for custom property, this will be referenced from ADF UI:
There is issue with JDEV 12c, it fails to parse custom properties set with Groovy expressions. It fails to parse and removes custom property from the code. To prevent such behavior, specify empty string for custom property value (this will allow to keep Groovy expression):
Luckily in this case of mandatory check, JDEV by default generates mandatory property for ADF UI component. We simply override its original value with custom property and calculate new value in custom trusted method, referenced by Groovy expression:
Download sample application - ADF12cGroovyCustomPropertyApp.zip.
To demonstrate the use case, I was using mandatory property. This is standard property to control if attribute is required or no. By default, mandatory property is static, but we can make it dynamic with Groovy expression. I have implemented a rule, where Salary attribute is required, if value is more than 5000:
Salary is not required, if value is less than 5000. This is just example, you can implement more complex logic:
There is a method in ViewRow class, to calculate required property for Salary attribute:
Method is callable from custom property Groovy expression, as we have set annotation AllowUntrustedScriptAccess for ViewRow class. Annotation definition, must list all allowed methods:
Here you can see Groovy expression, to call ViewRow class method - mandatory. Expression is assigned for custom property, this will be referenced from ADF UI:
There is issue with JDEV 12c, it fails to parse custom properties set with Groovy expressions. It fails to parse and removes custom property from the code. To prevent such behavior, specify empty string for custom property value (this will allow to keep Groovy expression):
Luckily in this case of mandatory check, JDEV by default generates mandatory property for ADF UI component. We simply override its original value with custom property and calculate new value in custom trusted method, referenced by Groovy expression:
Download sample application - ADF12cGroovyCustomPropertyApp.zip.
Monitoring ADF 12c Client Request Time with Click History
You must be excited to read this post, I will describe one very useful feature, available in ADF 12c. This feature - Click History. You can follow steps described by Duncan Mills, to enable and read click history data in server log. There is one more option available - we can read click history data from ADF request (captured by filter class) and log it in custom way (for later analysis). Click history gives such information as client request start/end time (you can calculate client request duration), component client ID, component type, action event type, etc. All this is useful to understand application performance perceived by the client.
Download sample application - DashboardApp.zip (it contains JET libraries, to render JET content within ADF). To test click history and view generated statistics, click on any button and selection component. Click history returns previous action statistics. Let's click on Cancel button:
You should see similar XML message printed in the log (click history statistics data):
To view it in readable way, you can copy it into XML file and format in JDEV:
Logged properties, useful to track client request performance:
1. CID - ECID number, identifies request
2. CST - client request start time
3. CET - client request end time
4. ETY - client event type
5. CLD - client component ID
6. CTY - client component type
XML message can be parsed and property values can be logged to DB for performance history analysis.
To enable click history, you only need to set parameter (true) in web.xml oracle.adf.view.faces.context.ENABLE_ADF_EXECUTION_CONTEXT_PROVIDER:
To read click history data from ADF request, we need to define custom filter in web.xml. Register filter under Faces Servlet:
In the filter, override doFilter and retrieve oracle.adf.view.rich.monitoring.UserActivityInfo parameter (this will return click history XML string, as above):
Download sample application - DashboardApp.zip (it contains JET libraries, to render JET content within ADF). To test click history and view generated statistics, click on any button and selection component. Click history returns previous action statistics. Let's click on Cancel button:
You should see similar XML message printed in the log (click history statistics data):
To view it in readable way, you can copy it into XML file and format in JDEV:
Logged properties, useful to track client request performance:
1. CID - ECID number, identifies request
2. CST - client request start time
3. CET - client request end time
4. ETY - client event type
5. CLD - client component ID
6. CTY - client component type
XML message can be parsed and property values can be logged to DB for performance history analysis.
To enable click history, you only need to set parameter (true) in web.xml oracle.adf.view.faces.context.ENABLE_ADF_EXECUTION_CONTEXT_PROVIDER:
To read click history data from ADF request, we need to define custom filter in web.xml. Register filter under Faces Servlet:
In the filter, override doFilter and retrieve oracle.adf.view.rich.monitoring.UserActivityInfo parameter (this will return click history XML string, as above):
ADF BC View Criteria Query Execution Mode = Both
View Criteria is set to execute in Database mode by default. There is option to change execution mode to Both. This would execute query and fetch results from database and from memory. Such query execution is useful, when we want to include newly created (but not commited yet) row into View Criteria result. Newly created row will be included into View Criteria resultset.
Download sample application - ViewCriteriaModeApp.zip. JobsView in sample application is set with query execution mode for View Criteria to Both:
I'm using JobsView in EmployeesView through View Accessor. If data from another VO is required, you can fetch it through View Accessor. View Accessor is configured with View Criteria, this means it will be automatically filtered (we only need to set Bind Variable value):
Employees VO contains custom method, where View Accessor is referenced. I'm creating new row and executing query with bind variable (primary key for newly created row). View Criteria is set to execution mode Both, this allows to retrieve newly created row (not commited yet) after search:
View Criteria execution mode Both is useful, when we want to search without loosing newly created rows.
Download sample application - ViewCriteriaModeApp.zip. JobsView in sample application is set with query execution mode for View Criteria to Both:
I'm using JobsView in EmployeesView through View Accessor. If data from another VO is required, you can fetch it through View Accessor. View Accessor is configured with View Criteria, this means it will be automatically filtered (we only need to set Bind Variable value):
Employees VO contains custom method, where View Accessor is referenced. I'm creating new row and executing query with bind variable (primary key for newly created row). View Criteria is set to execution mode Both, this allows to retrieve newly created row (not commited yet) after search:
View Criteria execution mode Both is useful, when we want to search without loosing newly created rows.
Change ADF BC Data Update Locking with FOR UPDATE WAIT
Each time when data is changed and updated through ADF BC, before posting changes to DB, SQL query with FOR UPDATE NOWAIT is generated and executed. In case if other process locks row to be updated, or another user in the same moment is updating it, error will be generated and update will be stopped. There might be use cases, when you would like to wait for certain period of time, until row will be unlocked and then commit row changes. This is especially true, if 3rd party (PL/SQL) process is updating rows and you have defined change indicator attribute in ADF BC (see my previous post - ADF BC Version Number and Change Indicator to Track Changed Rows).
We can change default behavior, instead of requesting for immediate lock - we can wait a period of time. If lock becomes available during this period, session acquires lock. If row remains locked, error is returned. Instead of default FOR UDPATE NOWAIT, we can generate FOR UDPATE WAIT (time period in seconds).
To override default behavior, we need to specify custom SQLBuilder class. This can be registered in Application Module configuration jbo.SQLBuilder property:
Class must extend from OracleSQLBuilderImpl and override getSqlVariantLockTrailer() method. My sample application is implemented to return FOR UPDATE WAIT 30:
We can do a test. We can simulate PL/SQL lock by executing SQL query with FOR UPDATE from SQL Developer:
Try to update same row from ADF BC with default behavior, error will be returned - "Failed to lock the record, another user holds the lock". This is expected, because row remains locked:
With overriden SQL Builder, FOR UPDATE WAIT 30 is generated. It waits 30 seconds, as soon as lock is removed - lock from current session is set and row data is updated:
Download sample application - LockUpdateApp.zip.
We can change default behavior, instead of requesting for immediate lock - we can wait a period of time. If lock becomes available during this period, session acquires lock. If row remains locked, error is returned. Instead of default FOR UDPATE NOWAIT, we can generate FOR UDPATE WAIT (time period in seconds).
To override default behavior, we need to specify custom SQLBuilder class. This can be registered in Application Module configuration jbo.SQLBuilder property:
Class must extend from OracleSQLBuilderImpl and override getSqlVariantLockTrailer() method. My sample application is implemented to return FOR UPDATE WAIT 30:
We can do a test. We can simulate PL/SQL lock by executing SQL query with FOR UPDATE from SQL Developer:
Try to update same row from ADF BC with default behavior, error will be returned - "Failed to lock the record, another user holds the lock". This is expected, because row remains locked:
With overriden SQL Builder, FOR UPDATE WAIT 30 is generated. It waits 30 seconds, as soon as lock is removed - lock from current session is set and row data is updated:
Download sample application - LockUpdateApp.zip.
ADF BC Version Number and Change Indicator to Track Changed Rows
One of the common use cases in enteprise applications is to track concurrent user changes. There are two types of changes possible - when two real users are changing data in the same row, or when single user is changing data and same row is updated by PL/SQL procedure/function (all happen in the same user session). In the first case, we would like to inform a user - row data was changed (two different users changing data). In the second case, there is no need to inform user (data wasn't changed by another real user, it was changed by PL/SQL function/procedure invoked in the same session).
ADF BC by default tracks all attributes and checks if they values were changed. This would mean, if any attribute value is changed directly in DB, EO cache will be out of synch and changed row error will be reported. We can minimize number of attributes to be checked to single attribute - Version Number. There must be additional number type column created in DB table, we are going to use it for Version Number in the EO. EO attribute should be marked as Change Indicator and set to be "version number" in Track Change History property. This means, row will be considered as changed by another user, only if Version Number attribute value will be changed. All changes happening directly from PL/SQL will not increment Version Number (it will be incremented only for the changes submitted from ADF BC):
There should be new column created in the DB for Version Number attribute:
Let's see how it works. I will use two different browser sessions, to simulate two users. Change value for First Name, take a look into Version Number. Value is equal to 4, before Save:
Data is saved successfully to DB. New value 5 is assigned automatically for Version Number:
Switch to different session now. Version Number is equal to previous value 4 (this session was opened before recent commit from first session). Both sessions are working with the same row. Change value for Salary:
Try to save this change. You will get standard error message, informing user about changes in the same row. This happens, because Version Number value doesn't match with recent Version Number for the same record:
At the same time, Version Number is synchronized automatically (updated to 5) and user can try to commit his changes again. This time data is saved and Version Number is increased to 6:
We should check now, how it works data is changed in the DB directly, without increasing Version Number (the case of update happening from PL/SQL). I'm changing Employee_Id = 100 record directly in DB (changing Salary value). Version Number is not changed:
Go back to Web session and change First Name value, press Save:
Despite data was changed in DB, save was successful - Version Number was recent. First Name was successfully changed and Version Number was increased to 7:
Download sample application (make sure to create manually VERSION_NO(10, 0) column in the EMPLOYEES DB table) - ADFChangedRowApp.zip.
ADF BC by default tracks all attributes and checks if they values were changed. This would mean, if any attribute value is changed directly in DB, EO cache will be out of synch and changed row error will be reported. We can minimize number of attributes to be checked to single attribute - Version Number. There must be additional number type column created in DB table, we are going to use it for Version Number in the EO. EO attribute should be marked as Change Indicator and set to be "version number" in Track Change History property. This means, row will be considered as changed by another user, only if Version Number attribute value will be changed. All changes happening directly from PL/SQL will not increment Version Number (it will be incremented only for the changes submitted from ADF BC):
There should be new column created in the DB for Version Number attribute:
Let's see how it works. I will use two different browser sessions, to simulate two users. Change value for First Name, take a look into Version Number. Value is equal to 4, before Save:
Data is saved successfully to DB. New value 5 is assigned automatically for Version Number:
Switch to different session now. Version Number is equal to previous value 4 (this session was opened before recent commit from first session). Both sessions are working with the same row. Change value for Salary:
Try to save this change. You will get standard error message, informing user about changes in the same row. This happens, because Version Number value doesn't match with recent Version Number for the same record:
At the same time, Version Number is synchronized automatically (updated to 5) and user can try to commit his changes again. This time data is saved and Version Number is increased to 6:
We should check now, how it works data is changed in the DB directly, without increasing Version Number (the case of update happening from PL/SQL). I'm changing Employee_Id = 100 record directly in DB (changing Salary value). Version Number is not changed:
Go back to Web session and change First Name value, press Save:
Despite data was changed in DB, save was successful - Version Number was recent. First Name was successfully changed and Version Number was increased to 7:
Download sample application (make sure to create manually VERSION_NO(10, 0) column in the EMPLOYEES DB table) - ADFChangedRowApp.zip.
How To Be More Productive with JDeveloper 12c Application Redeployment
When you work on larger JDeveloper/ADF 12c project, most likely you will experience issues with application redeployment (rerun). These two issues will be encountered:
1. It doesnt work anymore (it worked on 11g) to simply rerun application (applies for 12.1.3 and 12.2.1), without stopping it and then running again. In most of the cases if you try to rerun application, some strange errors are returned or changes are not visible at all. It takes time to stop application and run it from scratch
2. Oracle recommended approach is to use Make All to recompile the code and then refresh or reopen browser (Servlet Reload or Fast Swap options). This should bring the changes in ADF BC, Bindings, Task Flows and UI. While this works on simple use cases, often it doesnt work if many files are changed at once
What if you want to re-run application in JDeveloper 12c, same as it was possible in 11g? Yes, you can do it. There is a simple configuration change in JDeveloper 12c, to be able to rerun application without issues:
Go to JDeveloper preferences wizard and select Run option. Uncheck option "Enable Application Rerun Optimizations" - this will allow to rerun ADF application without errors and without a need to stop and start it manually:
Once application rerun optimizations are disabled in JDeveloper, you should add simple configuration to your ADF application (to make sure application redeploys fast). Disable auto generation for JDBC descriptors in application profile (Deployment -> WebLogic):
Provide external Data Source in AM configuration, or set direct JDBC URL (suitable for testing locally, as in my case):
Do above steps, when Make All (Servlet Reload or Fast Swap) doesn't work for you:
1. It doesnt work anymore (it worked on 11g) to simply rerun application (applies for 12.1.3 and 12.2.1), without stopping it and then running again. In most of the cases if you try to rerun application, some strange errors are returned or changes are not visible at all. It takes time to stop application and run it from scratch
2. Oracle recommended approach is to use Make All to recompile the code and then refresh or reopen browser (Servlet Reload or Fast Swap options). This should bring the changes in ADF BC, Bindings, Task Flows and UI. While this works on simple use cases, often it doesnt work if many files are changed at once
What if you want to re-run application in JDeveloper 12c, same as it was possible in 11g? Yes, you can do it. There is a simple configuration change in JDeveloper 12c, to be able to rerun application without issues:
Go to JDeveloper preferences wizard and select Run option. Uncheck option "Enable Application Rerun Optimizations" - this will allow to rerun ADF application without errors and without a need to stop and start it manually:
Once application rerun optimizations are disabled in JDeveloper, you should add simple configuration to your ADF application (to make sure application redeploys fast). Disable auto generation for JDBC descriptors in application profile (Deployment -> WebLogic):
Provide external Data Source in AM configuration, or set direct JDBC URL (suitable for testing locally, as in my case):
Do above steps, when Make All (Servlet Reload or Fast Swap) doesn't work for you:
Accessing Oracle Mobile Cloud Service (MCS) REST from Postman
Do you want to test in your environment REST service running on Oracle MCS cloud? I have implemented public REST service hosted on Oracle MCS, you can call it from Postman application (Google Chrome extension).
Implemented use case - SOAP Connector in MCS is reading Stock Quote information for the company supplied in the request. Information is transformed by MCS Custom API (with Node.js) into REST structure and returned to the client through MCS Mobile backend.
Here is the REST GET url to invoke in Postman: https://mobileportalsetrial1304dev-mcsdem0001.mobileenv.us2.oraclecloud.com:443/mobile/custom/STOCKQUOTE_API_REDSAMURAI_BLOG_1/stockquote/ORCL. Try to run it, you can replace ORCL with another company ID. To execute this REST request in Postman, you need to provide configuration file (this allows to call MCS). Configuration file can be downloaded from here - Postman_MCS_1.json. This file contains all required info to make REST request from Postman:
To add configuration file to Postman, go to Configuration and upload it through Import data option:
Make sure MCS profile is selected:
When you copy paste REST URL, go to Headers section and select MCS from Presets - this will add two required headers:
You should see company stock information returned back from REST call:
Let's switch to MCS. There are three main concepts in MCS - Mobile Backend, custom API, Connector. Mobile Backend is a service interface, it allows to configure mobile access functionality and contains a list of Web Services (API's) exposed to the clients. It allows to track service usage and view statistics in the dashboard:
My example contains single Web Service (custom API). This service is responsible to handle REST GET method and return company stock quote data:
Custom API defines REST resource /stockquote/{company}, this is what we are executing in Postman URL:
Resource /stockquote/{company} is configured with GET method:
GET method is configured to handle two type of responses - 200 (OK) and 404 (Not Found):
To provide API implementation (mapping between Connector and API response), we can download (see example - stockquote_api_redsamurai_blog_1.zip) Node.js file from MCS and edit it locally (edited file can be uploaded back in the archive):
We need to specify Connector name to be available in Node.js, this is how it will call Web Service defined in the Connector:
Node.js function is responsible to call Web Service from Connector, pass parameter and check the result (here you can do complex transformations between SOAP and REST):
I'm using SOAP connector, pointing to external Web Service to fetch Stock Quote information for the company:
Implemented use case - SOAP Connector in MCS is reading Stock Quote information for the company supplied in the request. Information is transformed by MCS Custom API (with Node.js) into REST structure and returned to the client through MCS Mobile backend.
Here is the REST GET url to invoke in Postman: https://mobileportalsetrial1304dev-mcsdem0001.mobileenv.us2.oraclecloud.com:443/mobile/custom/STOCKQUOTE_API_REDSAMURAI_BLOG_1/stockquote/ORCL. Try to run it, you can replace ORCL with another company ID. To execute this REST request in Postman, you need to provide configuration file (this allows to call MCS). Configuration file can be downloaded from here - Postman_MCS_1.json. This file contains all required info to make REST request from Postman:
To add configuration file to Postman, go to Configuration and upload it through Import data option:
Make sure MCS profile is selected:
When you copy paste REST URL, go to Headers section and select MCS from Presets - this will add two required headers:
You should see company stock information returned back from REST call:
Let's switch to MCS. There are three main concepts in MCS - Mobile Backend, custom API, Connector. Mobile Backend is a service interface, it allows to configure mobile access functionality and contains a list of Web Services (API's) exposed to the clients. It allows to track service usage and view statistics in the dashboard:
My example contains single Web Service (custom API). This service is responsible to handle REST GET method and return company stock quote data:
Custom API defines REST resource /stockquote/{company}, this is what we are executing in Postman URL:
Resource /stockquote/{company} is configured with GET method:
GET method is configured to handle two type of responses - 200 (OK) and 404 (Not Found):
To provide API implementation (mapping between Connector and API response), we can download (see example - stockquote_api_redsamurai_blog_1.zip) Node.js file from MCS and edit it locally (edited file can be uploaded back in the archive):
We need to specify Connector name to be available in Node.js, this is how it will call Web Service defined in the Connector:
Node.js function is responsible to call Web Service from Connector, pass parameter and check the result (here you can do complex transformations between SOAP and REST):
I'm using SOAP connector, pointing to external Web Service to fetch Stock Quote information for the company:
OFM Forum Session Slides - Oracle JET and WebSocket
I would like to post slides from my session on Oracle Fusion Middleware Forum - Oracle JET and WebSocket. This session was done today.
Slides are available on slideshare:
Sample application used for the demo, can be downloaded from here (it consists of three parts - WebSocker server implementation, ADF BC tester application to generate continuos DB updates and JET application) - Oracle JET Live List with WebSocket.
ADF application with live data DVT component rendered in JET can be downloaded from here - When JET Comes To Rescue - Live Data Charts in ADF.
Slides are available on slideshare:
Sample application used for the demo, can be downloaded from here (it consists of three parts - WebSocker server implementation, ADF BC tester application to generate continuos DB updates and JET application) - Oracle JET Live List with WebSocket.
ADF application with live data DVT component rendered in JET can be downloaded from here - When JET Comes To Rescue - Live Data Charts in ADF.
Oracle JET and ADF BC REST Basic Authentication
You might be interested to check my previous sample about CRUD implementation in JET - Handling ADF BC 12.2.1 REST Validation in Oracle JET. I'm going to describe how to access secure ADF BC REST service from JET, based on the CRUD sample app. We need to pass authorization header on each REST request, this way server can authenticate user and authorize access to the REST resource. There are couple of other tips applied on ADF BC REST service side, all described below.
Blog reader rjahn shared a link in this blog comment - Oracle JET - Rendering Table from ADF BC REST Service, he describes how to pass authorization header using oauth property, while executing GET and fetching collection in JET. I'm using same approach for GET, but for PATCH, POST and DELETE authorization header is set in slightly different way.
First of all we need to take a look into ADF BC REST security implementation. Each time when custom header is attached in JET, it executes OPTIONS method without appending authorization header. I have disabled security check for OPTIONS in web.xml:
Important tip - by default security operations granted to ADF BC resource are all assigned through single permission (in jazn-data.xml):
I have found this doesn't work, even from Postman - only first operation from the list is authorized. This must be a bug in ADF BC REST security. Workaround is to separate each operation into different permission group manually:
GET executed from JET is authorized and data is coming through:
We can see in network monitor recorded GET execution from JET - authorization header is submitted along with request:
Authorization header is encoded with JavaScript btoa function. Obviously it must run on HTTPS to be completely secure, otherwise someone can intercept and decode the password. Authorization header is being constructed in JET (you would fetch username/password from HTML form, etc.) and is attached to the collection (this way it gets included into request header executed by GET). It doesn't work to attach it to collection through header property (as it works for POST, PATCH and DELETE), but it works with oauth:
Authorization header can be set for PATCH. Data can be updated through secure service:
Authorization header is present in PATCH request:
Authorization header is injected through property - headers (differently than for collection and GET). It didn't work to use oauth here:
Create case is executed through POST, security is enforced through authorization header here also:
We can see header is present in the request:
Authorization header is attached in the same way as for PATCH:
Delete case is executed through DELETE, same authorization header is applied:
Header info:
Authorization header is attached to JET destroy operation:
Download sample application (JET and ADF BC code) - JETCRUDApp_v5.zip. You should include JET code into JET toolkit distribution.
Blog reader rjahn shared a link in this blog comment - Oracle JET - Rendering Table from ADF BC REST Service, he describes how to pass authorization header using oauth property, while executing GET and fetching collection in JET. I'm using same approach for GET, but for PATCH, POST and DELETE authorization header is set in slightly different way.
First of all we need to take a look into ADF BC REST security implementation. Each time when custom header is attached in JET, it executes OPTIONS method without appending authorization header. I have disabled security check for OPTIONS in web.xml:
Important tip - by default security operations granted to ADF BC resource are all assigned through single permission (in jazn-data.xml):
I have found this doesn't work, even from Postman - only first operation from the list is authorized. This must be a bug in ADF BC REST security. Workaround is to separate each operation into different permission group manually:
GET executed from JET is authorized and data is coming through:
We can see in network monitor recorded GET execution from JET - authorization header is submitted along with request:
Authorization header is encoded with JavaScript btoa function. Obviously it must run on HTTPS to be completely secure, otherwise someone can intercept and decode the password. Authorization header is being constructed in JET (you would fetch username/password from HTML form, etc.) and is attached to the collection (this way it gets included into request header executed by GET). It doesn't work to attach it to collection through header property (as it works for POST, PATCH and DELETE), but it works with oauth:
Authorization header can be set for PATCH. Data can be updated through secure service:
Authorization header is present in PATCH request:
Authorization header is injected through property - headers (differently than for collection and GET). It didn't work to use oauth here:
Create case is executed through POST, security is enforced through authorization header here also:
We can see header is present in the request:
Authorization header is attached in the same way as for PATCH:
Delete case is executed through DELETE, same authorization header is applied:
Header info:
Authorization header is attached to JET destroy operation:
Download sample application (JET and ADF BC code) - JETCRUDApp_v5.zip. You should include JET code into JET toolkit distribution.
ADF BC REST Support for List Of Values (LOV) Data
ADF BC REST service out of the box supports LOV list data. You can define LOV for ADF BC View Object attribute and use it straight away in REST service. This is especially useful when LOV list is filtered based on other attributes from current row - no need to collect and send these attributes to the service separately, filtering logic will be handled for you in ADF BC backend. One more benefit - you can reuse existing ADF BC with LOV's implementation and expose it through REST service.
Below you can see employee #108 attributes with values returned by REST service, generated on top of ADF BC View Object. ManagerId attribute in View Object is configured with LOV list. Based on this configuration, each item in REST collection will have a link to REST resource providing LOV data (Employees/280/lov/EmployeesLovView1). For employee #108, it retrieves LOV data from VO EmployeesLovView1:
We can get LOV list entries for employee #108 by executing simple REST request, no need to specify additional parameters (Employees/280/lov/EmployeesLovView1):
LOV VO is set with View Criteria to return a list of possible managers, employees with the same job, excluding employee himself:
LOV View Accessor in the main VO (Employees) is configured to use employee ID and job ID from current row (current REST collection item) - in this way LOV list will be filtered automatically, by View Criteria:
The only thing that needs to be done to enable LOV support - define LOV for manager ID attribute in the base VO:
As you see, it is pretty easy to reuse ADF BC LOV's in REST service. Download sample application - ADFBCRestApp_v6.zip.
Below you can see employee #108 attributes with values returned by REST service, generated on top of ADF BC View Object. ManagerId attribute in View Object is configured with LOV list. Based on this configuration, each item in REST collection will have a link to REST resource providing LOV data (Employees/280/lov/EmployeesLovView1). For employee #108, it retrieves LOV data from VO EmployeesLovView1:
We can get LOV list entries for employee #108 by executing simple REST request, no need to specify additional parameters (Employees/280/lov/EmployeesLovView1):
LOV VO is set with View Criteria to return a list of possible managers, employees with the same job, excluding employee himself:
LOV View Accessor in the main VO (Employees) is configured to use employee ID and job ID from current row (current REST collection item) - in this way LOV list will be filtered automatically, by View Criteria:
The only thing that needs to be done to enable LOV support - define LOV for manager ID attribute in the base VO:
As you see, it is pretty easy to reuse ADF BC LOV's in REST service. Download sample application - ADFBCRestApp_v6.zip.
Oracle JET Live List with WebSocket
I have updated sample - Oracle JET and WebSocket Integration for Live Data, to include live data list. Along with live updates delivered to the chart component, list component displays changed data. I'm displaying five last changes, with new data appearing in the last row:
Watch recorded demo, each 4 seconds new data arrives and chart is updated, along with list records:
JET list component UI structure (data grid) is rendered with a template, to arrange separate column for each job:
When new data arrives, it is being pushed into JET observable array (this triggers automatic UI list reload/refresh). If there are more than five items in the list, first one is removed. List observable array is configured with 500 ms. delay, to make UI refresh smoother:
Download sample application (JET, WebSocket server side and ADF to simulate updates in the DB) - JETWebSocket_v2.zip.
I'm going to present JET and WebSocket integration on Oracle Fusion Middleware Partner Community Forum 2016.
Watch recorded demo, each 4 seconds new data arrives and chart is updated, along with list records:
JET list component UI structure (data grid) is rendered with a template, to arrange separate column for each job:
When new data arrives, it is being pushed into JET observable array (this triggers automatic UI list reload/refresh). If there are more than five items in the list, first one is removed. List observable array is configured with 500 ms. delay, to make UI refresh smoother:
Download sample application (JET, WebSocket server side and ADF to simulate updates in the DB) - JETWebSocket_v2.zip.
I'm going to present JET and WebSocket integration on Oracle Fusion Middleware Partner Community Forum 2016.