Building Oracle ADF Applications: Workshop

This comprehensive workshop shows you how to build applications using Oracle Application Development Framework (Oracle ADF). The technologies used in the sample application include Oracle TopLink, Oracle ADF Databinding, Struts, and JavaServer Pages.

In the workshop, you will build a web-based application that provides for maintenance of customer information. You will start by building the business services using Toplink. You then build the client portions of the application.

Topics

This workshop covers the following topics:


The workshop application is a set of customer maintenance pages that are used to create new customer records, update existing records, and delete existing records. During this workshop, you learn how to create these pages and make them user-friendly and robust enough for a multiuser environment.

We need to provide our customer with an application that is easy to use and understand and that provides useful feedback to the user. The user needs to be able to edit or delete customer records as well as create new customer records. Navigation should be easy and intuitive, and feedback needs to be clear and obvious. For example, when a record is updated, the user needs a message that indicates success. The same technique should apply to deleting and creating records as well as handling any errors that may occur. The messages must be internationalized so that the application can run in multiple languages.

Prerequisites

Back to Topic List

Before starting this workshop, you should have:

1. Installed Oracle JDeveloper 10g version 10.1.2
2. Installed the sample schema and created a connection to the OE schema to use in the workshop. See Installing the Sample Schemas and Establishing a Database Connection.
3.

Add a database view to use for your model. This view is a subset of columns in the CUSTOMERS table.

Run the following statement connected as OE. You can use SQL*Plus or SQL Worksheet that is installed as part of JDeveloper.

create view
CUSTOMERS_WS_VIEW as
SELECT customer_id cust_id
,cust_first_name cust_name_first
,cust_last_name cust_name_last
,cust_email customer_email
FROM customers;

4.

Add a database sequence that you will use to populate the CUSTOMER_ID column of the CUSTOMERS table.

Open SQL*Plus, connect to the OE schema, and run the following script:

create sequence CUSTOMERS_SEQ start with 1000;

 

5.

At the end of the workshop, run the following statements to remove the view you just created.

drop view CUSTOMERS_WS_VIEW;
drop sequence CUSTOMERS_SEQ;

 

Introduction to the Completed Application

Back to Topic List

The Customer Maintenance application provides the functions to maintain customer information. It is built to provide the standard create, retrieve, update, and delete functions. There are four pages in the application:

The Browse page provides a starting point and a way to see a list of all customers. The user can select a row and either edit or delete the row. This page also provides a button that the user can use to create a new row.

The Edit page provides all of the fields that the user can modify. This page is also used for creating a new customer row. When the user creates a new row, the customer ID is not displayed because it is retrieved from a database sequence. You should have created the sequence as a prerequisite to this workshop.

The Sure page provides a confirmation opportunity for the user before a delete is finalized. This page shows the customer row that a user is about to delete. The user can click either Yes (to delete the customer row) or Cancel (to abort the delete). In both cases, we display the correct message and navigate back to the Browse page.

The Errors page displays any error messages that are created during the delete cycle.

 

Building the Business Services

Back to Topic List

The Customer Maintenance application is based on a rather simple data model. It is based on a database view of the Customers table. TopLink is a ramework that makes creating and managing the persistence layer of an application easy.

Because we use the default behaviors, we can use the Toplink Wizard to create the business services we need for our application. In the next few steps, you create the components that you use in the Customer Maintenance application.

There is a demonstration at the end of this section if you would like to watch these steps.

 


Creating the Default Application Workspace and Projects

Back to Topic List

1.

Create a new Application workspace to hold the business model components:

  1. Choose File > New, and then in the General Category, select Application Workspace.
  2. Change the name of the application to CustomerMaintenance.
  3. Make sure that Web Application [Default] is set as the Application Template.
  4. Accept the other defaults and click OK to continue.

 

2.

When you have completed the wizard, you should have an application named CustomerMaintenance and two projects: Model and ViewController. You use only the Model project for this part of the workshop.

When you are finished, the Application Navigator should appear as follows:

 

 

Creating Default Toplink Components

Back to Topic List

1.

Because we are using Toplink as our business services layer, we use the Toplink Wizard to create all of the components required for our business model.

To create a set of default components, right-click the Model project node in the System Navigator and choose New.

 

2.

In the New Gallery, change the Filter By: option to All Technologies.

Note: The Technology filter is at the top of the New Gallery window.

 

3.

Expand the Business Tier node and select TopLink.

 

4. Select Java Objects from Tables and click OK to continue.
5.

The first step of this wizard is to establish a connection to your database. Select the connection name that connects to the OE schema.

Note: If you have not established this connection to the OE schema, please see the Prerequisites section of this workshop.

 

6.

For this workshop, we will use the database view that you created in the prerequisite section. Because we are using a view, you need to click the Views checkbox to include views in the selection list.

In the next step of the wizard, select the CUSTOMERS_WS_VIEW , shuttle it to the Selected pane and click next to continue. Accept the defaults for the rest of the steps in the Wizard.

Click Save after the Wizard is done.

When you are finished, the Application Navigator appear as follows:

 

7.

Since we are using a database view that doesn't have a primary key, we need to select one with the TopLink generated object.

Double-click the CustomersWsView.java node in the Application Navigator. This action opens the Source Editor for the CustomersWsView.java file.

8.

Click the Mapping tab at the bottom of the Visual Editor.

 

9.

First, set the Primary Key attribute.

Click the checkbox next to the CUST_ID attribute.

 

10.

Next, set the descriptor to use a database sequence to populate the CUST_ID column.

  • Click the Use Sequencing checkbox
  • Enter CUSTOMERS_SEQ as the Name value.
  • Select OE.CUSTOMERS_WS_VIEW as the table value.
  • Select CUST_ID as the Field value.

 

11.

In the Applications Navigator, expand the TopLink node and double-click the TopLinkMappings node to open the TopLinkMappings object in the Visual Editor.

 

12.

Click the Use Native Sequencing radio button in the Sequencing area of the Visual Editor.

 

The TopLink objects are now ready for deployment descriptors and data controls. You will create those in the next few steps.

 

 

Creating Deployment Descriptors and DataControls

Back to Topic List

In the previous step, you created the default Java object for the CUSTOMERS_WS_VIEW view. In this step, you will create the default deployment descriptor and a default DataControl object. The ADF framework uses these two components to provide data access to client applications.

1.

To create the default deployment descriptor, expand the TopLink node in the Application Navigator, right-click TopLink Mappings and choose Generate toplink-deployment-descriptor.xml from the context menu.

Click OK when the descriptor is created.

 

2.

The Application Navigator should now look like:

 

3.

Next, create a default sessions.xml file. Just as before, right-click Toplink Mapplings and choose New sessions.xml from the context menu.

 

4.

The Application Navigator should now look like:

.

5.

Next. add a named session object to the sessions.xml file. You will do this using the Visual Editor.

Double-click the sessions.xml node in the Application Navigator. This opens the sessions.xml file in the Visual Editor.

 

6.

Click Add and add a new session. Accept the default name.

 

7.

Next, create a DataControl based on the Customers object.

Right-click Customers.java in the Application Navigator and choose Create Data Control from the context menu.

 

8.

Change the values in the Deployment Resource wizard to:

  • Click the Sessions Configuration radio button
  • Location: sessions.xml(Model.jpr) (from the drop-list)
  • Sessions: MySessionName(Server) (from the drop-list)

 

Click OK to continue.

 

9.

Click Save on the toolbar or choose File | Save All from the main menu to save your workspace.

The Application Navigator should now look like:

 

 

If you would like to watch a demonstration of the previous tasks, click here.

 

You have now created all of the objects needed for the Business Services Layer of our application. The next steps will be to create the client application.

 

 

Building the Basic Page Flow

Back to Topic List

Building an application can be an iterative process. You have already built the business service layer, so you now create a basic page flow of the application. After the basic pageflow is complete, you begin developing page content. As you move through the process, you will probably revisit the page flow and make additions and changes to make the application more robust.

Creating DataPage Components
Adding Data Components to the editCustomers Page
Connecting the Pages
Adding a Create Function

Creating DataPage Components

DataPages are the basic components of our Web-based Oracle ADF application. The first thing you do is create the basic framework of the application by creating a DataPage for each of the pages in the application. The pages are shown at the beginning of this workshop. As a reminder they are:

You add these DataPages using the Struts diagrammer in JDeveloper.

There is a demonstration at the end of this section if you would like to watch these steps.

Back to Topic List

1.

Open the Struts diagram. Right-click the ViewController project and choose Open Struts Page Flow Diagram from the context menu.

 

2.

Now create a DataPage for each of the pages in the application.

  • browseCustomers
  • editCustomers
  • sure
  • showErrors

To create the DataPages, click the DataPage icon in the Component palette. The Component palette should be on the right side of the JDeveloper window.

You can either click the icon or click and drag it to the diagram. If you click and drag, you have control over where on the diagram the DataPage is created. Other than that, the two techniques are the same.

When you are done, your diagram should look something like this:

The DataPages you just created are now items in the Struts configuration, but they do not yet reference pages that display data in your application. The next step is to create those pages.

 

3.

In this step, you create and test the browseCustomers page. Double-click the /browseCustomers DataPage to create a corresponding JSP. You are prompted for the type of page you want to create. You can select either a JSP, an HTML page, or a UIX page. For this application, choose /browseCustomers.jsp. Click OK to continue.

You now have a JSP named browseCustomers.jsp. The default page that you just created is blank. The next step is to add some data- aware components.

 

4.

JDeveloper and Oracle ADF make it easy to add data-aware components to a page. There are two tabs found at the bottom of the Component Palette, the Components tab and the Data Control tab. They are the . The Components tab show all of the nondatabound components, while the Data Control tab shows all of the ADF-databound components.

Select the Data Controls tab to see the databound components. Expand the CustomersWsViewDataControl node to see the components that are available. In this case, there is only one ADF component: readAllCustomer(), which you created earlier in this workshop.

If you expand the return node, you see all of the individual data controls that are available for your use.

 

5.

The browseCustomers page should display multiple customers in a table layout style. JDeveloper provides drag-and-drop functions to create databound pages.

Make sure you are in the editor window for the browseCustomers JSP. If you are not sure, you can double-click the browseCustomers data page on the Struts Page Flow diagram.

Expand the readAllCustomersWsView() node on the DataControl palette. Select the return node. Notice the "Drag and Drop As" list at the bottom of the palette. This list shows the possible styles you can use for the selected component. Use the Read-Only Table option for this JSP.

 

Now that you have selected Read-Only Table, drag the return component to the browseCustomers.jsp editor window.

The page should now look like:

 

6.

You have just created an ADF data-aware JSP. To test your new JSP, go to the Struts PageFlow diagram and right-click the /browseCustomers data page. Choose Run from the context menu to launch an internal server that runs your page.

If you run the browseCustomers.jsp from the Application Navigator, the jsp will run but it will not be data aware or run in the context of the page flow diagram. You must run the /browseCustomers data page to see the data aware components.

You page should look like the following:

 

7.

Now that you have a basic page, add a few navigation buttons to make it a bit more useful. You add buttons to the page the same way you added to databound Read Only Table for the return data control.

Expand the return node in the Data Control palette and then the Operations node. Click the Previous Set component. Select 'Button with Form' as the Drag and Drop as value.

Drag the Previous Set component to the browseCustomers.jsp editor window, just below the data table.

Select 'Button' as the Drag and Drop as value and drag the Next Set component to the right of the Previous Set button.

 

 

8.

Run the JSP as you did earlier and test the buttons to verify that they navigate through the list of customers.

 

 

If you want to see a demonstration of these steps, click here.

 

 

Adding Data Components to the editCustomers Page

In this task, you add data components to the editCustomers Page. The only difference in creating the browseCustomers page and this page is the type of data component that you use. On the browseCustomers page, you used a read-only table; on this page, you use an Input Form component.

There is a demonstration at the end of this section if you would like to see what the steps look like.

Back to Topic List

1.

The first step is to create a JSP that corresponds to the editCustomers node in the Struts Page Flow diagram. You do that just as you did for the browseCustomers page: Double-click the editCustomers node and accept /editCustomers.jsp as the page name.

This opens an editor window for editCustomers.jsp. Click the Design tab at the bottom of the Visual Editor.

2.

Add an Input Form data component from the Data Component palette as follows: Select return, select Input Form from the "Drag and Drop As" list, and drag the return node to the editor window.

 

3.

Add two buttons to the page. Because this is an edit form, you need to add a button that accepts and processes any user input as well as a button that rejects, or cancels, any changes the user makes. Those two buttons are the Commit and Rollback buttons.

When you added the Input Form component, JDeveloper created a Submit button for you. Because we are adding our own buttons to manage the submit function, delete the default Submit button.

Add the two new buttons: Commit and Rollback. Make sure you are in the editCustomers.jsp window, select the Commit button from the top-level Operations node, make sure that the "Drag and Drop As" list is set to Button, and drag the button to the page within the Form tag which is highlited with a red dotted line.

Repeat this step for the Rollback button.

 

4.

Rename the buttons to make the page a little more user friendly. The easiest way to change the label on the button is to double-click the button and change the Value property.

You can also change that property in the property inspector.

  1. Change the Commit button value to "OK".
  2. Change the Rollback button value to "Cancel".

 

5.

By default, the code that is created for the buttons includes an expression that tests to see if the button should be enabled or not. Because this is an edit form, we want both of the buttons enabled all the time.

Click the Source tab at the bottom of the editor window to edit the code. To enable the buttons all the time, remove the following code from the button definitions.

Commit button: <c:out value="${bindings.Commit.enabledString}" />
Rollback button: <c:out value="${bindings.Rollback.enabledString}" />

When you are done, the code should look like the following:

<input type="submit" name="event_Commit" value="OK" />
<input type="submit" name="event_Rollback" value="Cancel" />

 

6.

Test the page just as you tested the browseCustomers page earlier. Right-click the editCustomers on the Struts Page Flow diagram and choose Run from the context menu.

 

Notice that there is only one row available and that there is no facility to scroll through the customer rows. This is deliberate. In the next few steps, you will connect the browseCustomers page and the editCustomers page. After they are connected on the page flow, the user starts and finishes with the browseCustomers page and uses the editCustomers page only for inserting or editing specific rows.

When you are done, close the browser.

 

 

If you want to see a demonstration of these steps, click here.

 

 

Connecting the Pages

You have now created two databound JSPs that display related data in different ways. The next step is to connect those pages in a logical way. The browseCustomers page will serve as the starting point for the users. They will select which row they want to edit from the browseCustomers page. They can also insert a row staring on that same page. The editCustomers page is used for editing and inserting rows. We need to create several connections, called forwards, between these two pages.

In this task, you will add a few buttons to the browseCustomers page so the user can click either Edit or Create and automatically navigate to the editCustomers page. You will also add forwards from the editCustomers page back to the browseCustomers page. Those forwards relate to the user clicking either the OK or the Cancel buttons.

There are demonstrations in this section if you would like to see what the steps look like.

Back to Topic List

1.

JDeveloper and Oracle ADF provide a number of data-aware functions that make data navigation easy. In the next few steps, you will add some default behaviors, test them, and then modify them to meet specific needs.

The first thing you will do is add a function that will set the current row to any row the user clicks. The TopLink model that you created earlier will synchronize all of the data without any coding on your part.

To add the method that sets the current row:

  1. Open browseCustomers,jsp in the Visual Editor.
  2. Scroll to the right side of the page.
  3. Right-click inside the last column in the table.
  4. Choose Table > Insert Rows Or Columns from the context menu.
  5. Insert two columns after the selection.

These two columns will hold the links, and later the buttons, that you will add to edit a customer row.

2.

Next, add the setCurrentRowWithKey method from the data model as a link.

To add this method:

  1. Expand the return node in the Data Control palette.
  2. Expand the Operations node.
  3. Select setCurrentRowWithKey(String).
  4. Select FindRowLink in the "Drag and Drop As" list.
  5. Drag the setCurrentRowWithKey(String) to the first empty column in the bottom row of the data table.

You will now have the text select in that column that is a link. The link won't take the user anywhere yet, but it will set the current row to the row that they click.

 

 

3.

Test the page by running it from the Page Flow diagram.

Click the select link and notice that the * in the first column is displayed in the row that you click. This shows the current row changing as you click.

 

 

If you want to see a demonstration of the previous steps, click here.

 

4.

Now that you know that the setCurrentRowWithKey method is working, you can make the link a little nicer by replacing the select text with a button. One of the nice features of the JDeveloper IDE is that you can drag an image from almost anywhere to the editor window and JDeveloper will incorporate it in your page.

Add the Edit button by saving this button to your local disk drive (right-click the image and choose Save Picture As or Save Image). Save the button_edit.gif to a temporary directory.

Open Windows Explorer to the directory where you saved the image. Now drag the image to the editor window and drop it in the middle of the select text.

The IDE will prompt you to add it to the document root of the application. Click Yes and save the image in the public_html directory.

The result should appear like the following:

 

5.

Next, delete the select text from around the button. You can do this from either the Design window or the Source window.

After you have deleted the text, click the button_edit.gif image to select it. Now go to the Properties palette and set the border property to 0. This will ensure that the image does not appear with a blue border around it (indicating that it is a link).

 

6.

Test the page just as you tested it earlier. Notice that when you click the image, the current row changes just as it did when it was a text link.

 

 

 

If you want to see a demonstration of these steps, click here.

 

Now that we have the links and buttons on the pages, it's time to add the forwards to the Struts Page Flow diagram.

The Struts controller manages page navigation by using events and forwards. The event notification is specified in the JSP, while the forwards are defined within the Struts configuration. The Page Flow diagram in JDeveloper manages the Struts configuration file so you don't have to modify the file directly.

In the next few steps, you will add forwards to the Page Flow diagram and modify one link to add an event.

 

8.

Let's start by adding the forwards to the Page Flow diagram.

Open the Page Flow diagram, then go to the Component palette. Click the Forward component. To draw a forward from the browseCustomers node to the editCustomers node, click inside browseCustomers, then click inside editCustomers. If you want more control over where the line is drawn, you can click anywhere on the diagram between the two nodes.

The default name for a forward is success. To change the name of the forward, click once on the label (success). Click a second time to edit the label. Change the name of the forward to Edit.

 

9.

Now that you have an Edit forward on the page flow, you need to add a reference to it on the browseCustomers page.

Open the Source editor for the page. Find the Edit button link that you added earlier. Add the following event code to the link:

&event=Edit

The link should now look like:

<a href="browseCustomers.do?event=setCurrentRowWithKey&Arg0=<c:out value='${Row.rowKeyStr}' /> &event=Edit">
<img height="21" width="39" src="button_edit.gif" border="0"/></a>

 

10.

By adding this event reference, Struts will call the setCurrentRowWithKey method, set the current row, and then navigate through the Edit forward to the editCustomers Page.

You now need to add forwards to get back to the browseCustomers page.

Add two forwards just as you did for the browseCustomers page. This time add them from the editCustomers page to the browseCustomers page.

Name the two forwards as follows:

Commit
Rollback

Recall that these were the names of the built-in methods that you added to the page earlier. The Page Flow diagram should look something like this:

 

11.

Test the application. You should now be able to edit rows, commit the changes, and see them on the browseCustomers page. You should also be able to make changes, cancel the transaction, and see the unchanged row on the browseCustomers page.

 

 

If you want to see a demonstration of these steps, click here.

 

 

Adding a Create Function

So far, you have created a couple of databound JSPs within a Struts-controlled application. Those pages enable browsing and editing customer information. The next step is to add the ability to create customers. JDeveloper and Oracle ADF provide a built-in function to make this task easy.

There is a demonstration at the end of this section if you would like to see what the steps look like.

Back to Topic List

1.

Open the browseCustomers.jsp in the editor window. Select the Create operation from the Data Control palette. Drag it to the edit window and drop it within the form tag that contains the Previous Set and Next Set buttons.

2.

If you run the page now and click the Create button, Oracle ADF inserts a new blank row into the rowset iterator and stays on the browseCustomers page. The insert works but it does not do users much good: they can't add values to the row.

We really want two things to happen: insert a blank row into the rowset, and navigate to the editCustomers page so that the user can add values to the new row.

The good thing is that we already have the capability to control navigation through Struts.

Add a forward to the Page Flow diagram from browseCustomers to editCustomers and name it Create.

 

You now have the buttons and navigation in place to insert a new row. Remember that when you created the TopLink mappings and data control, you set the CustId attribute to use a database sequence for it's value. The custId attribute will be populated at commit time.

Later in this workshop, you will change the attribute to make this more obvious to the user.

 

3..

Test the new functions by running the browseCustomers page and creating a new row.

Make sure to leave the Customer ID field blank. TopLink will populate that value from the sequence at commit time.

Later in this workshop, you will change the display properties of the field to make it more user friendly.

 

 

If you want to see a demonstration of these steps, click here.

 

 

Adding the Delete Function with a Confirmation Page

Back to Topic List

In this task, you will add the delete function to the application. We could add the delete function in the same way that we added the create function, but the default behavior doesn't give the user a place to confirm the delete. Most applications require that the user be given the opportunity to confirm a delete action before a row is permanently deleted.

In this task, you will add a remove button and a confirmation page.


Adding a Remove Link

The first step is to create a button on the browseCustomers page. This button will look like a button, but it won't actually remove a row. It will do the same thing that the Edit button does: it will simply set the current row to the row that the user clicks. Next, it will navigate to the Delete Confirmation page. The delete confirmation page will hold the delete function.

Back to Topic List

1.

Add the Remove button to the browseCustomers page just as you added the Edit button in an earlier step:

  1. Open browseCustomers.jsp
  2. Drag a setCurrentRowWithKey operation as a Find Row Link to the last column in the last row in the table.
  3. Add the Remove button by saving this button to your local disk drive (right-click the image and choose Save Picture As or Save Image). Save the button_remove.gif to to a temporary directory. Open Windows Explorer to the directory where you saved the image. Now drag the image to the editor window and drop it in the middle of the select text.

The IDE will prompt you to add it to the document root of the application. Click Yes and save the image in the public_html directory.

The result should look like the following:

 

2.

Next, delete the select text from around the button. You can do this from either the Design window or the Source window.

After you have deleted the text, click the button_remove..gif image to select it. Now go to the Properties palette and set the border property to 0. This will ensure that the image does not appear with a blue border around it (indicating that it is a link).

 

3.

Test the page to make sure that when you click the remove button , the row is set to the current row.

 

4.

Now that the button sets the current row, you need to add an event (a chained event) to the link so that when the user clicks the button, Struts will navigate to the next page in the flow. In this case, you will navigate to a Delete Confirmation page that you will create shortly.

Open the Source editor for the page. Find the Remove button link that you just added. Add the following event code to the link:

&event=Delete

The link should now look like:

<a href="browseCustomers.do?event=setCurrentRowWithKey&Arg0=<c:out value='${Row.rowKeyStr}' /> &event=Delete">
<img height="21" width="39" src="button_remove.gif" border="0"/></a>

 

The button will now set the current row and navigate to the delete event.

In the next task, you will create the Delete Confirmation page and add the forward to the Struts diagram.

 

 

Creating a Delete Confirmation Page and a Forward

The next task in building the delete process is to create a Delete Confirmation page and incorporate it into the flow of the application. Recall that in the previous task you added a button to the browseCustomers page that sets the current row and navigates to the Delete forward in the Struts page flow. In this task, you will create the Delete Confirmation page.

There is a demonstration at the end of this section if you would like to see what the steps look like.

Back to Topic List

1.

The confirmation page in our application is named sure.jsp.

Create the page just as you created the Edit page earlier.

  • Double-click /sure on the pageflow diagram
  • Accept the default name of /sure.jsp.

Add a Read Only Form based on the CustomersWsViewDataControl.

 

2.

The page needs two buttons to make it complete: confirmation and cancel. The confirmation button is a databound delete button. Remember that users access this page only if they click the Remove button on the browseCustomers page

Although this is a real delete button, we'll disguise it by changing the label to Yes. It will look to users as if the Remove button on the browseCustomers page is the delete and that the Yes button is the confirmation, which is exactly what we want.

Add a Delete button from the Operations node (within CustomersWsViewDataControl). If you need help, refer to the earlier steps where you added the Create button.

Change the value property of the button to Yes.

Next add a Cancel button. An easy way to create another button is to copy and paste the button you just added. After you paste it, double-click the button to open the edit window. Change the name of the button to event_Cancel and the value to Cancel.

Add some text to the left of the button that says "Are You Sure?" Set the style to Heading 4.

 

3.

You also will need an action binding named 'Commit' that will be bound to the built-in commit operation of your UI model.

To add the Action:

  1. With sure.jsp active in the Visual Editor, click the UI Model tab of the Structure window.
  2. Right-click the root node in the tree and choose Create Binding > Actions > Action from the context menu.
  3. Select your data control (CustomersWSViewDataControl) in the Data Collection list.
  4. Select Commit as the action using the Select an Action drop-list.
  5. Click OK to continue

Later in the workshop, you will add code to call this commit function.

4.

You can now run and test the form.

Note: Remember that this is a delete confirmation form. If you click Yes, the row will be deleted, but just from the iterator. You will add code to call the commit later in the workshop

 

5.

Now that the confirmation page is functionally complete, we can incorporate it into the page flow.

Open the Page Flow diagram and add a Forward named Delete from the browseCustomers page to the sure page.

Next add a Forward named Delete from the sure page to the browseCustomers page.

The Delete forward from browseCustomers will get the user to the confirmation page. The Delete forward from the Sure page will get the user back to the browseCustomers page after a delete.

Next add a Forward named Cancel from the sure page to the browseCustomers page. This will take the user back to the browseCustomers page after clicking Cancel.

The Page Flow diagram should now look something like the following:

6.

Run the browseCustomers page. You can now edit rows, create new customer rows, and delete customers. You can also change your mind and click Cancel from the Edit and Delete confirmation pages.

All of the basic application functions are now in place. There are a couple of areas that we still need to address. First, we need to issue and handle messages that will keep the user informed. Messages like "Customer nnn has been updated," "Customer nnn has been deleted," and "Transaction canceled" will keep users from guessing what just happened and whether they were successful.

We also need to make the pages a a bit more attractive as well as internationalizable. Struts makes both of these tasks easy.

In the next task, you will address both of these issues.

 

 

If you want to see a demonstration of these steps, click here.

 

 

Creating and Displaying User Messages

Back to Topic List

The application that you have created covers all the basic functions we need. We now need to add some messages that will help users know the status of any actions they choose. If they delete a row, we want to display a message showing what row was deleted. If they insert a row, we want to show them that it was successful. Likewise, it they modify a row or cancel a transaction, we need to display the appropriate message.

Because the messages are transaction-type specific, we need a way to know and keep track of which button the user clicked. We will override, or augment, some standard methods in the Struts action to maintain the transaction type as a session variable. We will also override a method to interpret the transaction type and create and store the appropriate message.


Creating Session Variables with onEvent() methods

The first step in managing the custom methods for our application is to store the type of transaction or button click that caused Struts to forward to a new page. In our case, the starting point (and ending point) of the application is the browseCustomers page. You will override the dataAction class and add code to store the transaction type.

Back to Topic List

1.

To override the Struts data action:

  1. Right-click browseCustomers on the Page Flow diagram.
  2. Choose Go To Code from the context menu.
  3. Accept the default name and click OK.

 

You now have a class that you can use to augment or override standard methods and behaviors.

 

2.

In our application, we need to store the transaction type when the user clicks a button. The way to intercept the button click event is to add a method to this class with the name onEventName, where EventName is the name of the event associated with the button or link.

For example, the Edit button includes event=Edit in the href. Struts will do several things based on this event. First, it will look for a method named Edit and execute it if it exists. Second, it will look for a method named onEdit and execute that method. And finally, it will look for a forward named Edit and navigate through that forward.

We can use this pattern and add an onEdit to our override of the DataAction class.

Create a new method in the BrowseCustomersAction class as follows:

public void onEdit (DataActionContext actionContext)
{


}

The argument for this method is a DataActionContext object. When you enter this code into the class, JDeveloper will prompt you to import oracle.adf.controller.struts.actions.DataActionContext. Press [Alt] + [Enter] to add the import statement.

 

3.

Add an onEdit method to the BrowseCustomersAction.java that you created in the first step. You will do only two things in this method:

  • Store the transaction type in the session variable.
  • Perform the standard action.

The code to store the transaction uses the DataActionContext to get the HTTP Servlet Request and the Session and set an attribute within the session. The attribute name is type and the value is create. The code is:

actionContext.getHttpServletRequest().getSession().setAttribute("type", "edit");

Add this code to the onEdit method.

 

4.

Next, you need to execute the default behavior of the class. You do that by adding a call to method called doIt(). Before making the call, you want to make sure there is a good EventActionBinding. The code is:

if (actionContext.getEventActionBinding() != null)

{
actionContext.getEventActionBinding().doIt();
}

Add this code to the onEdit method.

The complete method should look like the following:

public void onEdit (DataActionContext actionContext)
{

actionContext.getHttpServletRequest().getSession().setAttribute("type", "edit");

if (actionContext.getEventActionBinding() != null)

{
actionContext.getEventActionBinding().doIt();
}

}

 

5.

Next, add a method to handle the Create button, name the method onCreate.

Add the same code you used for the onEdit method except set the attribute type value to create. The code should look something like the following:

public void onCreate (DataActionContext actionContext)
{

actionContext.getHttpServletRequest().getSession().setAttribute("type", "create");

if (actionContext.getEventActionBinding() != null)

{

actionContext.getEventActionBinding().doIt();

}


}

Right click browseCustomersAction.java and choose Make to compile the class.

 

6.

You can test the application with these changes to make sure you haven't introduced any errors, but you won't see any change in the application behavior.

In the next tasks, you will add code to interpret the session variable and create and store an appropriate message.

 

Building a Message Stack with a findForward()Method

You have added code to the browseCustomers action to store a session variable that indicates which button the user clicked. We now need to add code to the Edit page that interprets the event and builds and stores a standard Struts message based on the event.

In this task, you will also add code to get some values from the data binding context so you can use them in the messages. We want to make our application internationalizable, so you will add the messages to the ApplicationResources.properties file in the ViewController project.

Back to Topic List

1.

The first thing we need to do is create the EditCustomersAction class. You do this just as you did in the previous step for the BrowseCustomersAction class.

Right-click editCustomers and choose Go To Code from the context menu. Accept the default values for the class. Click OK to continue.

The code will look like:

 

2.

In this class, you will override the findForward() method to build the message stack.

The findForward() method is one of the last methods run in the DataAction class lifecycle. This makes it the best place to check the transaction type and build the message stack. The findForward()method is also where you would set the forward to a specific event, based on application logic. For example you could use this method to set the forward to a notAuthorized page if certain conditions were true.

JDeveloper provides a menu option that will add method signatures and help for methods that you want to override.

First open EditCustomersAction.java in the editor window. Put the cursor in the code where you want to add the method. Choose Tools | Override methods from the menu. Select the check box for the findForward(DataActionContext) method and click OK.

 

This will insert the method with the proper signature into your class. Now you can begin adding the code to interpret the transaction type and build a message stack.

The complete findForward() method is included at the end of this section for your reference.

 

3.

You should carry out several housekeeping actions before you check and interpret the transaction type variable that you set earlier. Add all of the following code after the call to super.FindForward(actionContext).

First, create a List object that contains the events from the DataActionContext.

List events = actionContext.getEvents();

JDeveloper will prompt you to import the List class. Choose java.util.List for the proposed list of classes.

Check that the List is not null and that the size is greater than 0.

if (events != null && events.size() > 0)
{

 

4.

We'll work on handling errors a little later, but for now make sure that there are no errors on the DataActionContext object.

if ( !hasErrors(actionContext))
{

Your code should now look like:

protected void findForward(DataActionContext actionContext)
throws Exception
{
// TODO: Override this
// oracle.adf.controller.struts.actions.DataAction method
super.findForward(actionContext);

List events = actionContext.getEvents();
if (events != null && events.size() > 0)
{
  if ( !hasErrors(actionContext) ) // check for context errors
  {
   } // end of if ( !hasErrors(actionContext)
} // end of if (events != null && events.size() > 0)
} // end of findForward()

 

5.

Compile the file, if there are no errors, create an ActionMessages object to hold the user messages. A Struts application has built-in access to this object so it is easy do display messages in an ActionMessages object.

Add the following code within the if ( !hasErrors) test.

ActionMessages messages = new ActionMessages();

Import the ActionMessages class as JDeveloper prompts you.

You now have an object in which to store the transaction specific messages.

 

6.

As part of the housekeeping, or preparation, for building the message, you need to get the Customer name from the Data Context. You will use this as part of the message to make it more informative. You could simply send a message like "Edit successful," but it would look better to show which customer was changed.

Remember that as you add code, JDeveloper may propose import statements to support the code you are adding. Press [ALT] + Enter to accept the import statements.

To get data from the context, you need to get the BindingContainer from the DataActionContext, then get the binding for a specific attribute and store the String value. The following code sets two local variables. One is set to the value of custNameFirst, and the other is set to the value of custNameLast.

DCBindingContainer bindings = actionContext.getBindingContainer();
DCControlBinding binding = bindings.findCtrlBinding("custNameFirst");
String firstName = (binding != null) ? binding.toString() : "";

binding = bindings.findCtrlBinding("custNameLast");
String lastName = (binding != null) ? binding.toString() : "";

You can now use firstName and lastName in the messages.

Your code should now look like:

protected void findForward(DataActionContext actionContext)
throws Exception
{
// TODO: Override oracle.adf.controller.struts.actions.DataAction
super.findForward(actionContext);

List events = actionContext.getEvents();
if (events != null && events.size() > 0)
{
if ( !hasErrors(actionContext) ) // if there are errors, don't create messages
{

ActionMessages messages = new ActionMessages();
DCBindingContainer bindings = actionContext.getBindingContainer();
DCControlBinding binding = bindings.findCtrlBinding("custNameFirst");
String firstName = (binding != null) ? binding.toString() : "";

binding = bindings.findCtrlBinding("custNameLast");
String lastName = (binding != null) ? binding.toString() : "";


} // end of if ( !hasErrors(actionContext)
} // end of if (events != null && events.size() > 0)
} // end of findForward()

7.

In a previous step, you created an onCreate and onEdit methods that store the transaction type in a session variable. The purpose of those methods is to record what user action occured on a page. If the user clicks the edit button, the type attibute is set to edit If the user clicks the create button, the the type attribute is set to create. . In this step, you will retrieve that variable and build a message based on the value.

First, get the transactions type variable from the session variable. The code to get the session and variable is:

protected void findForward(DataActionContext actionContext)
throws Exception
{
// TODO: Override oracle.adf.controller.struts.actions.DataAction
super.findForward(actionContext);

List events = actionContext.getEvents();
if (events != null && events.size() > 0)
{
if ( !hasErrors(actionContext) ) // if there are errors, don't create messages
{
ActionMessages messages = new ActionMessages();
DCBindingContainer bindings = actionContext.getBindingContainer();
DCControlBinding binding = bindings.findCtrlBinding("custNameFirst");
String firstName = (binding != null) ? binding.toString() : "";

binding = bindings.findCtrlBinding("custNameLast");
String lastName = (binding != null) ? binding.toString() : "";

String type = (String)actionContext.getHttpServletRequest().getSession().getAttribute("type");

} // end of if ( !hasErrors(actionContext)
} // end of if (events != null && events.size() > 0)
} // end of findForward()

Now that you have the type, you can add logic to test for the type of transaction and set the appropriate message.

8.

Add an if statement for each of the transaction types you want to test for. So far, we have edit and create. Later in this exercise you will add a cancel transaction type, so add that test as well.

Inside each of the if statements, add a message to the message object that uses entries in the ApplicationResources.properties file along with the firstName and lastName variables you just created. The code should be as follows:

protected void findForward(DataActionContext actionContext)
throws Exception
{
// TODO: Override oracle.adf.controller.struts.actions.DataAction
super.findForward(actionContext);

List events = actionContext.getEvents();
if (events != null && events.size() > 0)
{
if ( !hasErrors(actionContext) ) // if there are errors, don't create messages
{
ActionMessages messages = new ActionMessages();
DCBindingContainer bindings = actionContext.getBindingContainer();
DCControlBinding binding = bindings.findCtrlBinding("custNameFirst");
String firstName = (binding != null) ? binding.toString() : "";

binding = bindings.findCtrlBinding("custNameLast");
String lastName = (binding != null) ? binding.toString() : "";

String type = (String)actionContext.getHttpServletRequest().getSession()
.getAttribute("type");

if (type == "edit")
{
messages.add("feedback", new ActionMessage("customers.message.update.success", firstName, lastName));
}


} // end of if ( !hasErrors(actionContext)
} // end of if (events != null && events.size() > 0)
} // end of findForward()

The first argument to the messages.add() is just a name for the entry. The second argument is an ActionMessage object. The ActionMessage() accepts a string that it uses as a key to a value stored in the ApplicationResources.properties file. At run time, the message is built and stored using the values found in the ApplicationResources.properties file. You can also append arguments to the ActionMessage which will be substituted for arguments in the ApplicationResources.properties entry.

 

9.

Add the following else if statements with a reference to the associated message in the resources.properties file:

"create" customers.message.insert.success, firstName, lastName
"cancel" general.message.transactionCancelled

With this addition, your code should look like the following:

protected void findForward(DataActionContext actionContext)
throws Exception
{
// TODO: Override oracle.adf.controller.struts.actions.DataAction
super.findForward(actionContext);

List events = actionContext.getEvents();
if (events != null && events.size() > 0)
{
if ( !hasErrors(actionContext) ) // if there are errors, don't create messages
{
ActionMessages messages = new ActionMessages();
DCBindingContainer bindings = actionContext.getBindingContainer();
DCControlBinding binding = bindings.findCtrlBinding("custNameFirst");
String firstName = (binding != null) ? binding.toString() : "";

binding = bindings.findCtrlBinding("custNameLast");
String lastName = (binding != null) ? binding.toString() : "";

String type = (String)actionContext.getHttpServletRequest().getSession()
.getAttribute("type");

if (type == "edit")
{
messages.add("feedback", new ActionMessage("customers.message.update.success", firstName, lastName));
}
else if (type == "create")
{
messages.add("feedback", new ActionMessage("customers.message.insert.success", firstName, lastName));
}
else if (type == "cancel")
{
messages.add("feedback", new ActionMessage("general.message.transactionCancelled"));
}

} // end of if ( !hasErrors(actionContext)
} // end of if (events != null && events.size() > 0)
} // end of findForward()

 

10.

Because we set the transaction type each time a button is clicked, we need to clear it here so that the message logic is not inadvertently invoked. You remove an attribute from the session much like you added it. The session object has a removeAttribute() method similar to the addAttribute() method.

Add the following code after all the else if statements:

actionContext.getHttpServletRequest().getSession().removeAttribute("type");

 

11.

The last step is to save the message stack to the Request object. The Struts Action class has a saveMessages() method, which saves the message stack into the appropriate request attribute. Add a call to this method after the removeAttribute() code you just added.

saveMessages(actionContext.getHttpServletRequest(), messages );

 

12.

This is what the completed findForward() method should look like:

protected void findForward(DataActionContext actionContext)
throws Exception
{
// TODO: Override oracle.adf.controller.struts.actions.DataAction
super.findForward(actionContext);

List events = actionContext.getEvents();
if (events != null && events.size() > 0)
{
if ( !hasErrors(actionContext) ) // if there are errors, don't create messages
{

DCBindingContainer bindings = actionContext.getBindingContainer();
DCControlBinding binding = bindings.findCtrlBinding("custNameFirst");
String firstName = (binding != null) ? binding.toString() : "";

binding = bindings.findCtrlBinding("custNameLast");
String lastName = (binding != null) ? binding.toString() : "";

ActionMessages messages = new ActionMessages();

String type = (String)actionContext.getHttpServletRequest().getSession()
.getAttribute("type");

if (type == "edit")
{
messages.add("feedback", new ActionMessage("customers.message.update.success"
, firstName, lastName));
}
else if (type == "create")
{
messages.add("feedback", new ActionMessage("customers.message.insert.success"
, firstName, lastName));
}
else if (type == "cancel")
{
messages.add("feedback", new ActionMessage("general.message.transactionCancelled"));
}

actionContext.getHttpServletRequest().getSession()
.removeAttribute("type");

saveMessages(actionContext.getHttpServletRequest(), messages );


} // end of if ( !hasErrors(actionContext)
} // end of if (events != null && events.size() > 0)
} // end of findForward()

 

13.

Next you need to add the entries in the ApplicationResources.properties file. You find the default file in the ViewController project under Application Sources > View. Double-click the file to open it in an editor window.

Make the following entries at the end of the existing entries:

customers.message.update.success=Customer {0} {1} Updated
customers.message.delete.success=Customer {0} {1} Deleted
customers.message.insert.success=Customer {0} {1} Created

general.message.transactionCancelled=Transaction cancelled

Now the editCustomers page will store context-sensitive messages in an ActionMessage object.

Click Save All to save the changes to the file.

 

 

You have now completed the code to build and store a message stack. In the next task, you will add code to the browseCustomers page to find and render the messages.

 

Adding Code to Display the Message Stack on the browseCustomers Page

You have added code to the editCustomers action to store messages in an ActionMessage object. We now need to add code to the Edit page that interprets and renders those messages.

Back to Topic List

1.

One of the benefits of Struts is that it provides tags that make standard operations easy. In this part of the workshop, you will add tags to find and render messages that are stored in the Request object. The tags will find, interpret, and render the messages in an ActionMessage object.

Open the browseCustomers page in the visual editor.

 

2.

Add the html:messages tag just below the html:errors tag in the visual editor. The html:messages tag is in the Struts HTML component palette.

Populate the properties and valuse as:

id - messages
message - true.

This tag retrieves the ActionMessage object from the Request object and labels it "messages." It doesn't display the messages.

 

3.

Next, add the tag to loop through and display each of the messages in the object.

Drag a bean:write tag from the Struts Bean page of the Component palette to inside the html:messages tag that you just added.

Hint: Drop the tag in the white space on the inside right of the tag.

Use "messages" as the name for the tag.

When you add the bean:write tag, the html:messages tag may disappear from the visual editor. That's because there is nothing to display or work as a placeholder.

Add a <br/> tag to the source code just after the bean:write tag. Your code should now look like:

<html:errors/>
<P>
   <html:messages id="messages" message="true">
      <bean:write name="messages"/>
      <br/>
   </html:messages>
</P>

The bean:write tag will now appear in the visual editor.

 

4.

You can now test your application. Right-click browseCustomers on the Struts diagram and choose Run from the context menu.

Select a row to edit, change one of the fields, and click OK. You will see the update success message that you added earlier.

 

5.

Now that you have the messages displayed, you need to make them more noticeable. We will set the html:messages tag and the bean:write tag to a Heading 4 style with a foreground color of red.

You can either add the code in the source code editor or use the visual editor. For a change of pace, we will use the source code editor.

Open the source code editor and put the cursor just above the html:messages tag. Press [Enter] to insert a blank line.

 

6.

Begin by typing a "<" and pause for a second or two. JDeveloper will prompt you for the rest of the code by showing a list of valid values that you can choose from. Click <h4>. Make sure the tag is closed with a >.

Next press [Enter] to insert another blank line.

Change the color of the font by typing another "<". Wait for JDeveloper to prompt you and select <font> from the list. Notice that it does not close the tag by adding and ending ">". Press [spacebar] and JDeveloper will show you a list of attributes for this tag. Select the color attribute from the list.

JDeveloper will now prompt you with a list of colors. Choose Red from the list.

Make sure the close the tag with a >.

You have added a couple of tags that you need to close. Put your cursor on a blank line just after the </html:message> tag. Enter a "<" followed by a "/". JDeveloper will automatically propose the proper closing tag of the closest previous nonclosed tag. In this case, it will be the <font> tag. Press [Enter] to accept the closing tag.

Repeat this step for the <h4> tag.

Your code should now look like the following:

<html:errors/>
<P>&nbsp;</P>
<h4>
<font color="Red">
  <html:messages id="messages" message="true">
    <bean:write name="messages"/>
    <br/>
  </html:messages>
</font>
</h4>

 

7.

Click Save All to save your changes. Test your application to see that the messages now appear in red.

Enabling the Delete Confirmation Page

Back to Topic List

You created the Delete Confirmation page, which offers the user the choice to click Yes to delete the current row or to click Cancel to cancel the transaction. To make the application more complete, we need to add some user feedback in the form of a message, as well as a commit so that when users click Yes, the row is actually deleted.

In the next section, you will add code to augment the delete function with a message and a commit. You will also add a cancel event that will build a message to show that the transaction was canceled.


Augmenting the Delete Function

You have already added a delete button to the confirmation page and labeled it Yes. This button calls the delete built-in function. This function marks the row in the cache as deleted but does not commit the transaction. To make this application more user friendly, we need to make a commit part of the processing so that users won't have to worry about clicking commit at some later time. As soon as they confirm the delete by clicking Yes, the delete will be complete. In addition to the commit, we want to create a message that shows users which row they deleted and indicates that the delete was successful.

Back to Topic List

1.

The code you need to add goes in the Action associated with /sure.. You create the Action just as you did when you created the editCustomersAction.java class.

Right-click the sure node in the Struts diagram and choose Go to Code from the context menu. Accept the default name for the Action class.

You now have an Action class to support the sure.jsp.

 

2.

Start by adding the onDelete method. This method will be called by the page when the user clicks the button with the name event_Delete. The call to this method is automatic and based on the name of the button and the name of the method.

The method should accept an DataActionContext object as a parameter. As you enter the code, JDeveloper will prompt you to import the class to support this object. Accept the import proposals.

The code should look like the following:

package view;
import oracle.adf.controller.struts.actions.DataActionContext;
import oracle.adf.controller.struts.actions.DataForwardAction;

public class SureAction extends DataForwardAction
{

  public void onDelete(DataActionContext ctx)
  {


  }
}

3.

Because this method will delete the current row, the first thing you need to do is retrieve the customer name from the row. You do this just as you did in the editCustomersAction. Open the editCustomersAction class and look at the code in the findForward method.

Your code should get the DataActionContext BindingContainer and retrieve the DCControlBinding object for custNameFirst and custNameLast.

You will need to import the following two classes as JDeveloper prompts you:

  • oracle.adf.model.binding.DCBindingContainer
  • oracle.adf.model.binding.DCControlBinding

The method should now look like:

package view;
import oracle.adf.controller.struts.actions.DataActionContext;
import oracle.adf.controller.struts.actions.DataForwardAction;
import oracle.adf.model.binding.DCBindingContainer;
import oracle.adf.model.binding.DCControlBinding;

public class SureAction extends DataForwardAction
{

  public void onDelete(DataActionContext ctx)
  {

    DCBindingContainer bindings = ctx.getBindingContainer();
    DCControlBinding binding = bindings.findCtrlBinding("custNameFirst");
    String firstName = (binding != null) ? binding.toString() : "";

    binding = bindings.findCtrlBinding("custNameLast");
    String lastName = (binding != null) ? binding.toString() : "";


  }
}

4.

Now that you have stored the customer name, you can execute the built-in delete function. To execute the default behavior, call the doit() method.

Before calling this method, you want to make sure that the EventActionBinding is not null. The code to call this method looks like the following:

if (ctx.getEventActionBinding() != null)

{
ctx.getEventActionBinding().doIt();
}

Add this line of code to your onDelete() method.

5.

The onDelete() method now retrieves and stores the customer's name and executes the default behavior. The next thing to do is check for errors from the default behavior; if there are errors, build the ActionMessage object.

You build the ActionMessage object just as you did in the editCustomersAction. If you need to, check the code in that Action class.

The DataActionContext object has a Boolean method that you can use to check for errors. Use (!hasErrors(DataActionContext)) to make sure the default behavior was successful. The code should look like:


if ( !hasErrors(ctx) )
{

} // end of if ( !hasErrors(ctx)

Add your code to build the ActionMessage object within this if statement. The message should reference the customers.message.delete.success entry in the ApplicationResources.properties file.

The complete code should look like the following:

if ( !hasErrors(ctx) )
{
  ActionMessages messages = new ActionMessages();
  messages.add("feedback",
              new ActionMessage("customers.message.delete.success", firstName, lastName));
  saveMessages(ctx.getHttpServletRequest(), messages );

} // end of if ( !hasErrors(ctx))

 

6.

The last step is to add a commit to this method. The result will be that when the user clicks the Yes button, you will build a message, execute the default behavior, make sure it works, and then commit the transaction.

There are several ways to commit the transaction. The best technique is to call the doIt() method on the bindingContainer. When you created the sure.jsp, you added the commit built-in function. Because the function is part of the data binding for that page, you can use it here. All you have to do is get the binding container and find the control binding named Commit. You then call doIt() on that action binding.

    JUCtrlActionBinding actionBinding =     (JUCtrlActionBinding)ctx.getBindingContainer().findCtrlBinding("Commit");
    if (actionBinding != null)
    {
        actionBinding.doIt();
    }

Add this code as the last line within the if (!hasErrors(ctx)) statement.

You should have added the following import statements as prompted by JDeveloper:

import oracle.adf.controller.struts.actions.DataActionContext;
import oracle.adf.controller.struts.actions.DataForwardActio
n;
import oracle.adf.model.binding.DCBindingContainer;
import oracle.adf.model.binding.DCControlBinding;
import oracle.jbo.uicli.binding.JUCtrlActionBinding;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;

 

7.

The completed onDelete() method should now look like:

public void onDelete(DataActionContext ctx)
{
  /**
  * use the DCBindingContainer to get the customer first and last name
  * for messages
  */
  DCBindingContainer bindings = ctx.getBindingContainer();
  DCControlBinding binding = bindings.findCtrlBinding("custNameFirst");
  String firstName = (binding != null) ? binding.toString() : "";

  binding = bindings.findCtrlBinding("custNameLast");
  String lastName = (binding != null) ? binding.toString() : "";
  ctx.getHttpServletRequest().setAttribute("deletedCustomerName",
        firstName + " " + lastName);

  /**
  * execute the default behavior
  */


if (ctx.getEventActionBinding() != null)

{
ctx.getEventActionBinding().doIt();
}


  /**
  * if there are no errors on the transaction build the messages
  */
  if ( !hasErrors(ctx) )
  {
    ActionMessages messages = new ActionMessages();
    messages.add("feedback",
          new ActionMessage("customers.message.delete.success",
                                                firstName, lastName));
    saveMessages(ctx.getHttpServletRequest(), messages );
    JUCtrlActionBinding actionBinding =     (JUCtrlActionBinding)ctx.getBindingContainer().findCtrlBinding("Commit");
    if (actionBinding != null)
    {
        actionBinding.doIt();
    }
  } // end of if ( !hasErrors(ctx)
} // end of onDelete

 

8.

Click Save All to save your changes.

You can now test your delete and message. Right-click browseCustomers on the Struts diagram and choose Run from the context menu.

Scroll through the Customers list to find a customer ID that starts with a 2 (for example, 201). Delete this customer by clicking the Remove button.

Verify that the correct message is displayed on the browseCustomers page.

 

 

Enhancing the Cancel Function

The cancel function on the Delete Confirmation page simply creates a message to show that the transaction was canceled by the user and executes the default behavior. The technique you will use for this is the same that you used for the Delete function except for commit and error checking.

Back to Topic List

1.

Open sureAction.java. Create an onCancel() method that accepts a DataActionContext object as a parameter.

The method should be as follows:

public void onCancel (DataActionContext ctx)
{


} // end of onCancel

 

2.

Create an ActionMessages object just as you did in the onDelete() method. Use the add() method to add a message using the general.message.transactionCancelled entry in the ApplicationResource.properties file that you made earlier. You can reference the onDelete() method for code details.

Your code should now look like:

public void onCancel (DataActionContext ctx)
{

  ActionMessages messages = new ActionMessages();

  messages.add("feedback", new ActionMessage("general.message.transactionCancelled"));
  saveMessages(ctx.getHttpServletRequest(), messages );


} // end of onCancel

 

3.

Next add the call to execute the default behavior of a cancel. Again, this code is the same as in the onDelete() method.

You completed onCancel() method should be as follows:

public void onCancel (DataActionContext ctx)
{

  ActionMessages messages = new ActionMessages();

  messages.add("feedback", new ActionMessage("general.message.transactionCancelled"));
  saveMessages(ctx.getHttpServletRequest(), messages );

  if (ctx.getEventActionBinding() != null) ctx.getEventActionBinding().doIt();


} // end of onCancel

 

4.

The one thing left to do to make the onCancel work is to set the type variable to "cancel" when the user clicks the button. If you recall, this is how you made the edit and create buttons work from the browseCustomersAction. You will add the same type code but this code goes in the editCustomersAction.

Open the editCustomersAction and add an onRollback() method that accepts a DataActionContext object as a parameter.

The method shell should be as follows:

public void onRollback (DataActionContext ctx)
{

}

 

5.

Add code to set an attribute names type on the HttpServletRequest Session to the value "cancel."

The code is:

ctx.getHttpServletRequest().getSession().setAttribute("type", "cancel");

The method should now look like:

public void onRollback (DataActionContext ctx)
{

  ctx.getHttpServletRequest().getSession().setAttribute("type", "cancel");


} // end of onRollback

6.

Add code to execute the default behavior by calling the doit() method, as you have in other methods.

The complete onRollback method should be:

public void onRollback (DataActionContext ctx)
{

  ctx.getHttpServletRequest().getSession().setAttribute("type", "cancel");

  if (ctx.getEventActionBinding() != null)
  {
    ctx.getEventActionBinding().doIt();
   }

} // end of onRollback

 

7.

You can now test the application to see the delete confirmation and transaction canceled messages.

Right-click the browseCustomers node in the Struts diagram and choose Run from the context menu. Remember that if you choose to delete a customer, make sure that the customer ID is greater than 200.

Converting Middle-Tier Errors to Struts Messages

Back to Topic List

Struts and Oracle ADF work together to pass messages from the middle tier to the client application. If there is an error on the middle tier, Oracle ADF passes the message to the Struts layer, which passes it on to the client. These messages are passed as ActionError messages. These messages will be displayed just as they are passed from the middle tier.

There are two problems with passing these messages directly to the client. The first is that the messages are from the middle tier and will probably not be clear to the user. The second is that these messages are not internationalized. In other words, whatever language is used in the middle tier is the language in which the message will be displayed. If the user of the application is using another language, the message won't be that useful.

The purpose of the next few tasks is to intercept a few known error messages from the middle tier and exchange them for messages stored in the ApplicationsResource.properties file. By intercepting the messages, we provide clearer—and more internationalizable—messages to the user.

You will also create a page that is designed to show the error message from the delete function. It will do two things. The first is to provide a page that shows only the error message. In our previous example, we displayed the message on the browseCustomers page. That is a good practice if the message is information based. If the message is a true processing error, it's a good idea to display it on a separate page where it cannot be ignored. We will also use this page to roll back the transaction that was in error before continuing in the application.

To make this process work, you will also override the findForward() method to check for errors. If you find one, change the forward to point to the error page.


Intercepting the Middle-Tier Message

During the Struts Lifecycle processing, Struts calls the reportErrors() method on the the lifecycleContext. This is going to be the method that we override to check for and intercept error messages created in the DataBindingControl or middle tier. We will override this method in the Action class where we anticipate the error.

For our example, we will use an error that could be caused trying to delete a customer who has associated orders. Our business rules do not allow the deletion of customers if they have orders. Because the error condition we are intercepting can occur when a customer is being deleted, the code goes in the Action associated with that function. In our case, that is the SureAction class.

Back to Topic List

1.

Override the reportErrors() method in the SureAction class. JDeveloper provides a tool option that makes overriding inherited methods easy.

Choose Tools | Override Methods from the main menu. Select reportErrors(DataActionContex):void from the list and click OK.

This will add the following code to the SureAction class:

protected void reportErrors(DataActionContext actionContext)
{
// TODO: Override this oracle.adf.controller.struts.actions.DataAction method
super.reportErrors(actionContext);
}

 

2.

The first thing to do is get the DCBindingContainer from the actionContext.. Because this method is always called during the lifecycle, we want to check for errors before we do any processing. If there are no errors, simply return to the calling method.

Here is the code to get the BindingContainer and check for errors.

protected void reportErrors(DataActionContext actionContext)
{
// TODO: Override this oracle.adf.controller.struts.actions.DataAction method

DCBindingContainer container = actionContext.getBindingContainer();
ActionErrors errors = new ActionErrors();

if (container == null || errors == null)
{
  return;
} // end if container == null

super.reportErrors(actionContext);
}

 

3.

Now that we know there are errors, we need to build and ArrayList of the errors or exceptions in the container. There is a method in the container which returns an ArrayList of exceptions. The method is getExceptionsList(). After you have the list, create an if statement to check that this list is not null. You will place all of the list processing within this check.

If there are errors that are not from the middle tier, you want to process them using the super.reportErrors() method. To accommodate this test, create a Boolean variable that you can set when you find a Jbo error. Set the default value to false.

Here is the code for getting the ArrayList and checking for null:

protected void reportErrors(DataActionContext actionContext)
{
// TODO: Override this oracle.adf.controller.struts.actions.DataAction method

DCBindingContainer container = actionContext.getBindingContainer();
ActionErrors errors = new ActionErrors();

if (container == null || errors == null)
{
  return;
} // end if container == null

ArrayList runtimeErrors = container.getExceptionsList();
if (runtimeErrors != null)
{

  boolean hasJboErrors = false;

} // end of runtimeErrors

super.reportErrors(actionContext);
}

 

4.

We need to add some logic inside the test for errors. The logic will loop through all the errors in the ArrayList looking for JboException errors. This is the type of error that will be added to the list if there is a constraint violation, like deleting customers when they have orders.

Add a loop to loop through the errors.

protected void reportErrors(DataActionContext actionContext)
{
// TODO: Override this oracle.adf.controller.struts.actions.DataAction method

DCBindingContainer container = actionContext.getBindingContainer();
ActionErrors errors = new ActionErrors();

if (container == null || errors == null)
{
  return;
} // end if container == null

ArrayList runtimeErrors = container.getExceptionsList();
if (runtimeErrors != null)
{

  boolean hasJboErrors = false;

  for (int i = 0; i < runtimeErrors.size(); i++)
  {

 

  } // end of errors loop

} // end of runtimeErrors

super.reportErrors(actionContext);
}

5.

Next we need to cast the exception to an instance of Throwable and check to see if it is an instance of JboException. If it is a JboException, we want to process it.

Here is the code:

protected void reportErrors(DataActionContext actionContext)
{
// TODO: Override this oracle.adf.controller.struts.actions.DataAction method

DCBindingContainer container = actionContext.getBindingContainer();
ActionErrors errors = new ActionErrors();

if (container == null || errors == null)
{
  return;
} // end if container == null

ArrayList runtimeErrors = container.getExceptionsList();
if (runtimeErrors != null)
{

  boolean hasJboErrors = false;

  for (int i = 0; i < runtimeErrors.size(); i++)
  {

    Throwable ex = (Throwable) runtimeErrors.get(i);
    if (ex instanceof JboException)
    {

 

    } // end of instanceof JboException

  } // end of errors loop

} // end of runtimeErrors

super.reportErrors(actionContext);
}

6.

Now that we know we have a JboException, we need to cast the exception to a JboException so that we have access to all of the methods in that exception class. Once it is a JboException object, we can use the getErrorCode method to interrogate the message number.

The error code we are looking for is JBO-29000. If we find that error, this first thing we want to do is set hasJboErrors to true.

protected void reportErrors(DataActionContext actionContext)
{
// TODO: Override this oracle.adf.controller.struts.actions.DataAction method

DCBindingContainer container = actionContext.getBindingContainer();
ActionErrors errors = new ActionErrors();

if (container == null || errors == null)
{
  return;
} // end if container == null

ArrayList runtimeErrors = container.getExceptionsList();
if (runtimeErrors != null)
{

  boolean hasJboErrors = false;

  for (int i = 0; i < runtimeErrors.size(); i++)
  {

    Throwable ex = (Throwable) runtimeErrors.get(i);
    if (ex instanceof JboException)
    {

      JboException jex = (JboException) ex;
      if ("29000".equals(jex.getErrorCode()))
      {
        hasJboErrors = true;


      } // end of 29000 test

    } // end of instanceof JboException

  } // end of errors loop

} // end of runtimeErrors

super.reportErrors(actionContext);
}

7.

You also need to deal with non-Jbo errors.

If there are errors but they are not Jbo errors, you want to allow Struts to use the default behavior for those errors. To do that, add a check just after the errors loop that calls super.reportErrors() if there are jboErrors. Make sure to remove the default call to super.reportErrors() from the end of the method.

Here is the code:

protected void reportErrors(DataActionContext actionContext)
{
// TODO: Override this oracle.adf.controller.struts.actions.DataAction method

DCBindingContainer container = actionContext.getBindingContainer();
ActionErrors errors = new ActionErrors();

if (container == null || errors == null)
{
  return;
} // end if container == null

ArrayList runtimeErrors = container.getExceptionsList();
if (runtimeErrors != null)
{

  boolean hasJboErrors = false;

  for (int i = 0; i < runtimeErrors.size(); i++)
  {

    Throwable ex = (Throwable) runtimeErrors.get(i);
    if (ex instanceof JboException)
    {

      JboException jex = (JboException) ex;
      if ("29000".equals(jex.getErrorCode()))
      {
        hasJboErrors = true;


      } // end of 29000 test

    } // end of instanceof JboException

  } // end of errors loop

  if (!hasJboErrors)
  {
  super.reportErrors(actionContext);
  } // end of (!hasJboErrors)

} // end of runtimeErrors


}

8.

Next you need to build an error message (just as you did earlier). You will use the deletedCustomerName attribute that you stored on the httpRequest object in the onDelete() method. The code is the same as you used to store the attribute except that you use the getAttribute() method.

After you have the value, build and add the error message to the ActionError object that you created in an earlier step. Make sure you save the errors in the context object using saveErrors().

protected void reportErrors(DataActionContext actionContext)
{
// TODO: Override this oracle.adf.controller.struts.actions.DataAction method

DCBindingContainer container = actionContext.getBindingContainer();
ActionErrors errors = new ActionErrors();

if (container == null || errors == null)
{
  return;
} // end if container == null

ArrayList runtimeErrors = container.getExceptionsList();
if (runtimeErrors != null)
{

  boolean hasJboErrors = false;

  for (int i = 0; i < runtimeErrors.size(); i++)
  {

    Throwable ex = (Throwable) runtimeErrors.get(i);
    if (ex instanceof JboException)
    {

      JboException jex = (JboException) ex;
      if ("29000".equals(jex.getErrorCode()))
      {
        hasJboErrors = true;

        String custName =
        (String)actionContext.getHttpServletRequest()
.getAttribute("deletedCustomerName");
        errors.add ("feedback", new ActionError("error.message.customerHasOrders",
                                custName));
        actionContext.setActionErrors(errors);
        saveErrors(actionContext);


      } // end of 29000 test

    } // end of instanceof JboException

  } // end of errors loop

  if (!hasJboErrors)
  {
  super.reportErrors(actionContext);
  } // end of (!hasJboErrors)

} // end of runtimeErrors


}

9.

Here is the completed method:

protected void reportErrors(DataActionContext actionContext)
{
// TODO: Override this oracle.adf.controller.struts.actions.DataAction method

DCBindingContainer container = actionContext.getBindingContainer();
ActionErrors errors = new ActionErrors();

if (container == null || errors == null)
{
  return;
} // end if container == null

ArrayList runtimeErrors = container.getExceptionsList();
if (runtimeErrors != null)
{

  boolean hasJboErrors = false;

  for (int i = 0; i < runtimeErrors.size(); i++)
  {

    Throwable ex = (Throwable) runtimeErrors.get(i);
    if (ex instanceof JboException)
    {

      JboException jex = (JboException) ex;
      if ("29000".equals(jex.getErrorCode()))
      {
        hasJboErrors = true;

        String custName =
        (String)actionContext.getHttpServletRequest() .getAttribute("deletedCustomerName");
        errors.add ("feedback", new ActionError("error.message.customerHasOrders",
                                custName));
        actionContext.setActionErrors(errors);
        saveErrors(actionContext);

      } // end of 29000 test

    } // end of instanceof JboException

  } // end of errors loop

  if (!hasJboErrors)
  {
  super.reportErrors(actionContext);
  } // end of (!hasJboErrors)

} // end of runtimeErrors


}

10.

Add the error message that you used in building the error to the ApplicationResources.properties file. The line should look like:

error.message.customerHasOrders=Customer {0} has outstanding Orders and cannot be deleted.

Save the file when you are done.

 


Creating the showErrors Page

In the previous task, you added code to the sureAction that intercepts a Jbo error message and stores it as a Struts ActionError. If we were to stop here, the error would be displayed on the browseCustomers page just as the other messages that you have created. This would be fine, but it still leaves the possibility that the user could ignore, or not notice, the error message. Since this error really needs to be brought to users' attention, we need to create a page just for displaying the error. This page will be called showErrors.

Back to Topic List

1.

You already created a node on the Struts diagram called showErrors. In this step, create the JSP to associate with this node.

Double-click the showErrors node and accept the default name /showErrors.jsp.

 

2.

In the visual editor, drag a Struts html:errors tag to the top of the page.

 

 

3.

Add some text just below the html:errors tag that says "Click OK to continue."

 

 

4.

Add a Rollback button to the form and change the value property to "OK."

To add the button, select Rollback from the Operations node in the Data Control palette and drag it as a Button with Form to the page..

Remember that dragging this built-in creates the button and adds the built-in to the data control bindings for this page.

5.

The page should now appear as follows:

6.

This page will now display messages in the error stack and perform a rollback operation when the user clicks OK.

Overriding the findForward() Method

The next task in this error-handling process is to change the default navigation of the sureAction to go to the showErrors page if there are errors. This is a useful technique for programmatically managing navigation. In our example, we are navigating based on an error condition. However, you can use this technique for any conditional logic. For example, you could navigate to specific pages depending on a user's name or privilege, or even on the time of day.

Back to Topic List

1.

Open the SureAction class and override the findForward() method just as you did for the reportErrors() method.
Hint: Use the Override Methods option in the Tools menu.

protected void findForward(DataActionContext actionContext)
throws Exception
{
// TODO: Override this oracle.adf.controller.struts.actions.DataAction
// method

} // end of findForward

 

2.

Add an if statement to check for errors.

protected void findForward(DataActionContext actionContext)
throws Exception
{
// TODO: Override this oracle.adf.controller.struts.actions.DataAction
//method

if ( hasErrors(actionContext))
{


}


} // end of findForward

 

3.

If there are errors, change the forward to "hasError". Use the setActionForward() method to set the forward on the actionContext. You will add that forward to the Struts diagram in a few steps.

protected void findForward(DataActionContext actionContext)
throws Exception
{
// TODO: Override this oracle.adf.controller.struts.actions.DataAction
// method

if ( hasErrors(actionContext))
{
  actionContext.setActionForward("hasError");
}


} // end of findForward

 

4.

If there are no errors, use the default behavior by calling super.findForward(actionContext). Add an else statement that calls this method

protected void findForward(DataActionContext actionContext)
throws Exception
{
// TODO: Override this oracle.adf.controller.struts.actions.DataAction
// method

if ( hasErrors(actionContext))
{
  actionContext.setActionForward("hasError");
}

else
{
  super.findForward(actionContext);
}


} // end of findForward

 

5.

The completed method should look like:

protected void findForward(DataActionContext actionContext)
throws Exception
{
// TODO: Override this oracle.adf.controller.struts.actions.DataAction
// method

if ( hasErrors(actionContext))
{
  actionContext.setActionForward("hasError");
}

else
{
  super.findForward(actionContext);
}


} // end of findForward

This method will now set the forward to "hasErrors" if it finds errors on the context

 

6. The next step is to add the forward to the Struts diagram. Open the Struts diagram and add a forward from the /sure node to the /showErrors node. Name the forward "hasError". This will be the path that Struts will take if there are errors found in the SureAction.
7.

When you get to the showErrors page, you need to provide navigation back to the browseCustomers page when the user clicks OK. Recall that the OK button is really a Rollback function and the name of the button is event_Rollback. That means that when the user clicks the button, Struts will call the Rollback built-in and then navigate to a forward named Rollback if it exists.

Create a forward from the /showErrors node to the /browseCustomers node and name it Rollback.

 

8.

The last step in making the Rollback work is to reset the TopLink transaction state. Because of the way TopLink manages its Unit of Work, the transaction needs to be reset as part of this Rollback.

The transaction reset code goes in the Action associated with the /showErrors DataPage.

Create a DataAction for the /showErrors page. (right-click /showErrors on the structure diagram and choose Go To Code from the context menu; accept the default name).

 

9.

Override the handleLifecycle method and add a call to resetState().

The completed method should look like:

protected void handleLifecycle(DataActionContext actionContext) throws Exception
{
// TODO: Override this oracle.adf.controller.struts.actions.DataAction method
super.handleLifecycle(actionContext);
DCDataControl tlDC = actionContext.getBindingContext().findDataControl("CustomersWsViewDataControl");
tlDC.resetState();
}

When prompted, add the following imports:

import oracle.adf.controller.struts.actions.DataForwardAction;
import oracle.adf.model.binding.DCDataControl;

 

10.

The Struts diagram should now be as follows:

11.

Test your application.

Right-click the browseCustomers node in the Struts diagram and choose Run from the context menu.

Try to delete customer 101. This customer has orders and should throw an error. Next try to delete a customer in the 200 range. This delete should be successful.

 

Enhancing the User Interface

The user interface is the part of the application that the user sees and interacts with. In building the application so far, you have been concerned solely with the functionality of the application: what it does and how it does it.

In the next two tasks, you will add a standard heading to each page, make the labels on each of the pages internationalizable (using the ApplicationResources.properties file), and apply a style sheet to all of the pages.

Back to Topic List


Creating and Using a Header Page

It is important to have a consistent look across all pages of an application. You could modify the HTML of each page to include common elements, but doing so requires some work if there are a large number of pages. It would also take considerable maintenance if any of the elements need to be changed.

A simple way of including common elements is to create a JSP page that includes the common header portions of the application and include this page in each of the other pages.

Back to Topic List

1.

Create a new page using the Struts diagrammer and name it /header.jsp. Because this page won't be in the flow of processing, select simple Page from the Component palette.

 

2.

Double-click header.jsp to open the JSP visual editor.

 

3.

Enter the text Customer Information Maintenance on the first line of the page.

 

4.

Select the text and select Heading 1 from the block format (or style) list.

 

5.

Another way to manage consistency is to use Cascading Style Sheets (CSS).

You can add a CSS to your page by selecting one from the CSS list in the Component palette.

Select CSS from the list on the Component palette, and then click JDeveloper. This will add the CSS to the page.

Save the page.

 

6.

The page should look like the following:

Now that you have a header for your application pages, you need to put a hook to that page in each of the pages in the application.

 

7. Open browseCustomers in the visual editor. Select JSP from the list on the Component palette. From the list of available components, drag JSP:include to the top of the browseCustomers page just before the errors tag.

When you drop the JSP:include tag, you will be prompted to specify the file to include. Click the Browse button and select the header.jsp file. Click OK to accept the file.

6.

Run the application to see that the header is included on the browseCustomers page.

 

7. Add the same header to all the pages in your application. Run the application to see that the header is included on all the application pages.


Internationalizing Field Labels

Using JDeveloper, we have created the labels in our application by using JSTL to bind the fields to the iterator metadata. This is a good technique because you don't have to manually create labels for each of the fields on your databound pages. The negative side of this approach is that the pages are not internationalizable. If a user sets the browser to a language other than the default language, the labels will still appear in the default language because they are not being populated from the ApplicationResources.properties file.

In this task, you will remove the default labels and replace them with bean:message tags that use the ApplicationResources.properties file.

 

Back to Topic List

1.

Let's start by adding bean:message tags to the browseCustomers page.

Open the browseCustomers page in the visual editor.

The default labels are in the first row of the read-only table. The columns should be in the following order:

  • Customer ID
  • First Name
  • Last Name
  • Email

The labels are the following:

Select and delete each of the labels.

 

2.

Although you can add the bean:message tags next, it is easier if you have already created the entries in the ApplicationResources.properties file. Create the following entries in the ApplicationResources.properties file to support the label tags.

customers.customerId=Cust Id
customers.firstName=First Name
customers.lastName=Last Name
customers.email=eMail

Save the file after you have made the entries.

 

3.

Next add a bean:message tag from the Struts Bean Component palette for the Customer ID field.

Click in the table where you want to place the bean:message tag (the second column of the first row), and then click the bean:message tag in the Component palette

 

4.

Set the bean:message to use the value of customers.customerId in the ApplicationResources.properties file by selecting that value for the key property of the bean. You can double-click the bean and select the key value in the dropdown list of the pop-up window, or you can use the Properties Inspector to set the value.

Set the style of the bean:message tags to Heading 4 using the toolbar item.

 

5.

Repeat these steps for each of the field labels in the browseCustomers page.

 

6. Next change the editCustomers page to use bean:message tags for the labels. The labels for the fields on the editCustomers page are the same (and are in the same order) as the labels on the browseCustomers page.

The fields are:

  • Customer ID
  • First Name
  • Last Name
  • Email

Because the fields are the same, you can use the same entries in the ApplicationResources.properties file that you used for the browseCustomers page.

Make sure to set the style of the bean:message tags to Heading 4 using the toolbar item.

 

7.

Next change the sure page to use bean:message tags for the labels. The labels for the fields on the editCustomers page are the same (and are in the same order) as the labels on the browseCustomers page.

The fields are:

  • Customer ID
  • First Name
  • Last Name
  • Email

Because the fields are the same, you can use the same entries in the ApplicationResources.properties file that you used for the browseCustomers page.

Make sure to set the style of the bean:message tags to Heading 4 using the toolbar item.

These are the only pages with field labels, so you are finished changing abels.

 

8.

Next let's add some titles to each of the pages. You will use bean:message tags again, just as you did to replace the field labels.

In anticipation of the page titles that you are going to add, make the following entries in the ApplicationResources.properties file:


page.editCustomers=Edit Customer Information
page.browseCustomers=Browse Customer Information
page.deleteCustomers=Delete Customer
page.deleteError=Application Errors

 

9.

Edit each of the pages in your application and add the correct page title using bean:message tags. Add the bean:message tag just after the JSP:include tag that you added to display the header.jsp.

Set the style of these heading tags to Heading 3.

 

10.

Run the application to see that each of the pages has the correct page heading.

Your application should now have a consistent look across all pages and should be internationalizable.

 

Conditionally Displaying a Field

Back to Topic List

In some cases, you may not want to display a field based on a condition within the application. An example in our application is the Customer ID field. This field should not be updateable. In addition, the column is automatically populated by the middle tier when the row is created. Because it is populated at the time of creation, we don't want to display the field when the user is creating a row. Because we use the same page for edting and creating customers, we will add some conditional display logic to the editCustomers page.

1.

Open the editCustomers page in the visual editor.

Place your cursor just after the customerId field, which is in the first row and second column of the table.

Click the c:choose component in the JSTL Core section of the Component palette. You will be prompted to add a when condition.

Click New in the dialog box to create an attribute named "test."

Double-click inside the Value property of the test attribute. This is where you will specify the condition to check. In our case, you will check for a Boolean value of ${bindings.Rollback.actionEnabled}.

As you enter the text, pause for a second or two and JDeveloper will provide a list of valid values for each part of the string. Make sure to check the code for the closing "}". The result should look like the following:

 

2.

When ${bindings.Rollback.actionEnabled} is true, the form is in "create" mode. When this is true, we want to display a simple message in place of the field to tell the user that this field will be populated on creation of the row.

Place your cursor inside the jstl:when item, in the white space on the far right side of the tag. Enter the text ID is generated on creation of the row.

The tag should now look like the following:

 

3.

Next you need to add the c:otherwise side of the condition. This is the tag that will be executed if the Rollback is not enabled. This is where you will display the Customer ID field.

Click the c:choose tag on the page. Click the JSTL Core c:otherwise component in the Component Palette. This adds the c:otherwise condition to the c:choose tag.

 

4.

When the Rollack is not enabled, you want to display the Customer ID field, so drag it to the white space inside the c:otherwise tag.

The tag should now look like the following:

The page will now display the Customer ID only if users are editing the row. If they are creating a new row, they will see the message "ID is generated on creation of the row" in place of the Customer ID field.

 

5.

There is one last thing we want to do. The Customer ID field should not be editable. To make it display-only:

  1. Select the Customer ID field.
  2. Click in the disabled field in the Properties Inspector.
  3. Select True from the list.

The field will now be display-only on the Edit page and will not appear at all on the Create page.

The page should look like the following:

 

6.

You can now run the application and test the editing, deletion, and creation of rows.

 

Back to Topic List

In this workshop, you created a sample application that uses a number of standard techniques that will prove useful in creating any Struts application that uses Oracle ADF and JavaServer Pages. The techniques were: