Spring and Oracle ADF – registering a POJO Spring JDBC based Business Service as Data Control

In several previous posts, I have wondered about the potential for combining the ADF Binding Framework and the IDE capabilities around ADF in Oracle 10g JDeveloper with the Spring Framework. I concluded rapidly that the most meaningful way forward would be to implement a Business Service based on DAO interfaces and DAO implementation classes, potentially using either Hibernate or Toplink or – as in this case – plain JDBC made easy by Spring JDBC Templates, and registering that service as a DataControl with the ADF Binding Framework. Once that DataControl is registered, it cannot be discerned from other POJO based DataControls and it can be used just as easy as in most cases as the ADF BC based Data Controls. That means: rapidly developing web applications, largely through dragging and dropping Data Controls and their components in a WYSIWYG style visual editor becomes a reality.

In a previous post – Introducing Spring JDBC – frequently the best introduction of Spring in an organization – I have introduced Spring’s support for JDBC. In this same article I have demonstrated the HrmService, a simple business service that implements this interface:

package nl.amis.spring.hrm;
import java.util.List;

public interface HrmService  {
  public void setEmployeeDao(EmpDAO employeeDAO);
  public List getAllEmployees();
  public long getSalarySum();
}

The article goes on to describe the implementation of the HrmService – the HrmServiceImpl class that is injected with a DAO, the EmployeeJdbcDAO class that implements the EmpDAO interface and extends the JdbcDaoSupport class, the Spring JDBC template, that needs a DataSource to be injected. With the HrmServiceImpl requested from the Spring BeanFactory, all dependencies get injected and the service object can deliver on its contract – the interface. The DataSource in this example connects the DAO class with the traditional SCOTT schema in an Oracle Database.

To build an ADF Web Application on this Hrm Business Service, we need to register HrmService (or HrmServiceImpl) as Data Control. There are several challenges here. Normally, a client for the Hrm Business Service would use the Spring BeanFactory to get hold of a HrmService object. The client would only know about the interface, not actual implementation class. Typical code in the client looks like:

    // initialize Bean Factory, based on the configuration file SpringConfig.xml in the classpath (at the /class root)
    ApplicationContext ctx = new ClassPathXmlApplicationContext("SpringConfig.xml");
    HrmService hrmService = (HrmService)ctx.getBean("hrmService");

The SpringConfig.xml file would be something like this:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
    "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
  <bean id="dataSourceDBDirect"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource"
        destroy-method="close">
    <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
    <property name="url" value="jdbc:oracle:thin:@localhost:1521:orcl"/>
    <property name="username" value="scott"/>
    <property name="password" value="tiger"/>
  </bean>
  <bean id="employeeDAO" class="nl.amis.spring.jdbc.EmployeeJdbcDAO">
    <property name="dataSource">
      <ref local="dataSourceDBDirect"/>
    </property>
  </bean>
  <bean id="hrmService" class="nl.amis.spring.hrm.HrmServiceImpl">
    <property name="employeeDao">
      <ref local="employeeDAO"/>
    </property>
  </bean>
</beans>

However, the Application Development Framework knows nothing about the Spring Framework. It does not have a clue that I want it to instantiate an HrmService object through the Spring Bean Factory. So if I do nothing about it, it would simply instantiate the HrmServiceImpl bean – and not the interface because it cannot be instantiated – as it does all objects. This is the point where I got stuck. Earlier this week, when I did a Spring presentation for the Dutch Oracle Consulting Professional Java Community, I explained the above to my audience. And it was there that Steven Davelaar, ADF guru – besides many other things – pointed out the Factory Class property that ADF sets for each Data Control.

The Factory Class is the class that ADF calls upon to instantiate the Bean underlying the Data Control. I need to implement my own Factory Class, one that is Spring aware, and Bob’s your uncle!

Implementing and Registering a Factory Bean class

Registering a Data Control based on a Class in JDeveloper is dead easy. Just select the class and pick Create Data Control from the right mouse button menu. The Data Control is created and displayed in the Data Control palette. Note: you cannot create a Data Control based on an Interface!
Spring and Oracle ADF - registering a POJO Spring JDBC based Business Service as Data Control ADFSpring CreateDataControl
What really is happening behind the scenes: an entry is added to the DataControls.dcx file. This entry refers indirectly to a Data Control definition file that is created for the Class that is registered; this file has the same name as the Class with the extension xml. So for a Data Control registered for class HrmServiceImpl, we get a file HrmServiceImpl.xml. This latter file defines the attributes and operations that are available on the data control.

By the way, now is a good time to define the BeanClass for the AllEmployees attribute in the DataControl. This can be done in property palette and is reflected in the HrmServiceImpl.xml. This is the way to tell ADF that the objects returned in List that getAllEmployees returns are actually Employee objects.
Spring and Oracle ADF - registering a POJO Spring JDBC based Business Service as Data Control SpringADF SetBeanClass
I also decide to define a parameter for our Data Control. The parameter – beanName – is used later on to instruct the Factory Class which Bean from the Spring Bean Configuration file to instantiate.
Spring and Oracle ADF - registering a POJO Spring JDBC based Business Service as Data Control SpringADF DataControls

When you inspect the properties of the HrmServiceImplDataControl, you see the Factory Class property, set by default to oracle.adf.model.generic.DataControlFactoryImpl.
Spring and Oracle ADF - registering a POJO Spring JDBC based Business Service as Data Control AdfSpring factoryclass

We can easily take a look at the source for this class: To go to the source of the factory class, simply press CTRL – and enter the fully qualified class name:
Spring and Oracle ADF - registering a POJO Spring JDBC based Business Service as Data Control SpringADF gotoJavaSource

There is one line of code in this Factory Class that is responsible for instantiating the HrmServiceImpl object:

         Object bean = oracle.jbo.common.JBOClass.forName(beanClass).newInstance();

It is very simple to create our own Factory Class and replace that line of code with logic that uses the Spring Bean Container to instantiate the HrmServiceImpl. We create a new class nl.amis.adfspring.SpringBeanDataControlFactoryImpl that implements interface DataControlFactory. We start out by copying all code from oracle.adf.model.generic.DataControlFactoryImpl and then we replace the ‘offending’ line of code with:

         String beanName = (String)parameters.get("beanName");
         ApplicationContext springCtx = new ClassPathXmlApplicationContext("SpringConfig.xml");
         Object bean = springCtx.getBean(beanName);

We also need to add a couple of imports of Spring classes:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

You can download the nl.amis.adfspring.SpringBeanDataControlFactoryImpl class here: SpringBeanDataControlFactoryImpl.java

For some reason, the property inspector does not let us specify the Factory Class (perhaps because my Factory Class is in another project?). So I go outside JDeveloper and edit the DataControls.dcx file in a Text Editor. While I am at it, I also change the the Id, Definition and BeanClass properties: instead of referring to the implementation class HrmServiceImpl, I want to only refer to the interface HrmService. It is a simple Replace All of HrmServiceImpl with HrmService.

I also rename the file HrmServiceImpl.xml to HrmService.xml.

Spring and Oracle ADF - registering a POJO Spring JDBC based Business Service as Data Control SpringADF HrmService

Now it is time to make use of this Data Control. I create a new web project, HrmWebClient. In this project, I create a new struts-config.xml file (from the File, New, (filter by all items), Web Tier Node, Struts, Struts Controller Page Flow).

The next step: drag and drop a Data Page from the Components Palette. Call the Data Page StaffSummary. Double click the Data Page and set the Page Name to staffSummary.uix. Now the Visual Page Editor is opened. Drag and drop the allEmployees attribute in the HrmServiceDataControl as Read Only Table to the page. This creates the UIX logic for displaying the table layout with the Employee details as well as the UIModel that binds the DataControl to the Data Page.
Spring and Oracle ADF - registering a POJO Spring JDBC based Business Service as Data Control AdfSpring uimodelStaff
A reference to this page level UI Model is included in the Data Binding container, DataBindings.cpx. Annoyingly enough, the Factory Class property is set to the default value of oracle.adf.model.generic.DataControlFactoryImpl. Once again, I fire up a text editor and change this value to nl.amis.adfspring.SpringBeanDataControlFactoryImpl. In my case, I also had to manually add the parameter beanName to the DataBindings.cpx file.
Spring and Oracle ADF - registering a POJO Spring JDBC based Business Service as Data Control SpringAdf DataBindings

Now I can run the Data Page from the struts-config.xml. The result is most gratifying: my Spring powered HrmService backed DataControl has been used to provide employee details and these are displayed in a sound table layout, with even the Pagination taken care of: first 1-10 of 15 are shown. Note that after writing our Business Service and registering the HrmServiceDataControl using the customized Factory, I did not have to write any code at all to achieve this result!
Spring and Oracle ADF - registering a POJO Spring JDBC based Business Service as Data Control

This conclusively shows the success of integrating Spring and ADF. An visual overview of what we have achieved in combining ADF and Spring is seen here (sorry it is only a dining table sketch):
Spring and Oracle ADF - registering a POJO Spring JDBC based Business Service as Data Control adfspring 12aug2005

Getting started with the Spring and ADF Application

Download JDeveloper Application Workspace: SpringAndADF.zip

It contains two projects: SpringJDBC and HrmWebClient. Both projects need access to the libraries for the Spring Framework.

So download the Springframework from SourceForge: http://sourceforge.net/project/showfiles.php?group_id=73357; choose Download spring-framework-1.2.4-with-dependencies.zip;this will download a zip-file that contains all jars of Spring and related libraries such as Log4J and CGLib. Unzip the zipfile in the directory of your choice.

Start JDeveloper 10.1.2. Open the SpringAndAdf application workspace. Edit the SpringJDBC project. Go to the Libraries node. Create a new library SpringFramework. Have the classpath of this library refer to all jars you have just extracted from the Spring-with-dependencies-download. For example:

C:/spring/dev/repository/springframework/springframework/jars/spring-1.2.1.jar;C:/spring/dev/repository/springframework/springframework/jars/spring-mock-1.2.1.jar;C:/spring/dev/repository/apache/commons-logging/jars/commons-logging-1.0.4.jar;
C:/spring/dev/repository/apache/log4j/jars/log4j-1.2.9.jar;C:/spring/dev/repository/cglib/cglib/jars/cglib-full-2.1.jar

Add this library to the SpringJDBC project as well as the HrmWebClient project.

Set up database connection details

The HrmWebApplication attempts to connect to a SCOTT schema that contains an EMP table. The precise connection details such as database location and name and username and password are defined in the SpringConfig.xml file – the Spring Bean Configuration file – that you can find in the SpringJDBC project under Application Sources. Edit these configuration details to suit your environment.

Run the application

You can click on the struts-config.xml file – under Web Content WEB-INF in the HrmWebClient project.

Extra/Miscellaneous/Next Steps

Reference on Steve Muench’s weblog: http://radio.weblogs.com/0118231/2005/09/06.html

Spring Sighting on the Spring site: http://www.springframework.org/node/162

16 Comments

  1. kunal August 25, 2011
  2. Paul July 23, 2009
  3. Andrew November 6, 2008
  4. San Shet April 28, 2008
  5. Alvaro Coronel October 29, 2007
  6. Alvaro Coronel October 29, 2007
  7. Jb Mos July 18, 2007
  8. Robert Willems of Brilman March 22, 2007
  9. Olivier March 16, 2007
  10. Tom Moore July 6, 2006
  11. PieterB December 23, 2005
  12. Spring Abuser October 20, 2005
  13. Lucas Jellema September 14, 2005
  14. Giuliano September 13, 2005
  15. Lucas September 7, 2005
  16. Steve Muench September 6, 2005