Wednesday, April 27, 2011

Modifying the Application Module's JDBC DataSource at run time

In normal scenario, your Application Module(AM) may have a static JDBC data source configured and will be connected to the single data base in all conditions. However there are cases where the application may need to display data from different data bases based on user profile. In today's post, I talk about an example for the above use case. Well, this is not a new topic as such. In fact, this has been discussed previously many times.

1. Dynamic JDBC Credentials example by Steve Muench
2. How To Support Dynamic JDBC Credentials

I'm copying the same solution in a slightly modified form ;)

Programmatically setting DataSource for an Application Module

A bit of theory before get in to the implementation. When a Fusion Web Page is getting rendered, UI controls may try to get data by evaluating the expression set for the value attribute. At this stage binding layer comes in to action, and will identify the underlying data provider(your Application Module) for the the specific attribute. When the binding layer refers Application Module, run time would either an instance or check out one from the AM pool. By default, AM instance is configured using the static meta data from bc4j.xcfg file, which is generated at design time. The design time configuration can be very well overridden by system parameters supplied at run time (java -Dproperty=value).

In the above use case, the requirement is to override the default data source specified for an AM. Interestingly, ADF BC provides a mechanism to override the configuration properties dynamically using a custom EnvInfoProvider. This part is well explained in Fusion Developer's Guide - 41.2.4 How to Programmatically Set Configuration Properties.

In this example, I'm trying to make use of this custom EnvInfoProvider which may get engaged whenever run time needs configuration properties for an Application Module. You may also need to keep jbo.ampool.dynamicjdbccredentials=true(default is true) to enable additional pooling lifecycle events to allow developer-written code to change the db credential.

The custom EnvInfoProvider used in this example may look like as shown in the following picture. You can see that the DynamicEnvInfoProvider::getInfo(...) overrides the default configuration property for Configuration.JDBC_DS_NAME(JDBC DataSource) using value from a session variable.

 public class DynamicEnvInfoProvider implements EnvInfoProvider {  
  public Object getInfo(String infoType, Object env) {  
  if (EnvInfoProvider.INFO_TYPE_JDBC_PROPERTIES.equals(infoType)) {  
      Map session = ADFContext.getCurrent().getSessionScope();  
      Object dsName = session.get(Configuration.JDBC_DS_NAME);  
      if (dsName != null) {  
           if (((Hashtable)env).containsKey(Configuration.JDBC_DS_NAME)) {  
                ((Hashtable)env).put(Configuration.JDBC_DS_NAME,  
                                          (String)dsName);  
           }  
      }  
  }  
  return null;  
 }  

Note

When you enable dynamic credentials (jbo.ampool.dynamicjdbccredentials) the AM JDBC credentials will be updated whenever a new DataControl is constructed (new AM handle is created). The credentials will NOT be updated at the begin of each request in a stateful session, even though the AM is released at the end of each request.

Configuring a custom EnvInfoProvider for AM

There are two possible approaches for hooking your custom EnvInfoProvider with AM.

Approach 1 - Using custom session cookie factory class

a) Implement a custom session cookie factory class to set the custom EnvInfoProvider on the SessionCookie instance. Runtime will check for overridden EnvInfoProvider on the session cookie whenever JDBC data source needs to be created.

   @Override  
   public SessionCookie createSessionCookie(String applicationId,  
                        String sessionId,  
                        ApplicationPool pool,  
                        Properties properties) {  
     SessionCookie cookie =  
       super.createSessionCookie(applicationId, sessionId, pool,  
                    properties);  
     EnvInfoProvider provider = new DynamicEnvInfoProvider();  
     cookie.setEnvInfoProvider(provider);  
     return cookie;  
   }  
b)Configure AM to use the custom session cookie factory class


The following class diagram depicts the runtime engagement between the classes.


Approach 2 - Using Application Module configuration parameter - jbo.envinfoprovider

Configure 'jbo.envinfoprovider' to use custom EnvInfoProvider class using the AM configuration editor as shown in the following screen shot. Well, this seems to be more straight forward and much simpler compared to Approach 1.


The above action may update the bc4j.xcfg file as shown below.

Please note that, you may face some weird error at design time if you try modifying this property using JDeveloper 11.1.1.4.0 release. In case if the editor errors out while manipulating this property, then please follow the Approach 1(though you may need to add a couple of 'infrastructural' classes)

 <?xml version = '1.0' encoding = 'UTF-8'?>  
 <BC4JConfig version="11.1" xmlns="http://xmlns.oracle.com/bc4j/configuration">  
   <AppModuleConfigBag ApplicationName="model.AppModule">  
    <AppModuleConfig DeployPlatform="LOCAL" RELEASE_MODE="Stateless"  jbo.project="model.Model" name="AppModuleLocal" ApplicationName="model.AppModule">  
      <AM-Pooling jbo.doconnectionpooling="true"  />  
      <Database jbo.locking.mode="optimistic"/>  
      <Security AppModuleJndiName="model.AppModule"/>  
      <Custom JDBCDataSource="java:comp/env/jdbc/HRDS" jbo.envinfoprovider="binding.extension.DynamicEnvInfoProvider"/>  
    </AppModuleConfig>    
   </AppModuleConfigBag>  
 </BC4JConfig>  


Download

You can download the sample workspace from here.
[Runs with Oracle JDeveloper 11g R1 PS3 + HR Schema]

How to run this sample?

The 'CoreBindingRuntime' project in the example workspace contains custom implementations for EnvInfoProvider and HttpSessionCookieFactory classes. Take a look at this project to see the implementations.

1. To run this example you may need to configure a JDBC Data Source (pointing to HR schema) in your weblogic server. You can do this using weblogic admin console.

2. Run the entry.jspx, key in the JNDI name of the JDBC data source(refer step 1) and click on command button


3. You can see the data source what you keyed at step 2 is used by the next page to execute the view object.

Learn More ...
There are a lot more points like this. If  you are curious to learn the internals of the ADF Business Components and ADF Binding Layer,  the following book is for you - Oracle ADF Real World Developer’s Guide.
More details about this book can be found in this post- http://jobinesh.blogspot.in/2012/07/pre-order-your-copy-of-oracle-adf-real.html

Tuesday, April 26, 2011

Learning Oracle ADF

Sharing some resources/links on Oracle ADF which may help you to understand the technology better...

How Do I Start Learning Oracle ADF and JDeveloper 11g
JDeveloper ADF Oracle Technology Network site

Tutorials/Documentation/Useful Links

Demo Application

Hands on Topics
Learn More ...

 If  you are curious to learn the internals of the ADF Business Components and ADF Binding Layer,  the following book is for you - Oracle ADF Real World Developer’s Guide.
More details about this book can be found in this post- http://jobinesh.blogspot.in/2012/07/pre-order-your-copy-of-oracle-adf-real.html

Thursday, April 21, 2011

Accessing destination View Object using View Link Accessor

When you need to programmatically access the 'child' view object from a 'master' view object by virtue of a view link accessor, you can use the the below API.

ViewObject destVO = originVO.findAttributeDef("View Link Accessor Name").getAccessorVO(originVO);

Example:

ViewObjectImpl deptVO = getDepartmentsView();
ViewObject empVO = deptVO.findAttributeDef("EmployeesView").getAccessorVO(deptVO );

Wednesday, April 20, 2011

Identifying the source of the request for an LOV

The LOV can be enabled for an attribute of a view object, and the same can be used inside a query panel or in a parameter form.
How do you identify where the LOV is displayed currently, from a View Object?

Well, you can use the following groovy expression 'adf.isCriteriaRow' to achieve the same, which may rerun true if the request is from a model driven af:query component. This is useful if you need to switch between LOV definitions based on where its displayed at run time (e.g: one LOV in search panel and a different one in parameter form for the same attribute).

Please see the following topic in Dev Guide to learn more - How to Specify Multiple LOVs for an LOV-Enabled View Object Attribute

Saturday, April 16, 2011

Programmatically refreshing the child nodes of an <af:tree>

There are use cases where you may need to refresh(re-execute the query) the child nodes of a <af:tree> or <af:treeTable> programmatically . In this post I'm discussing a simple implementation for the same.

Query execution for retrieving child nodes

A bit of theory before getting in to the code sample. When you expand top level nodes for the first time, runtime will traverse through the View Link Accessor for the parent Row and identifies the child RowSet/ViewObject and execute the same. The execution of child View Object result in child nodes. When you expand the parent once again, the parent row may find an existing QueryCollection(wrapper object over Rows) with the matching row filter, e.g. say DeptId=10. This existing QC, which already has the filtered list of rows is re-used at this stage. In case you may need to refresh the child RowSet in all cases, you may need to brutally execute the corresponding RowSet. The below shown code snippet executes child Employees RowSet for a specific DepartmentId.

   public void refreshChildEmployees(Number deptId) {  
     ViewObjectImpl vo = getDepartmentsView1();  
     Row[] deptRow = vo.findByKey(new Key(new Object[] { deptId }), 1);  
     String nodeDefname = deptRow[0].getStructureDef().getDefFullName();  
     RowSet childRows = (RowSet)deptRow[0].getAttribute("EmployeesView");  
     childRows.executeQuery();  
   }  

Download

You can download the sample workspace from here.
[Runs with Oracle JDeveloper 11g R1 PS3 + HR Schema]

A glance at the implementation

This example displays tree table for Department-Employee hierarchy. A pop up is given for editing Employee data. You can edit the Employee details and click on the Update button to refresh the corresponding child nodes displayed on the parent page. In the edit pop up, there is a check box(labelled as HiddenFlag) to control the visibility of the corresponding row displayed in the tree table. If you select this check box, row is removed from the corresponding RowSet( which constitutes the child nodes of the tree table).


The EmployeesViewImpl::rowQualifies(...) has been overridden to enable the in memeory filtering for the child nodes . Please see this post in case you are not familiar with this concept. the above given efreshChildEmployees(Number deptId) is wired to the 'update' button which ensure the execution of EmployeesViewImpl::rowQualifies(...) by brutally executing the child RowSet.

Learn More ...

There are a lot more points like this. If  you are curious to learn the internals of the ADF Business Components and ADF Binding Layer,  the following book is for you - Oracle ADF Real World Developer’s Guide.
More details about this book can be found in this post- http://jobinesh.blogspot.in/2012/07/pre-order-your-copy-of-oracle-adf-real.html

Tuesday, April 12, 2011

Using JMS with ADF Business Components

There are use case scenarios where you may need to perform some service calls asynchronously, whose out put does not affect the success or failure of the entire business process. Some examples could be sending SMS notification on successful airline ticket booking or logging business data on data change etc. A possible solution for above case may be queuing the request/data for later process and configuring a listener on this Queue to process them asynchronously. In Java EE world, the Java Messaging Service (JMS) provides basic infrastructure for queuing messages. Interestingly, you can easily enable ADF Business Components to use JMS if your use case demands so. Sounds interesting? then read on... :)

Use case

In this example, I'm populating an 'audit' table whenever user commits the modified Department details. This audit table is updated asynchronously using a Message Driven Bean. The high level flow is depicted in the following diagram.


Download

You can download the sample workspace from here.
[Runs with Oracle JDeveloper 11g R1 PS3 + HR Schema]

Prerequisite for running the application

I. Create the table DEPARTMENTS_AUDIT to hold audit data for the Departments entity. You can either use the below given DDL or the script available in the above work space - <ADFJavaEEApp>/EJBModel/audit_tablescript.sql
 CREATE TABLE DEPARTMENTS_AUDIT   
 (  
  DEPARTMENT_ID NUMBER(4, 0) NOT NULL   
 , DEPARTMENT_NAME VARCHAR2(30 BYTE)   
 , MANAGER_ID NUMBER(6, 0)   
 , LOCATION_ID NUMBER(4, 0)   
 , MODIFIED_TIME TIMESTAMP(6) NOT NULL   
 , CONSTRAINT DEPT_AUDIT_CONSTRAINT PRIMARY KEY   
  (  
   DEPARTMENT_ID   
  , MODIFIED_TIME   
  )  
  ENABLE   
 )   

II. Configure the Queue Connection Factory and the Queue using Weblogic Admin Console. To do this configuration, you may need to define JMS Server and JMS Modules first. Don't worry, its easy!


Please refer the the below two links if you need detailed explanation on this topic
1. Configuring JMS Queues and Topics
2. JMS in JDeveloper 11g and WebLogic 10.3

High level steps for configuring Queue Connection Factory and Queue are given below.

1. Define the JMS Server. While defining, please select 'DefaultServer' as Target in the second page of the wizard.


2. Define the JMS Module. Please select 'DefaultServer' as Target in the second page of the wizard.


Nest step is to configure Queue Connection Factory and Queue for the JMS Module that we defined just now.

3. Open JMS Module that you defined in the previous step and Configure Queue Connection Factory. The JNDI name used in this examples is 'jms/QueueConnectionFactory'. So you may need to provide the same name here as well.

4. Open JMS Module that you defined in the step 2 and configure Queue. The JNDI name used in this examples is ' jms/testQueue'. So you may need to provide the same name here as well. While defining Queue, please select a subdeployment (if available) on the second page of the wizard. Please create new one, if nothing is listed in the drop down. If you are creating a subdeployment, then use the JMS server that you defined in Step1 as the target for deployment.


A glance at the implementation

The application has 3 model projects.

1. EJBModel - This contains Message Driven Bean(MessageDrivenEJBBean) implementation. The MessageDrivenEJBBean::onMessage(...) uses 'AuditAppModel' for updating DEPARTMENTS_AUDIT table.

2. AuditModel - This is for populating audit table. This is used from the MessageDrivenEJBBean.

3. Model - This contains Department EO and VO used by the UI.
This example has overridden DepartmentsImpl::afterCommit() for sending the audit data(Original values of Department entity - values before user update) to the queue that we configured. The class model.queue.QueueHandler does the queuing of audit data. The MessageDrivenEJBBean listens on the same Queue, so whenever data is put on this queue, MessageDrivenEJBBean gets notification and it's onMessage methods invoked. The auditing logic resides in this method.


How To run this sample?

1. Run test.jspx
2. Modify Departments attribute values and click on Commit.
3. Query the values from DEPARTMENTS_AUDIT using SQL Worksheet. You may notice new entries holding the previous values of the modified Departments record in this table.




Learn More ...

There are a lot more points like this. If  you are curious to learn the internals of the ADF Business Components and ADF Binding Layer,  the following book is for you - Oracle ADF Real World Developer’s Guide.
More details about this book can be found in this post- http://jobinesh.blogspot.in/2012/07/pre-order-your-copy-of-oracle-adf-real.html

Friday, April 8, 2011

Initializing your Fusion Web Application Parameters at startup

If you need to initialize specific business data during application start up,this post is for you.

Download

You can download the sample workspace from here.
[Runs with Oracle JDeveloper 11g R1 PS3 + HR Schema]

A glance at the implementation

This example make use of a custom javax.servlet.ServletContextListener for performing the application initialization during deployment. Please see 'view.ContextInitializer' class in the attached sample workspace, which act as the ServletContextListener in this example. btw, check out this post How to work with ServletContextListener if you not familiar with this approach.

This example invokes ApplicationModule and populates the ServletContext attributes from the custom ServletContextListener's ContextInitializer::contextInitialized(...) method. Please note that ADFContext.getCurrent().getApplicationScope() in a web app refers the ServletContext. It means that whatever you set to ServletContext, can be retrieved later by calling ADFContext::getApplicationScope() from the Model layer. You can use EL #{applicationScope.someVariable} if you need to refer ServletContext attributes from the UI.

The above example initializes a default DepartmentId during application start up and the same is used in the the Employees View Object query using groovy expression to get the employees (default DepartmentId is configured as context-param in the web.xml)

Displaying ClobDomain on a page

There is a default JSF converter(oracle.genericDomain) available for most of the oracle.jbo.domain types( not a new stuff, this has been available since 11.1.1.1.0). The converter-class for 'oracle.genericDomain' is oracle.adfinternal.view.faces.convert.GenericDomainConverter. This precofigured converter comes along with ADF binaries, so you just need to use it wherever required :)

Example :
 <af:inputText value="#{bindings.ClobContent.inputValue}"  
       label="#{bindings.ClobContent.hints.label}"  
       converter="oracle.genericDomain"  
       required="#{bindings.ClobContent.hints.mandatory}"  
       columns="#{bindings.ClobContent.hints.displayWidth}"  
       maximumLength="#{bindings.ClobContent.hints.precision}"  
       shortDesc="#{bindings.ClobContent.hints.tooltip}"  
       id="it2" rows="5">  
   <f:validator binding="#{bindings.ClobContent.validator}"/>  
 </af:inputText>  

Wednesday, April 6, 2011

Throwing ValidationException from the beforeCommit() logic !

If you have some validation logic added to beforeCommit() of the ApplicationModuleImpl or EntityObjectImpl and you face strange rollback behavior(Entity Objects are losing the modified data) while committing data second time after correcting the validation error, then this post is for you :)

From Developer's Guide....
8.5.2 How to Validate Conditions Related to All Entities of a Given Type

If your beforeCommit() logic can throw a ValidationException, you must set the jbo.txn.handleafterpostexc property to true in your configuration to have the framework automatically handle rolling back the in-memory state of the other entity objects that may have already successfully posted to the database (but not yet been committed) during the current commit cycle.

Java Option : -Djbo.txn.handleafterpostexc=true

Tuesday, April 5, 2011

Programmatically resetting the <af:query> and search result table

If you want to programmatically reset QueryModel of <af:query> component, you can use the following code snippet in your managed bean.

     RichQuery queryComp = getRichQuery();  
     QueryModel queryModel = queryComp.getModel();  
     QueryDescriptor queryDescriptor = queryComp.getValue();  
     queryModel.reset(queryDescriptor);  
     queryComp.refresh(FacesContext.getCurrentInstance());  

Along with the above requirement, If you need to reset the contents of result table as well, then call executeEmptyRowSet() on the ViewObject which is wired to the result table.

Download
I'm attaching simple application illustrating the above discussed points. You can download the sample workspace from here.
[Runs with Oracle JDeveloper 11g R1 PS3 + HR Schema]

A glance at the implementation

This example displays a popup with a search panel and result table. The custom popupFetchListener method added for the af:popup component resets the search panel and result table contents before displaying the popup.



Learn More ...
There are a lot more points like this. If  you are curious to learn the internals of the ADF Business Components and ADF Binding Layer,  the following book is for you - Oracle ADF Real World Developer’s Guide.
More details about this book can be found in this post- http://jobinesh.blogspot.in/2012/07/pre-order-your-copy-of-oracle-adf-real.html