In ADF applications, in that case if database is down, user will get error message and site will be broken. If user will open site, when database was already down, user will see 'Loading' message and nothing else. If database will go down, during user activity - most likely NullPointerException will be raised and user will see broken site. Such behavior is not acceptable, enterprise services should be available even when database is not working. Ideally, database related content should be disabled and static content still should be loaded and available.
Today I have developed sample application, where regions are loaded conditionally, based on database availability. Download working sample - ApplicationAvailability.zip.
We will follow such scenario - user is working with Departments and Jobs regions:
Suddenly, database goes down for some unknown reason:
User of course is not aware about database status and press Next button in Departments region:
At this point, ADF will generate Exception, however we can handle it with Error activity registered in ADF Task Flow:
Without closing browser, user can refresh site, and if database still will be down - only static content will be loaded, database related regions will be disabled (I will describe how you can achieve this after few steps):
This allows to present at least static content, which is much better comparing to Error page, I'm even not talking about visible exception.
New users, who will open site while database is still down, will get static content rendered as well:
Finally, when database will be back to life again:
It will be enough just to refresh browser (no need to close it and open again), regions will be activated automatically and dynamic content will be rendered:
Now I will explain how it is done. Thanks to ADF Regions functionality , we can activate them conditionally:
When we use conditional region activation, activation condition must be provided. In this case, I'm calling managed bean (request scope) where check is done for Application Module status:
Its enough to acquire Application Module instance reference, if database will be down, Application Module will be unavailable and exception will be thrown. We can catch this exception and deactivate regions:
Finally, Error activity should be defined in ADF Task Flow in order to handle exception, when database is going down during active session:
Monday, January 25, 2010
Friday, January 22, 2010
Demystifying ADF BC Passivation and Activation
It happens to face exceptions in production environment, even if during development and testing everything was working good. In most of the cases, this strange behavior will be related to wrong transient attribute usage, transient attribute which value is not passivated and then its lost after activation. You can test your code, if it contains any bad practice implementations, by disabling application module pooling. Most of ADF developers know this, however only few really understand what is happening. Today I will present sample case, where bad practice is demonstrated practically.
Let's say developer decided to store temporary values in VO transient attribute and created new DummyAttr attribute in Employees VO:
Developer thinks - since I already have my temporary value in VO, why not to use it for another, this time permanent, attribute. And he calls transient attribute from permanent attribute getter method:
Thats it, Model is implemented. Next step, transient attribute should be somehow initialized. Its easy in ADF, we can use af:setActionListener component and initialize attribute directly from ViewController:
In our case, DummyAttr will be assigned with DepartmentName value:
At this point, functionality is implemented and can be tested. On runtime, transient attribute will be initialized in Departments page:
Employees page will be successfully opened:
Everything works good during testing. In production, when there will be high workload and many concurrent users, ADF will start to passivate Application Module instances and we will face a problem with such implementation. Production environment can be simulated by disabling Application Module Pooling:
User will open Departments page and will set transient attribute value:
Will try to open next page - Employees, and will get java.lang.NullPointerException. Why? Let's find an answer in detail log. When there is not enough space in the pool (during next submit in our test case), and Application Module instance is passivated, transient DummyAttr attribute value was not passivated and this means it was simply lost:
There is nothing to activate for transient DummAttr attribute, and since this attribute is called, exception is raised.
You should avoid such use cases as I just described and develop proper quality ADF code. In worst case scenario, which is not recommended anyway, you can force transient attribute passivation on attribute level:
Or even on VO level:
Then you can see that transient attribute will be passivated as well:
This means, code will work without exception, because activation will be done successfully. However, in my opinion, you should avoid this approach and use it only when you are really forced.
Download sample application, where Application Module Pooling is disabled and described bad practice is reproduced - AppModulePooling.zip.
Let's say developer decided to store temporary values in VO transient attribute and created new DummyAttr attribute in Employees VO:
Developer thinks - since I already have my temporary value in VO, why not to use it for another, this time permanent, attribute. And he calls transient attribute from permanent attribute getter method:
Thats it, Model is implemented. Next step, transient attribute should be somehow initialized. Its easy in ADF, we can use af:setActionListener component and initialize attribute directly from ViewController:
In our case, DummyAttr will be assigned with DepartmentName value:
At this point, functionality is implemented and can be tested. On runtime, transient attribute will be initialized in Departments page:
Employees page will be successfully opened:
Everything works good during testing. In production, when there will be high workload and many concurrent users, ADF will start to passivate Application Module instances and we will face a problem with such implementation. Production environment can be simulated by disabling Application Module Pooling:
User will open Departments page and will set transient attribute value:
Will try to open next page - Employees, and will get java.lang.NullPointerException. Why? Let's find an answer in detail log. When there is not enough space in the pool (during next submit in our test case), and Application Module instance is passivated, transient DummyAttr attribute value was not passivated and this means it was simply lost:
There is nothing to activate for transient DummAttr attribute, and since this attribute is called, exception is raised.
You should avoid such use cases as I just described and develop proper quality ADF code. In worst case scenario, which is not recommended anyway, you can force transient attribute passivation on attribute level:
Or even on VO level:
Then you can see that transient attribute will be passivated as well:
This means, code will work without exception, because activation will be done successfully. However, in my opinion, you should avoid this approach and use it only when you are really forced.
Download sample application, where Application Module Pooling is disabled and described bad practice is reproduced - AppModulePooling.zip.
Monday, January 18, 2010
Storing/Accessing Objects in ADF BC UserData Map
While implementing one of the requirements for View Criteria, I faced an issue when there was a request to filter View Criteria Item based on user language. Well, its trivial when you want to filter choice list defined for attribute - just specify bind variable value in View Accessor. Its a bit more tricky for View Criteria Item choice list, mainly because View Criteria can't access value from VO row. This means, if we want to filter View Criteria Item choice list, we need to use Groovy language script in order to retrieve value for View Accessor bind variable. In my case it was user language. What I did - I have read Example #154 from Steve Muench blog - Not Yet Documented ADF Sample Applications, and decided to store user language in UserData map, in order to access it from View Accessor Groovy.
If you will read Steve post, you will understand there are two approaches to access from your Model data available in ViewController layer. I prefer approach when UserData is set from custom Application Module method and then it can be accessed through Groovy.
Download sample application - UserDataGroovy.zip, where LocationId choice list is filtered based on user language. When browser works with English locale, LocationId brings locations from US:
When its set with French locale, LocationId is filtered again and shows French speaking countries:
As I already mentioned, there should be always clear separation between Model and ViewController. I'm storing user language value in UserData by calling ADF Task Flow Method Call:
This Method Call is invoking Application Module custom method, where current language is stored in UserData map:
You should see, language is stored in UserData map with currentUserLanguage ID. Same ID is used to access current language from View Accessor - adf.userSession.userData.currentUserLanguage:
If you will read Steve post, you will understand there are two approaches to access from your Model data available in ViewController layer. I prefer approach when UserData is set from custom Application Module method and then it can be accessed through Groovy.
Download sample application - UserDataGroovy.zip, where LocationId choice list is filtered based on user language. When browser works with English locale, LocationId brings locations from US:
When its set with French locale, LocationId is filtered again and shows French speaking countries:
As I already mentioned, there should be always clear separation between Model and ViewController. I'm storing user language value in UserData by calling ADF Task Flow Method Call:
This Method Call is invoking Application Module custom method, where current language is stored in UserData map:
You should see, language is stored in UserData map with currentUserLanguage ID. Same ID is used to access current language from View Accessor - adf.userSession.userData.currentUserLanguage:
Wednesday, January 13, 2010
Red Samurai Consulting - OPN Specialization Video
Oracle Partner Network (OPN) published interview with me, I'm talking about Oracle Specialized program:
Tuesday, January 12, 2010
Auto Commit Use Case in Oracle ADF 11g Table Component
It happens to develop such screens, where users can browse through table rows, select any row and using related form update selected row data. Thinking from pure end-user approach, when data table contains many columns, sometimes its easier to edit row data in separate form. Everything is fine with this approach, however there is one thing that can be confusing to the end-user. Let's say, user is editing row data and then suddenly selects another row from the table, without saving changes from previous row. In such way, user can end up with changes in many rows and will loose track what should be saved and what reverted. This use case is from real life.
In order to avoid user confusion with changes in multiple rows, I have developed sample application - TableAutoCommit.zip. This application implements read-only table and editable form, which brings data from currently selected row. Main trick - if user will edit data from selected row and will select another row without saving or reverting his changes, changes will be saved automatically - this will allow end-user to keep track of his changes easier. Of course, its not generic solution and should not be applied everywhere, it should be applied only if its really needed in specific case.
In this example, user selects first row from the table, editable form allows to change selected attributes:
Then he enters phone number:
And selects another row without saving changes he did:
Changes will be saved automatically and message will appear to inform user about latest commit:
When there are no pending changes, user can select any rows, nothing happens:
I will describe how it is implemented - really easy with ADF 11g. Only thing you need to have is custom SelectionListener for table component:
Also there should be method to determine unsaved changes. You can check if there are any, just with ADF standard method - isDirty():
Lastly, if there are unsaved changes, invoke commit operation and display information message. Otherwise, just proceed to the next row selection:
In order to avoid user confusion with changes in multiple rows, I have developed sample application - TableAutoCommit.zip. This application implements read-only table and editable form, which brings data from currently selected row. Main trick - if user will edit data from selected row and will select another row without saving or reverting his changes, changes will be saved automatically - this will allow end-user to keep track of his changes easier. Of course, its not generic solution and should not be applied everywhere, it should be applied only if its really needed in specific case.
In this example, user selects first row from the table, editable form allows to change selected attributes:
Then he enters phone number:
And selects another row without saving changes he did:
Changes will be saved automatically and message will appear to inform user about latest commit:
When there are no pending changes, user can select any rows, nothing happens:
I will describe how it is implemented - really easy with ADF 11g. Only thing you need to have is custom SelectionListener for table component:
Also there should be method to determine unsaved changes. You can check if there are any, just with ADF standard method - isDirty():
Lastly, if there are unsaved changes, invoke commit operation and display information message. Otherwise, just proceed to the next row selection:
Sunday, January 3, 2010
WebCenter 11g Discussion Service in Oracle ADF
Everyone is familiar with various discussion services, for example OTN Forums. However, not everyone knows that you can use similar discussion services directly from ADF 11g application. Today I will describe how you can enable WebCenter 11g product component - Discussion Services. Before reading my post, I would recommend to walk through instructions available on George Maggessy blog - Oracle Discussions Configuration. Also you should read Section 17.2.1 Setting up Connections for Discussions from WebCenter 11g Developer Guide.
Download sample application for today post - WebCenterDiscussions.zip. This application implements ADF table and Discussion Service from WebCenter server on the same page.
Before developer will be able to use Discussion Service, there should be defined Discussion Service connection in Application Resources. You can read about this in 17.2.1 section mentioned above. However, there is one trick with forum.connection.secure property. In that case, when application is protected by ADF Security, you must set this property to true and define additional properties.
If Discussion Service connection - DevDiscussions:
Is defined with default false value for forum.connection.secure property:
After user login:
Authentication Failure will be reported for Discussion Service Task Flow:
I know cases where same thing was working in JDeveloper 11g R1, but it didn't worked in JDeveloper 11g R1 PS1. It is correct, because connection must be secured, when ADF application is protected by ADF Security.
Let's enable secured Discussion Service connection:
When connection is declared as secured, you must obtain security certificates and declare additional properties for Discussion Service Connection. You can read about this in WebCenter 11g Administrator Guide Section 23.8 Configuring WS-Security. However, its very hard to understand described instructions. I got additional hints from Oracle about how to generate keys and certificates, will list them now in step-by-step way:
1. Generate new key
keytool -genkey -alias jive -keypass jive_password -keystore client_certs.keystore -storepass clientKeyStorePassword -dname "cn=jive" -keyalg RSA
Remember three Discussion Service Connection properties:
encryption.key.alias = jive
encryption.key.password = jive_password
keystore.password = clientKeyStorePassword
2. Generate client signed certificate client_certs.keystore
keytool -selfcert -alias jive -keystore client_certs.keystore -storepass clientKeyStorePassword -keypass jive_password
3. Export the public key from the certificate
keytool -export -alias jive -file client_public_key.jks -keystore client_certs.keystore -storepass clientKeyStorePassword
4. Import the public key into a web app accessible keystore
keytool -import -alias jive -file client_public_key.jks -keystore server_public_certs.keystore -storepass serverKeyStorePassword
Generated server_public_certs.keystore file will be used as server certificate
Up to this step you should have those three files generated:
5. Copy the server_public_certs.keystore to the WebCenter 11g server machine
In my case, file was copied to /home/oracle/keystore
6. Set up the Jive Server Properties
In the WebCenter domain home lib directory (/home/oracle/Oracle/Middleware/user_projects/domains/wc_domain/lib), create a file named keystore.properties.
Within this file add the following values (change them according your case):
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=serverKeyStorePassword
org.apache.ws.security.crypto.merlin.keystore.alias=jive
org.apache.ws.security.crypto.merlin.file=/home/oracle/keystore/server_public_certs.keystore
7. Place the keystore.properties file into a jar file named jive_crypto_props.jar
jar cvf jive_crypto_props.jar keystore.properties
8. Modify the Jive System Property
Login as the admin user, to the Jive admin page, for example: http://127.0.0.1:8890/owc_discussions/admin
Click on the System Properties link and add the following property:
webservices.soap.custom.crypto.fileName=keystore.properties
9. Restart WebCenter 11g Services Managed Server
10. Last step, define Additional Properties for Discussion Service Connection:
Now discussions will appear inside my sample ADF application:
Let's talk a bit more about discussions security configuration. In my sample I'm not using SSO, ADF application is deployed on JDeveloper 11g embedded WebLogic, while WebCenter 11g Services are running on standalone WebLogic domain. This means I should define same users in both systems. ADF application jazn-data.xml file contains two users - john and scott:
Same two users I have defined in standalone WebLogic domain embedded LDAP:
Those users will be used by Discussions Service. I will grant Moderator rights to user scott:
Discussion Service is based on the same embedded LDAP from standalone WebLogic, both users are propagated automatically:
Thats all about security, now you can enable Discussion Service inside your ADF application just by doing WebCenter 11g ADF Task Flow drag and drop:
Let's see how it works on runtime. If we login as user john, we can create new discussion topic:
New topic is created:
Now let's login as another user - scott, and answer to john question:
User scott is granted Moderator privileges, this means he can edit or even delete john post:
But if scott creates new topic about ADF trainings:
User john will be able to read it and reply:
However, will not be able to edit or remove it, because john is not granted with Moderator rights:
Its important to mention, same posts done through ADF application, can be accessed through external Discussions Service URL:
Download sample application for today post - WebCenterDiscussions.zip. This application implements ADF table and Discussion Service from WebCenter server on the same page.
Before developer will be able to use Discussion Service, there should be defined Discussion Service connection in Application Resources. You can read about this in 17.2.1 section mentioned above. However, there is one trick with forum.connection.secure property. In that case, when application is protected by ADF Security, you must set this property to true and define additional properties.
If Discussion Service connection - DevDiscussions:
Is defined with default false value for forum.connection.secure property:
After user login:
Authentication Failure will be reported for Discussion Service Task Flow:
I know cases where same thing was working in JDeveloper 11g R1, but it didn't worked in JDeveloper 11g R1 PS1. It is correct, because connection must be secured, when ADF application is protected by ADF Security.
Let's enable secured Discussion Service connection:
When connection is declared as secured, you must obtain security certificates and declare additional properties for Discussion Service Connection. You can read about this in WebCenter 11g Administrator Guide Section 23.8 Configuring WS-Security. However, its very hard to understand described instructions. I got additional hints from Oracle about how to generate keys and certificates, will list them now in step-by-step way:
1. Generate new key
keytool -genkey -alias jive -keypass jive_password -keystore client_certs.keystore -storepass clientKeyStorePassword -dname "cn=jive" -keyalg RSA
Remember three Discussion Service Connection properties:
encryption.key.alias = jive
encryption.key.password = jive_password
keystore.password = clientKeyStorePassword
2. Generate client signed certificate client_certs.keystore
keytool -selfcert -alias jive -keystore client_certs.keystore -storepass clientKeyStorePassword -keypass jive_password
3. Export the public key from the certificate
keytool -export -alias jive -file client_public_key.jks -keystore client_certs.keystore -storepass clientKeyStorePassword
4. Import the public key into a web app accessible keystore
keytool -import -alias jive -file client_public_key.jks -keystore server_public_certs.keystore -storepass serverKeyStorePassword
Generated server_public_certs.keystore file will be used as server certificate
Up to this step you should have those three files generated:
5. Copy the server_public_certs.keystore to the WebCenter 11g server machine
In my case, file was copied to /home/oracle/keystore
6. Set up the Jive Server Properties
In the WebCenter domain home lib directory (/home/oracle/Oracle/Middleware/user_projects/domains/wc_domain/lib), create a file named keystore.properties.
Within this file add the following values (change them according your case):
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=serverKeyStorePassword
org.apache.ws.security.crypto.merlin.keystore.alias=jive
org.apache.ws.security.crypto.merlin.file=/home/oracle/keystore/server_public_certs.keystore
7. Place the keystore.properties file into a jar file named jive_crypto_props.jar
jar cvf jive_crypto_props.jar keystore.properties
8. Modify the Jive System Property
Login as the admin user, to the Jive admin page, for example: http://127.0.0.1:8890/owc_discussions/admin
Click on the System Properties link and add the following property:
webservices.soap.custom.crypto.fileName=keystore.properties
9. Restart WebCenter 11g Services Managed Server
10. Last step, define Additional Properties for Discussion Service Connection:
Now discussions will appear inside my sample ADF application:
Let's talk a bit more about discussions security configuration. In my sample I'm not using SSO, ADF application is deployed on JDeveloper 11g embedded WebLogic, while WebCenter 11g Services are running on standalone WebLogic domain. This means I should define same users in both systems. ADF application jazn-data.xml file contains two users - john and scott:
Same two users I have defined in standalone WebLogic domain embedded LDAP:
Those users will be used by Discussions Service. I will grant Moderator rights to user scott:
Discussion Service is based on the same embedded LDAP from standalone WebLogic, both users are propagated automatically:
Thats all about security, now you can enable Discussion Service inside your ADF application just by doing WebCenter 11g ADF Task Flow drag and drop:
Let's see how it works on runtime. If we login as user john, we can create new discussion topic:
New topic is created:
Now let's login as another user - scott, and answer to john question:
User scott is granted Moderator privileges, this means he can edit or even delete john post:
But if scott creates new topic about ADF trainings:
User john will be able to read it and reply:
However, will not be able to edit or remove it, because john is not granted with Moderator rights:
Its important to mention, same posts done through ADF application, can be accessed through external Discussions Service URL: