Developer: Java
   DOWNLOAD
 Oracle TopLink
 Oracle JDeveloper 10g & ADF
 
   TAGS
java, adf, All
 

 

Learning Oracle ADF: A Beginner's Story


by Tom Moore

 

This case study offers a brief but detailed introduction to UI development using Oracle ADF.

Published November 2006

The organization I work for, the University of Wisconsin - Eau Claire, recently ported part of a legacy system written in Cobol and running on a Unisys mainframe to a Java/J2EE environment. Our IT team primarily uses open source tools, including Eclipse, Tomcat, and Spring, to write our applications. However, a change in licensing opportunities made it possible for us to consider Oracle tools, so an application was written to test a development model using Oracle JDeveloper 10g and the Oracle Application Development Framework (ADF). In this article, I'll describe this application, which offers a quick but detailed introduction to UI development using Oracle ADF. (For more in-depth technical information, you'll want to consult Oracle's ADF Tutorial and the Oracle ADF Developer's Guide on Oracle Technology Network, Oracle ACE Steve Muench's excellent ADF columns in Oracle Magazine , and the comments in the Oracle JDeveloper discussion forum.)

In this article, I will not describe every single step needed to build our application—but hopefully there's enough information here for you to make your own decisions. A good understanding of Java is assumed, as well as general familiarity with either Eclipse or JDeveloper and common programmer savvy. (I won't tell you to Save All after each step.)

It's always tough to switch from a given architecture and development model, so first I'll offer some background about the value we found in JDeveloper and ADF that justifies a change. I'll also describe some "Tips and Gotchas" in a separate section.

Background

Our current applications are a straightforward use of Java (JDK 1.5), Spring (MVC, Web Flow, JDBC Template), and JSP pages. Our data is replicated to an Oracle Database 10g Release 2 database. Although we have some experience using JBoss Hibernate as an object-relational mapping (ORM) tool, our data access is not complex or dynamic and the Spring JDBC Template has proven sufficient for our needs.

As we are using Oracle Database 10g Release 2 in production, it seemed natural to use Oracle Database 10g Express Edition (XE)—the free starter edition of Oracle Database—in a Windows-based trial in which we wanted easy access to the data. It also made sense to use an Oracle database after we decided to use Oracle's TopLink ORM tool, so that we could easily port our mapping to a different data source if we decided to use the test case as the basis for a production application. The only change we made to XE was to change the system global area (SGA) from the system home page so that it uses less memory.

Our choice of TopLink turned out to be more of an advantage than we expected, as it abstracts away many of the troublesome details of using JDBC or even an interface like Spring's JDBC Template. (A fine example of using the JDBC Template without the need to configure a complex Spring environment can be found in Dustin Marx's "Add Some Spring to Your JDBC Programming" on OTN.) JBoss Hibernate is an excellent ORM tool, but the ability to integrate a TopLink mapping in an end-to-end ADF solution proved to be eye-opening and very productive.

As recommended by Oracle, we created an EJB session bean to encapsulate access to the database. It only makes sense to have a layer between the application and the database in which business logic can be employed as needed. There is nothing in our applications thus far that requires state, as is typical with use of the Session Façade design pattern.

A standard step in using ADF is to create a set of data controls that provide access to data repositories, and we followed suit. In circumstances in which business logic is not needed, as is often the case with queries and sometimes with updates and deletes, it can be very productive to directly access information from a UI control. In more complex cases, business rules can be applied by including their implementation in session bean methods. (Chapter 1.1 of the ADF Developer's Guide offers an excellent explanation of how ADF and JSF work hand-in-hand to provide a productive development environment.)

We also created a set of jspx files, using standard JSF navigation. Although we have experience with Struts, we chose to avoid it here. jspx files were used because this is a recommended practice from Oracle.

JDeveloper 10.1.3 was our IDE throughout the project. This may appear obvious, but to forget to mention it would be to fail to recognize the fundamental role that JDeveloper plays in building ADF applications. JDeveloper is a Java development tool, but it also gives you end-to-end access to the functionality of TopLink, ADF UI and data access controls, JSF functionality, and XML and JSP files, as well as a test deployment and database access environment.

Simultaneous, coordinated changes in windows take place automatically as you edit a file in JDeveloper, to a degree not typically seen when using Eclipse and other IDEs. Those changes provide various views and editing approaches, so learning to use JDeveloper's rich functionality can be a little tricky. You need to learn where to double-click, when to use the Structure and Data Control windows, when to set values in the property editor, and, occasionally, when to use the Source window and write Java or JSP code.

The Project

Our project is a simple banking account for student organizations. The basic functionality needed is to enter and edit expenditures and deposits. The project is by and large a subset of the functionality described in the ADF Tutorial, so those familiar with SRDemo will recognize many of its elements. The SRDemo is a complete sample application that you can download through the "Check-for-update" mechanism in the Help pulldown menu of JDeveloper. It comes with the complete source code, and the ADF Developer's Guide uses it to explain how to develop with ADF.

Phase 0: Initial Setup for Building an Application

Step 1: Create a new Web application using the JSF, EJB, and TopLink template. (See also Oracle ADF Tutorial 1-10.) There is nothing critical about the choice of template; all options are available even if they are not chosen initially. With this template, the standard Model and ViewController projects are created, holding the back and front ends of the application. Setting the default package name is worthwhile because it is used as the default in JDeveloper as Java classes are created.

Step 2: Set up the database. Although we were tempted to try the Offline Database Objects in the Database Tier in the New Gallery, we had previously created the SQL needed to create a schema with a set of three tables. We logged into the XE Web interface and used the script that you can find in the references on the SQL Command page to create our user and tables. It is important to specify the primary keys for these tables, at a minimum, so that TopLink has sufficient information to generate a more complex mapping:
  • Account holds basic information about the accounts.
  • Details is used for transaction information for all accounts.
  • Manager is a listing of individuals associated with the accounts.

To populate the tables, we wrote a Java application that uses DBUnit and used the data set found in the references to create a CampusAccounts.xml file. You can clone the XML elements to extend the data set if you wish. Since it was the first Java class in the application, we chose to create a New/General/Java class in the Model project so that a package would be created. The body of the class was then copied and pasted to the newly created class. If a class had already been created, we would have chosen the File/Import option to copy the Java Source into the Model project.

public static void main(String[] argv) throws Exception {
        Class driverClass = Class.forName("oracle.jdbc.OracleDriver");
        Connection jdbcConnection = 
            DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:XE", 
                                        "CampusAccounts", "xxxxxx");
        IDatabaseConnection connection = new DatabaseConnection(jdbcConnection);

        IDataSet dataSet = new FlatXmlDataSet(new File("c:\\CampusAccounts.xml"));

        try {
            DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet);
        } 
 finally {
            connection.close();
        }
     }
The body of the main snippet illustrates how easy it is to leverage DBUnit functionality. The URL needed to connect to an Oracle Database XE database is shown, as is the name of the standard Oracle JDBC driver.

However, you need to add a jar file from DBUnit and a library to the project so that the code can compile. First, you will need to extract the jar file from the DBUnit zip file ( download). From the Properties of the Model project, choose Libraries/Add Jar and add the DBUnit jar file to the project. By choosing Libraries/Add Library from the Model properties, you can add the Oracle JDBC Library, which includes the Oracle JDBC driver. Once the jar file and library are available, the needed imports can be added automatically using the Alt-Enter code completion option of JDeveloper. (Make sure you import the java.sql.Connection.) To run this Java application, choose the file in the Applications window and the green arrow. Once a connection has been created (the next step), it's easy.

Phase 1: Creating an Initial Model

Step 3: Create the TopLink object-relational mapping after the schema is created. (See also Oracle ADF Tutorial 1-8 and 2-2.) A connection needs to be created prior to using TopLink. Choose View/Connection Navigator if the window is not open. Choose New Connection on Database, give it a name, and make sure that an Oracle (JDBC) type is chosen. Enter the schema name and password. Change the sid to XE if you are using XE. Test the connection to ensure success.

Returning to the Applications window, right-click the Model and choose New/Business Tier/TopLink/Java Objects from Tables. Name the map and change the connection if necessary. Query the schema in Step 2 and shuttle all of the tables. Check the package name. A set of data access objects (DAOs) are created in the package that you specify, one for each table. You can see the field mapping for each of the schema tables by opening the map in the TopLink folder of the Model. If you choose a table in the Structure window, the Map window changes to show a different view of the mapping. It is a good practice to try this type of thing, because it gets you used to seeing how JDeveloper coordinates changes in windows.

Choose the Queries tab in the Detail map, and notice that a findAll query is created automatically by TopLink for each table. To add a simple query that will yield a smaller subset of table rows, in the Named Queries choose Add and name your query. Then choose Add in the Parameters window, set the parameter type by specifying the fully qualified Java class name (such as java.lang.Integer), and click the Name column to change the parameter name. Choose Add for each of the parameters you need to specify. In our case, we added findAccountTransactionsByAccount() to the Detail mapping to retrieve all rows for a particular account based on the account number, and findOneTransaction() to the Detail mapping to retrieve a particular transaction, which is distinguished by an account number, sequence number pair. findOneTransaction() is a ReadObjectQuery, while findAccountTransactionsByAccount() is a ReadAllQuery:

If you choose the Format tab, you can use the TopLink editor to create a query or enter a query string. To use the query expression builder, choose Expression/Edit. In the builder, choose Add and change the Second Argument to the parameter you just created. If you have a second parameter, choose Add again and the builder will automatically create an AND clause:

A default TopLink session is created, corresponding to the connection that was created. You can open the sessions.xml file and examine the defaults by choosing Default in the Structure window. Changing an option such as logging is very easy.

Step 4: Create an EJB session. (See also Oracle ADF Tutorial 2-14.) From the Model, choose New/Business Tier/EJB/Session bean. The defaults are good, except that a Remote Interface is not needed. When you examine the bean created, you will see that merge, persist, refresh, and remove methods are created that can be used on table rows. You will also see that a method was created for each of the named queries that you created. The Java code generated automatically by the EJB wizard creates a session transaction within the method. Of course you are free to edit this code, and the text for an existing method will not be overwritten if that is your preference when you regenerate the bean.

Step 5: Create ADF Data Controls. (See also Oracle ADF Tutorial 2-16.) ADF Data Controls provide an interface to the session bean and DAOs created in earlier steps. When it is time to create user interface controls that access the database, the data controls provide the glue. If you open the Data Control palette from the View menu, it should be empty. Right-click the session bean and choose the last entry: Create Data Control. The Data Control palette will populate:

Your initial work at creating a viable back end for the project is complete. It is amazing how little effort it takes, although the real value of artifacts like the ADF Controls won't be apparent until you build some pages.

Phase 2: Creating an Initial View

Step 6: Work with the initial faces-config.xml file. Open the Web Content/WEB-INF folder and the faces-config.xml file that was created when the application was originally opened. If the config file has not been created for some reason, right-click the ViewController project and choose New/Web Tier/JSF/JSF Page Flow & Configuration. The default name of faces-config.xml is correct. If you choose the Overview tab, you see an empty view of the config file:

This view is an important window into the central configuration file in a JSF application. Besides a set of XML elements that describe how a user transitions from one page to another in a Faces application, there are also elements for beans whose methods are called as users interact with interface controls (backing beans) and for beans used to store longer-lived information such as the user state.

Step 7: Create an initial navigation map and corresponding pages. (See also Oracle ADF Tutorial 3-2.) An initial set of pages, largely based on the page template used in the SRDemo, is now required, as well as initial navigation rules that describe how users transfer from one page to another. Choose the Diagram tab for the faces-confix.xml file, make sure the Configuration palette is open, and drag a JSF Page onto the configuration diagram. At this point you have created a navigation element in the configuration file, but not the page.

Double-click the untitled page icon to start a wizard that will actually create the file. Change the name to index; change the file suffix to jspx (generally recommended by Oracle); make sure that UI components are not automatically exposed for this page; and shuttle over the ADF Faces Components, as well as JSF Core, the ADF Faces, and the JSF HTML libraries. Choose the HTML version preferred—we had good luck with 4.0.1 Transitional.

The index.jspx is going to redirect to the real first page in the application, the standard practice in creating Web applications. It may set a bad precedent, but go to the Source window for index.jspx and replace the f:view tag with the following scriptlet. Note that you are creating a directory structure to manage the pages—administration holds all pages for all applications, while a subdirectory called campusaccounts will be used to keep all of this project's pages. You can safely save and close the file after the scriptlet is inserted into the index.jspx file.

<jsp:scriptlet>
      response.sendRedirect("faces/administration/welcome.jspx");
</jsp:scriptlet>

From the Component palette, drag another JSF page onto the faces-config.xml diagram. Double-click the icon to open a wizard to set up its jsp page. Change the name to welcome and the file type to jspx—and be sure to set the directory to create any subdirectory structure you wish to create. For example, in our case, we set the directory to ../public_html/administration so that it corresponded to the sendRedirect. The shuttle options should be the same as those used in creating index.jspx, and the HTML version should also be the same.

Adding content to a JSP page is a matter of getting used to switching between the Diagram, Property Editor, and Structure windows. Sometimes you even edit the page from the Source window. In the Structure window, open the faces view element and delete the default f:view tag so that it can be replaced by the text below that includes a panel page. A panel page is a complex UI control with a header, global controls for choosing help, a footer, a copyright section, and so on. As you use this page, you will appreciate how easy it is to create a complex page using a standard interface component. The easiest way to paste the text is to switch to the Source window, making sure that your cursor follows the three jsp tags found at the beginning of the file.

<f:view>
    <f:loadBundle basename="edu.uwec.financial.resources.UIResources"
                  var="res"/>
    <af:document title="#{res['uacct.campusAccounts.title']}"
                 initialFocusId="start">
      <af:form>
        <af:panelPage>
          <!-- Page Content End -->
          <f:facet name="branding">
            <af:panelHorizontal>
              <af:objectImage source="/images/banner.jpg"/>
            </af:panelHorizontal>
          </f:facet>
          <f:facet name="menu1">
            <af:menuTabs var="menuTab" value="#{menuModel.model}">
              <f:facet name="nodeStamp">
                <af:commandMenuItem text="#{menuTab.label}"
                                    action="#{menuTab.getOutcome}"
                                    rendered="#{menuTab.shown and menuTab.type=='default'}"
                                    disabled="#{menuTab.readOnly}"/>
              </f:facet>
            </af:menuTabs>
          </f:facet>
          <f:facet name="menu2">
            <af:menuBar/>
          </f:facet>
          <f:facet name="menuGlobal">
            <af:menuButtons>
              <af:commandMenuItem text="#{res['uacct.menu.logout']}"
                                  action="GlobalLogout" immediate="true"
                                  icon="../images/blafIcons/logout.gif"/>
              <af:commandMenuItem text="#{res['uacct.menu.help']}"
                                  action="GlobalHelp" immediate="true"
                                  icon="../images/blafIcons/help.gif"/>
            </af:menuButtons>
          </f:facet>
          <f:facet name="messages">
            <af:messages/>
          </f:facet>
          <f:facet name="appCopyright">
            <!--Copyright msg-->
            <h:panelGroup>
              <af:objectSpacer width="10" height="10" id="objectSpacer4"/>
              <af:panelHorizontal halign="center">
                <af:outputText value="#{res['uacct.copyright']}"/>
              </af:panelHorizontal>
            </h:panelGroup>
          </f:facet>
          <f:facet name="appAbout">
            <!-- The About this Application Link-->
            <af:panelHorizontal halign="center">
              <af:commandLink text="#{res['uacct.about']}" action="GlobalAbout"
                              immediate="true"/>
            </af:panelHorizontal>
          </f:facet>
          <f:facet name="infoUser">
            <!-- Show the Logged in user -->
            <h:outputFormat value="#{res['uacct.connectedUser']}"
                            rendered="#{userInfo.authenticated}" escape="false">
              <f:param value="#{userInfo.userName}"/>
            </h:outputFormat>
          </f:facet>
        </af:panelPage>
      </af:form>
    </af:document>
  </f:view>
 
You'll notice that there are errors reported in the Structure window; these will be fixed in the next step.

Step 8: Beg, borrow, and steal. (See also Oracle ADF Tutorial 4-2.) We recommend borrowing several files from the SRDemo as a rapid start to creating an application. Because it is so easy to use a resource file, we chose to consistently use it for labeling all UI components, even for seemingly obvious labels on buttons like Submit. We don't anticipate a need for i18n for our largely intranet applications, but it is a design decision that sets a good precedent. We also recommend borrowing classes like ADFUtils and JSFUtils because they are extremely useful. It is tricky when you have to access variables stored in the JSF and ADF frameworks from Java code. These utility classes make it much easier once you understand the binding and naming standards that Oracle uses; examples are shown below. Importing the images folder also makes sense, since it contains the browser look-and-feel (BLAF) folder and several icons used in the interface.

We imported the files by choosing the ViewController project, pulling down on File/Import/Java Source, and browsing to the classes in the SRDemo project. The full path in SRDemo is at the same level as mywork—look for samples/SRDemo/UserInterface/src/oracle/srdemo/view. Get ADFUtils and JSFUtils in util and copy them into your package structure (we used edu/uwec/financial/util in the src folder); ResourceAdapter and UIResources into resources (we used edu/uwec/financial/resources); and MenuItem, MenuModelAdapter, and MenuTreeModelAdapter from menu (we used edu/uwec/financial/menu). Notice that when you import the Java files, the package names will probably need to be corrected to match your directory structure.

You can import some standard images by choosing File/Import/Web Source, browsing to UserInterface/public_html, and choosing images—ensuring that you copy the images folder to your public_html folder. This step is a bit tricky, because it is easy to go too deep when browsing:

At this point, the errors in the jspx file should have disappeared, but there are elements of the panel page that need to be fixed, such as any images that are to be included in the branding facet. If you have an image you would like to include at the top of the page, import it as WebContent into the images folder in the ViewController project. In the panel page in the Structure window, choose branding/panelHorizontal, and reset its object image in the Properties window. It is also worthwhile to ensure that the icons on the page, like logout and help, have the correct Source property. Choose each icon and the ... button in the Source property in the Property window, and find the image in the Source window. You will need to edit entries like the loadBundle tag in welcome.jspx to match your package structure.

The sample properties file entries don't correspond to the properties referenced in welcome.jspx. Here is the set of starter name/value pairs that we added:

uacct.about=About MyBlugold
uacct.copyright=\u00a9 2006 University of Wisconsin - Eau Claire
uacct.menu.help=Help
uacct.menu.logout=Logout
uacct.menu.campusAccounts=Campus Accounts
uacct.campusAccounts.title=Campus Accounts
uacct.connectedUser=Logged in as <b>{0}</b>

The ADFUtils class does not compile, because it needs a library. Open the Properties on the ViewController project, and add the ADF Model Runtime library.

If you choose index.jspx and run the application (choose the green arrow), you should see something like the following—with your banner, of course.

<

The welcome page gives us buttons, such as Help and Logout, which are more "global" in nature because they can be chosen from any page.

Step 9: Create menus. This section describes code borrowed from the SRDemo to create menus, but it can be safely skipped if you want to keep the introduction simpler.

The tags and Java code needed to manage a top-level menu have been added to the project, but they need to be wired up by adding appropriate entries in the faces-config.xml file. Typically JDeveloper would make the entries for you, or you would open the faces-config.xml file and add the bean definitions from the Overview tab. An alternative is to copy the following examples into the file via the Source window, using the managed-bean elements as models for entries for similar beans (as noted in the bean comment tag).

<!-- Global menu tab for logout (we also added a tab for help -->
  <managed-bean>
    <managed-bean-name>menuItem_GlobalLogout</managed-bean-name>
    <managed-bean-class>edu.uwec.financial.menu.MenuItem</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
    <managed-property>
      <property-name>label</property-name>
      <value>#{resources['uacct.menu.logout']}</value>
    </managed-property>
    <managed-property>
      <property-name>icon</property-name>
      <value>/images/logout.gif</value>
    </managed-property>
    <managed-property>
      <property-name>type</property-name>
      <value>global</value>
    </managed-property>
    <managed-property>
      <property-name>viewId</property-name>
      <value>/administration/Logout.jsp</value>
    </managed-property>
    <managed-property>
      <property-name>outcome</property-name>
      <value>GlobalLogout</value>
    </managed-property>
  </managed-bean>

  <!-- Campus Accounts menu tab (we also added tabs for Purchasing and Accounting) -->
  <managed-bean>
    <managed-bean-name>menuItem_CampusAccounts</managed-bean-name>
    <managed-bean-class>edu.uwec.financial.menu.MenuItem</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
    <managed-property>
      <property-name>label</property-name>
      <value>#{resources['uacct.menu.campusAccounts']}</value>
    </managed-property>
    <managed-property>
      <property-name>viewId</property-name>
      <value>/administration/campusaccounts/CampusAccounts.jspx</value>
    </managed-property>
    <managed-property>
      <property-name>outcome</property-name>
      <value>GlobalCampusAccounts</value>
    </managed-property>
  </managed-bean>

  <!-- create the main menu menuModel bean, only one needed -->
  <managed-bean>
    <managed-bean-name>menuModel</managed-bean-name>
    <managed-bean-class>edu.uwec.financial.menu.MenuModelAdapter</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
    <managed-property>
      <property-name>viewIdProperty</property-name>
      <value>viewId</value>
    </managed-property>
    <managed-property>
      <property-name>instance</property-name>
      <value>#{menuTreeModel.model}</value>
    </managed-property>
  </managed-bean>

  <!-- create the main menu menuTreeModel bean, only one needed -->
  <managed-bean>
    <managed-bean-name>menuTreeModel</managed-bean-name>
    <managed-bean-class>edu.uwec.financial.menu.MenuTreeModelAdapter</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
    <managed-property>
      <property-name>childProperty</property-name>
      <value>children</value>
    </managed-property>
    <managed-property>
      <property-name>listInstance</property-name>
      <list-entries>
        <value-class>edu.uwec.financial.menu.MenuItem</value-class>
        <value>#{menuItem_GlobalLogout}</value>
        <value>#{menuItem_GlobalHelp}</value>
        <value>#{menuItem_CampusAccounts}</value>
        <value>#{menuItem_Purchasing}</value>
        <value>#{menuItem_Accounting}</value>
      </list-entries>
    </managed-property>
  </managed-bean>

  <managed-bean>
       <managed-bean-name>resources</managed-bean-name>
       <managed-bean-class>edu.uwec.financial.resources.ResourceAdapter</managed-bean-class>
       <managed-bean-scope>application</managed-bean-scope>
  </managed-bean>

Because the beans reference the resources file, the faces-config.xml file also needs an entry defined for it. In the application tag, add the following xml element (in addition to the default-render-kit):

<message-bundle>edu.uwec.financial.resources.UIResources</message-bundle>

Running the index.jspx file again should reveal a page that looks like the following (if you didn't add managed beans for purchasing, help, and so on, the page will display but only the Campus Accounts tab will display):

Step 10: Create the first input page. Your first "useful" page will be one that allows a user to enter an account number, the first step in managing an account. Open the faces-config.xml diagram and drop a new page onto it, and then double-click the icon to edit the page. In our case, we wanted to create another level to the organization for campusaccounts, so we created choose.jspx in /administration/campusaccounts (add \administration\campusaccounts after public html). We gave in to the temptation to let JDeveloper create a managed bean in the second step. The backing bean for choose.jspx will automatically be named Choose.java and will include fields for every user interface component. This means that every time you add a component as simple as a spacer, it shows up as a field with its own getter and setter. That makes sense if you want to write Java code to change the size of a property in the spacer, like its height, but it also creates a complex backing bean. In this case, it's easier to start from an automatically created backing bean when we want to understand what it does.

Using the Structure window, replace the with the view tag from welcome.jspx. In essence, you are using the welcome page as a template. In the Structure window for choose.jspx, you can delete the view tag, and then copy the view tag from welcome.jspx and paste it into choose.jspx. If you choose to make the change in the Source window instead, be careful to leave the html comment that indicates that the jspx has a backing bean—an Oracle convention that looks something like this:

<!--oracle-jdev-comment:auto-binding-backing-bean-name:backing_administration_campusaccounts_choose-->

Add a navigation rule to the faces configuration file that specifies that on GlobalCampusAccounts, the interface will navigate to the first page of the application. This is a global rule because you can change to CampusAccounts by choosing its tab from anyplace in any application. The tag entered in faces-config.xml is

<navigation-rule>    
   <navigation-case>
      <from-outcome>GlobalCampusAccounts</from-outcome>
      <to-view-id>/administration/campusaccounts/choose.jspx</to-view-id>
   </navigation-case>
</navigation-rule>

You may need to edit the logout and home icon properties in the commandMenuItems and change their icon value to use the gif file in the blaf folder.

You need to insert a PanelHorizontal inside the panel page to hold an input text component. Generally we have found it easier to do this from the Structure page, by right-clicking on the PanelPage element and choosing the ADF Faces Core components, although it can also be done by using the Component palette. Note that an ADF form encloses the PanelPage, so you already have your html form element. Add an InputText component to the horizontal panel. From the Properties window, change the label to use a property stored in the resources file. An easy way is to edit the resources file and add the property first; then double-click the inputText element in the Structure window, choose Bind to find res in the JSF Objects, and choose the property value you just entered. Once you are used to the EL format needed to access the resource, it is also easy to type the property value in the Property Editor. Add a CommandButton to the horizontal panel after the input text and change its name to use another property defined in the resources file—once again, we recommend using the Structure window.

Double-click the button to go to the backing bean. You need to get the text from the InputText component and put it in the user state so that it is always available when a user is accessing options in the CampusAccounts application.

public String commandButton1_action() {
        Object text = getInputText1().getValue();
         
        UserSystemState.storeCurrentAcctNum(new Integer(acctNum));

        return "list";    
    }

Notice that you avoided any validation her; instead you should use options in the Faces components to make validation unnecessary. If you set the required property on the InputText element, a user is forced to enter a value. If you insert a ValidateRegExp validator inside the InputText tag and add the pattern [0-9]+, the user will be required to enter at least one digit for an account number.

In our case, we added UserSystemState from SRDemo, using it as a model and creating static functions to save the account number. We also had to add userSystemState (with a lowercase u) as the name of a managed bean in the faces-config.xml file. You can infer the package name we used from the managed-bean-class field. One of the nice features of how JDeveloper manages the faces-config.xml file is that, if there are any problems with compiling code or specifying package names, you see a warning in its tag. JDeveloper actively parses xml configuration files.

<managed-bean>
        <managed-bean-name>userSystemState</managed-bean-name>
        <managed-bean-class>edu.uwec.financial.view.backing.UserSystemState</managed-bean-class>
        <managed-bean-scope>session</managed-bean-scope>
  </managed-bean>

Here is starter code for UserSystemState for creating the static function used in the Command button; it replaces all the fields and methods from SRDemo. Remember to check the package name:

private HashMap _settings = new HashMap();
    private static final String CURRENT_ACCT_NUM = "CURRENT_ACCT_NUM";

    public Integer getCurrentAcctNum() {
        return (Integer)_settings.get(CURRENT_ACCT_NUM);
    }

    public void setCurrentAcctNum(Integer acctNum) {
        _settings.put(CURRENT_ACCT_NUM, acctNum);
        System.out.println("setCurrentAcctNum:"+acctNum); // some old fashioned testing
    }

    public static void storeCurrentAcctNum(Integer acctNum) {
       
       JSFUtils.setManagedBeanValue("userSystemState.currentAcctNum", acctNum);
    }

    public static void retrieveCurrentAcctNum() {
        JSFUtils.getManagedBeanValue("userSystemState.currentAcctNum");
    }

There is a bit of magic in this code. userSystemState.currentAcctNum seems to be a reference to a field in UserSystemState, but there is no field of that name. The argument to the JSFUtils.getManagedBeanValue function call is resolved as a getter; thus, userSystemState.currentAcctNum results in a call to getCurrentAcctNum().

If you run index.jspx and choose the CampusAccounts tab, you should see an initial page for the application, similar to this one:

Step 11: Create a first ADF Data Control. (See also ADF Tutorial 5-2.) Here's where the fun begins and the value of ADF really begins to show. Having created a page that allows a user to enter an account number, now you want to fetch all the transactions associated with that number—all the Deposits and Expenditures on file.

Create a page called /administration/campusaccounts/list.jspx in the faces navigation diagram, and automatically expose UI Components when you edit list.jspx. Copy the view tag from welcome.jspx, as you did in Step 9.

You need to wire up the list page to display appropriate rows from the Detail table (or Detail DAO, depending on how you think of it.) Keep in mind that ADF Data Controls are central to building ADF applications, so open the Data Control palette.

You could drag-and-drop the findAccountTransactionsByAccount() function to the list page—in which case you would be able to create components like a Command button or a parameter form.

Instead you will drag-and-drop Detail onto the panelPage to create a set of rows that can be displayed from the Detail table—those having a common account number. Given a long list of components that make sense in this situation, choose Tables/ADF Read-only Table... The Action Binding Editor appears, so you need to take the value stored for the account number from the choose page and bind it as the value to be used for findAccountTransactionsByAccount:

Let's display all the fields and enable selection and sorting. You can also edit column properties to selectively enable/disable sorting and change the column headings by double-clicking each column in the Structure window. In our case, we added column headings to the resource file and then bound the HeaderText to references like #{res['uacct.campusAccounts.account']}. (You can also reset the text in the table's selection facet by double-clicking the component in the Structure window.) We found that the Bind button was our friend.

If you run the application and enter a valid account number, you hope to see something like this:

But after entering 1000 as the account number, you still find yourself on the choose page. You need to specify a transition from the choose to the list page. In the faces-config.xml file, open the Design window and make sure the Component palette is visible. Choose a JSF Navigation Case, and then click the choose and list pages—in that order. Edit the transition label and change success to list, corresponding to the value returned by the Command button action in Choose.java. Now data will be displayed on the list page.

Note how easy it was to display this table containing entries from our Detail database table. The interface component is an ADF JSF component, so you get a complex display object for free that includes features such as pagination (33 total results printed 10 at a time or all 33 rows if you have that many that match), banding (each row is a different color), sorting, and the ability to select an individual row via a radio button (for example, to be able to edit its contents). The look and feel of this page uses the default Oracle skin, but a different set of colors, fonts, and icons is possible by changing the CSS file used to specify the component tags.

Also, note how easy it was to make use of a sophisticated data control provided by ADF to painlessly access the data. When you dropped the ADF Read-Only table onto the page, the type of the helper object returned (Detail.java) was inferred and automatically used to create appropriate columns. Rowset behavior (10 rows at a time) is provided, as is type resolution based on metadata derived from the original database table.

Step 12: Create an edit page. (See also Oracle ADF Tutorial 10-2.) On the list page, the Table facet for selection contains an af:tableSelectOne tag with a Command button inside. If you double-click the Command button, you open the backing bean where you can call a helper function to fetch data to uniquely identify a row selected so that you can store it for retrieval in the next page.

public String commandButton1_action() {
        setCurrentTransactionIdFromRow();
        
        return "edit";
    }
The helper function in List.java looks like this:
private void setCurrentTransactionIdFromRow() {
     FacesContext ctx = FacesContext.getCurrentInstance();
     JUCtrlValueBindingRef tableRowRef = (JUCtrlValueBindingRef) this.getTable1().getRowData();
        
     Integer transId = (Integer)tableRowRef.getRow().getAttribute("cdPostSequence");
        
     UserSystemState.storeCurrentTransId(transId);
}

storeCurrentTransId() needs to be added to UserSystemState, using storeCurrentAcctNum() in Step 9 as a model.

Thus far, the changes needed to choose a row to be edited have been described. Now a new jspx page in which editing will take place needs to be created. In the faces-config.xml file, add a jsp page, and then edit it to name it edit.jspx. From the Component palette, choose a JSF Navigation Case and change the transition label in the Design window to edit to match the return value specified in the Command button.

Change to the edit.jspx page, and choose findOneTransaction/Detail from the Data Control palette and drag it onto the panel page using Forms/ADF Form. Include a Submit button and change the field order if you wish. Set the parameters to values from JSF Objects to use acctNum and transId from UserSystemState. Once the action binding is complete, you can set the readOnly property to true for fields that you do not wish a user to be able to edit. Double-click each inputText field in the Structure window and set their labels from the resource file. It is easier to edit the values in the resource file first. It is probably best to allow a user to Cancel out of the edit page, so you can surround the Submit button with a PanelGroup, then add another CommandButton before or after the Submit. You will need to choose those buttons and add return values. For the Cancel button, there is no need to call the backing function that corresponds to the button; you can just edit the action property and specify a navigation transition value. Something original like "cancel" for the Cancel button will work. Add a jsf navigation case in the faces-config.xml Design window for the submit return value so that you return to the list page from the edit page. A better alternative is to set the navigation value in the faces-config.xml file first, in which case the value will appear as an option when choosing the value of the action property for the buttons—JDeveloper uses configuration files to advantage.

You still need to write the code to persist the changes made on the edit page. But in this instance, you should use the ADF development paradigm again. In other words, rather than writing code in a backing bean, rely on JDeveloper/ADF wizards to write the code needed to persist the changes. You will also use the mergeEntity() function that was automatically created when you initially created our ORM and generated a session bean

As suggested in Chapter 10 of the ADF Tutorial, open the Design window on the edit.jspx page and then open the Data Control palette. Drop the mergeEntity() method onto the Submit button, and choose Bind Existing CommandButton. The next page displayed is the Action Binding Editor. Double-click the value box in the next step of the wizard (or choose the ... button if it is shown), and open bindings in ADF Bindings. Keep in mind that bindings is a reference to all the bindings available in ADF. Open the findOneTransactionIter node and choose the dataprovider in the currentRow. The expression that is built, specifying the current record, is ${bindings.findOneTransactionIter.currentRow.dataProvider}. Notice that the ActionListener property for the Submit button has been reset to #{bindings.mergeEntity.execute}. Set the action property to something like "list" so that, once the change to the database is complete, our interface will return to the list page. Set the immediate property to true for each of the text fields whose values can be changed. The edit page should look like the following:

Again, ADF makes it very easy to add the capability to edit a transaction. Although you haven't done it here, you could add another function to the session bean to do Apply Business Rules to Edited Field before calling mergeEntity.

 

You Have to Stop Somewhere



There are so many things that are incomplete in this exercise, but that could still be accomplished after referring to the tutorial or developer's guide:

  • You haven't added any security to the application to ensure that only users with signatory responsibility can add a record (or edit one). Simple security would allow you to print the user's name instead of null.
  • You haven't handled error or unusual conditions.
  • You didn't use the validation features of ADF. For example, it would make sense to ensure that input text boxes are not empty when a user enters a new transaction.
  • Adding a select to allow a user to delete a deposit or expenditure from the transaction list would have been interesting.

But you have to stop somewhere!

 

Acknowledgements and Conclusions



We wish to thank the JDeveloper Team for reviewing this article for accuracy in regard to statements about JDeveloper functionality. Many features of JDeveloper have not been described here but are described in the Oracle documentation.

As we mentioned in the introduction, it can be difficult to switch technologies when you are comfortable with a particular method of development. In our case, we were content to use Spring JDBC templates for data modeling, Spring MVC and Spring Web Flow for navigation, and Eclipse as our development environment. In retrospect, it may be easy to see that initially we wanted to learn how to do what we already knew in a new environment, so using options like backing beans made sense to us. Initially we weren't going to try TopLink and even looked for alternatives to using ADF Controls. We are very glad that we read the Oracle references, because they gave us the confidence to try the additional functionality that ADF provides.

ADF is clearly an excellent alternative, and a good adjunct, to other application development frameworks like Spring. We intend to explore several alternatives with ADF and JDeveloper, including

  • Making our existing back-end Spring-based services available as Web services that can be called from ADF Faces interfaces. The blog by Oracle ACE Lucas Jellema has inspired us to try this option.
  • Using Oracle XML Publisher as an alternative for building reporting pages
  • Using the BPEL Plug-in for JDeveloper to model the need for human intervention in a business process. It has been said that BPEL is to business process management what SQL is to data management.

The family of development tools provided by Oracle nicely coincides with our needs.

 

Tips and Gotchas

  1. In for a penny, in for a pound. It's mentioned in several places but it bears repeating: JDeveloper and ADF are end-to-end solutions with real power. They intend to compete with development environments like .net. For example, we initially planned on putting the sample application together without using TopLink, figuring that in our application it would be easy to write Spring-based data access. Our curiosity about TopLink was piqued by reading the ADF Tutorial, so we tried it and initially came away feeling that it was harmless. Our attitudes changed entirely when we added our first list form for transactions in approximately 90 seconds. Initially the mergeEntity function seemed to be just another artifact created by TopLink. After we added the edit page we understood its value, as well as the value of the ADF controls that made access to the Faces components seamless. JDeveloper provides an integrated development environment. ADF is not just a set of UI components but a complete front- and back-end framework with the aim of providing tools that truly leverage your efforts.
  2. There are differences between JDeveloper and Eclipse or Websphere Application Developer when it comes to creating packages, and copying and moving files around. For image and other non-Java files, we have found that the easiest way to copy and move files is to do it in Windows Explorer, followed by a Refresh in the Applications window. When using the Import option in File, be sure to edit the CopyTo line to include the full folder (package) name. Renaming and moving files can also play havoc with configuration files and property values. We generally use the ... option in the Property Editor to set property values for images and other files. Editing configuration files takes a sharp eye and patience. Probably the most painful error we made was to change campusAccounts to campusaccounts in the faces config file. No transitions would take place to following pages, and much time was wasted. To be honest, keeping packages consistent was an irritant when using JDeveloper.
  3. Downloading SRDemo is easy if you have just installed JDeveloper. In the Help menu, choose Check for Updates, then Official Oracle Extensions. Select ADF SRDemo, and complete the wizard. To run it, go to the SRDemo application and choose index.jspx and the green arrow. Once you've downloaded the demo, you have full access to its files, including helper classes like JSFUtils and UserSystemState that almost all applications will need. You will need to provide a database schema for SRDemo.
  4. You don't have to leave JDeveloper to view the values of your database tables. From the Connections tab, choose your Connection and schema, and then open a table. The table values are shown in the Data tab (it may be on the bottom of the window).

Tom Moore is Senior Consultant with Learning and Technology Services at the University of Wisconsin - Eau Claire. Tom holds a Master's Degree in Computer Science from the University of Wisconsin - Milwaukee and has been an active user of Java and Open Source products such as Struts and Spring. Tom was a member of the Computer Science Department at UW - Eau Claire for many years prior to turning his attention to Oracle development tools and frameworks.