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

16

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!

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.

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.

When you inspect the properties of the HrmServiceImplDataControl, you see the Factory Class property, set by default to oracle.adf.model.generic.DataControlFactoryImpl.

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:

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.

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.

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.

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!

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):

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

Share.

About Author

Lucas Jellema, active in IT (and with Oracle) since 1994. Oracle ACE Director for Fusion Middleware. Consultant, trainer and instructor on diverse areas including Oracle Database (SQL & PLSQL), Service Oriented Architecture, BPM, ADF, Java in various shapes and forms and many other things. Author of the Oracle Press book: Oracle SOA Suite 11g Handbook. Frequent presenter on conferences such as JavaOne, Oracle OpenWorld, ODTUG Kaleidoscope, Devoxx and OBUG. Presenter for Oracle University Celebrity specials.

16 Comments

  1. Hi, this is a great example and I got this working after some head scratching with ADF Faces in JDev 10.1.3.4. I’ve also implemented calling a Spring service method via specifying org.springframework.web.jsf.DelegatingVariableResolver in the faces-config.xml. I’m wondering which is the best method to integrate Spring. Via faces-config.xml is much easier than the DataControl route but drag/drop on the DataControl onto a page gives more UI options. Thanks for the work you did on this. Much appreciated.

  2. I was incredibly excited to find this post, however in Jdeveloper 11 the DataControlFactoryImpl.class looks completely different. I have seen other posts about a plugin for Jdeveloper and Spring now? Any comments on this?

  3. I wanted to find out whether I can use Spring Framework instead of ADF (Full) Frameork with ADF Rich components and Trinidad. If yes, how much work is involved as compared to using ADF Framwork. Does Spring Framework intergrate smoothly with ADF Faces Rich Components?

  4. Alvaro Coronel on

    I think this excellent example is a bit outdated. I am trying to learn from it, but so far haven’t managed to make it work.

    I haven’t managed to find any “UIX” files. my DataBindings.cpx file looks very different from the one provided in the example. I am getting errors and I am not quite sure what’s wrong…

    Has anyone tried this with JDeveloper 10.1.3.2.0.4066 ?

    I am stuck. If anyone can offer any pointers to possible solutions I will be most grateful.

    Best regards and thanks in advance,
    Álvaro.

  5. Alvaro Coronel on

    Hello there. This article’s great, but just when I was ready to run my JSP page…

    I am getting a “Error(113,19): constructor DCGenericDataControl() has protected access in class oracle.adf.model.generic.DCGenericDataControl” when compiling.

    Apparently, the “offending” line is right at the end of SpringBeanDataControlFactoryImpl.java

    Do you know another way of getting hold of a DCGenericDataControl ?

    Thank you in advance

  6. Have you had a chance to look into doing this on JDeveloper 11g. I have tried without success.

  7. Thanks for the example! Seems to be working like a charm…

    Any ideas about screens that need to do updates? The returned objects from the DAO aren’t smart enough to know they are editted and need to be put through the DAO again to be processed by hibernate. In BC4J this all is done by the datacontrol and the viewobject…

  8. Which version of Oracle software did you use to register the Spring Business Service as Data Control ? I tried to deploy the project on the latest version of JDev and OAS (10.1.3) but I have many different issues.

  9. Lucas — Thanks for writing this! It’s a nice, straightforward example of how ADF can collaborate with Spring. I have a couple of questions. 1. Lifetime? I don’t quite understand how ADF will handle instantiation of the HrmServiceImpl class — will it be re-instantiated each time there is a reference to the service, or can I set the IsPersistent property and avoid the startup costs associated with recreating the class? 2. Pooling? Is the service in essence a singleton or can I create a pool of HrmService objects? (Sorry if my EJB influence is showing). HrmService looks pretty stateless to me.

    Thanks again for your help!

  10. Giuliano,

    You specify the beanClass property through JDeveloper, in the property editor for the HrmService.xml file – file created for the HrmService data control (it is shown in the article by the way).

    This tutorial does not cater for CUD, it only does Retrieve/Read. However, the only reason for that is that no setters are available through the business service/data control. There is no reason why CUD could not be implemented.

  11. In the part when I need say to ADF the list of AllEmployees is Employee. How I do it? In jDeveloper or in text editor??
    And more, can I do a CRUD interface with this Tutorial or only a ReadOnly Table?

    Thanks