This
workshop shows you how to build applications using Oracle Application Development
Framework (Oracle ADF). The technologies that you use are Oracle ADF, Struts,
and JavaServer Pages.
In this workshop, you build a Web-based
application that provides for maintenance of customer information. You start by
building the business services based on Oracle ADF Business Components. You then
build the client portions of the application.
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.
Scenario
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 internationalizable so that the
application can run in multiple languages.
Add a database sequence and database
trigger to automatically 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; create trigger CUSTOMER_INS_TRIG before
insert on CUSTOMERS for each row begin select CUSTOMERS_SEQ.nextval into :new.CUSTOMER_ID
from dual; end; /
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 and database trigger 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.
The Customer Maintenance application
is based on a rather simple data model. In includes only one table, the Customers
table. Oracle ADF provides a framework that makes creating and managing the persistence
layer of an application easy. This framework is Oracle ADF Business Components
(ADF BC). The framework provides built-in validation rules and hooks for your
customization of the rules and standard behaviors. In most cases, the default
behavior is sufficient; when it is not quite what the user needs, you can override
whichever behaviors you choose.
Because we use the default
behaviors, we can use the ADF Business Components Wizard to create the business
components we need for our application. In the next few steps, you create the
default business components that you use in the Customer Maintenance application.
If
you would like to see a demonstration of the following tasks, clickhere.
Create a new Application workspace
to hold the business model components:
Select File > New,
and then select the Application workspace node.
Change the name of the
application to CustomerMaintenance and accept the other defaults.
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:
It is easy to create a default business
service layer using the ADF BC Wizard. After you create the default components,
it is usually necessary to customize the view components for your specific application.
For
our application, we want only the following attributes available to the client
application:
CustomerId
CustFirstName
CustLastName
CreditLimit
CustEmail
Double-click
CustomersView to open the View Object Editor. Select the Attributes node on the
left side of the editor. On the far-right side, select all of the attributes except
the ones listed above. Click the single left-pointing shuttle button to remove
these attributes from the list. When you are done, the list should appear as follows:
To
see a demonstration of how this was done, clickhere.
2.
Click OK to accept the changes you
made to the attribute list.
JDeveloper includes
a built-in Business Components browser for testing business components. The browser
works without requiring you to build any client code.
To invoke the
browser, right-click the application module that you just created in the System
Navigator, and then select Testfrom the context menu.
2.
The first step of the browser is to
select a database connection to use for this test. Select the database connection
that you used to create the default objects, and then click Connect.
3.
Double-click the CustomersView1 node
in the browser to see the results of the components that you just built.
Now that you have a business service
model, you can build a basic page flow structure for your application. 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 that
is complete, you develop page content. As you move through the process, you revisit
the page flow and make additions and changes to make the application more robust.
At the beginning of the process, you create the basic components (or building
blocks) that are the foundation of the application.
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:
Browse page
Edit page
Sure page
Errors
page
You add these DataPages using the Struts diagrammer
in JDeveloper.
Open the Struts
diagram (if it is not already open) as follows. Right-click the ViewController
project and select 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 you 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, select JSP.
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 at the bottom of the Component palette that you just used.
They are the Components tab and the Data Control tabs. 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 AppModuleDataControl node to see the ADF View components
that are available. In this case, there is only one ADF View component: CustomersView1
(which you created earlier).
If you expand that node, you see all of the
individual data controls that are available for your use.
5.
The browseCustomers page displays
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
component on the Struts Page Flow diagram.
Select the CustomersView1 node
on the DataControl palette. 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 CustomersView1 component to the browseCustomers.jsp
editor window.
NOTE: Although you have just added data components to a JSP, the starting
point of this page is the Struts DataAction. The Struts DataAction prepares
the model data with the business service layer so that when the JSP is
rendered, the data is available for the binding objects to iterate over.
Make sure to follow the instructions in the next step to run your page.
6.
You have just created an ADF data-aware JSP. To test your new JSP, go
back to the Struts PageFlow diagram and right-click the browseCustomers
node. Select Run from the context menu to launch an internal server that
runs your page.
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 CustomersView1.
Expand the AppModuleDataConrol node in the Data Control palette. Expand
the CustomersView1 node and then the Operations node. Select 'Button with
Form' as the Drag and Drop as value. Select the Previous Set component
and drag it to the browseCustomers.jsp editor window, just below the data
table. Repeat these steps for the Next Set component, except select 'Button'
as the Drag and Drop as value.
After
you have added the buttons, select the Form tag and change the Action property
to browseCustomers.do. This property tells Struts what page to run when the form
is submitted. In this case (as in most), the form you want to call is the same
as the form that contains the buttons. This is the page that contains the built-in
methods.
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 between 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.
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 the editCustomers JSP.
2.
Add an Input Form data component from the Data Component palette as follows:
Select CustomersView1, select Input Form from the "Drag and Drop As"
list, and drag the CustomersView1 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: Previous Set and Next Set. Make sure you are
in the editCustomers 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.
Change the Commit
button value to "OK".
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.
To enable the buttons all the time, remove the following code
from the button definitions.
Note: To edit the
code, click the Source tab at the bottom of the editor window.
6.
Test the page just as you tested
the browseCustomers page earlier. Right-click the editCustomers node on the Struts
Page Flow diagram and select run.
Notice that there is only one row available
and that there is now facility to scroll through the customer rows. This is deliberate.
In the next few steps, you 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.
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.
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 ADF Business Components
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:
Open the browseCustomers editor window.
Scroll to the right side of
the page.
Right-click inside the last column in the table.
Select
Table > Insert Rows Or Columns from the context menu.
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:
Expand the CustomersView1 node in the Data Control palette.
Expand
the Operations node.
Select setCurrentRowWithKey(String).
Select
FindRowLink in the "Drag and Drop As" list.
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.
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 select Save Picture As). 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 pick or create an images directory in the public_html
directory.
The result should appear like the following:
In
the next step, you remove the select
text and set the border for this image button.
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 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.
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. Change the
name of the forward to Edit.
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.
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.
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 on the form tag that contains the Previous 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 have 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.
3.
You now have the buttons and navigation in place to insert a new row. However,
we want to use a sequence to populate the customerId attribute. This eliminates
requiring users to create a unique value for this field; we'll do it for them.
Because
we used Oracle ADF Business Components for the data model, using a database sequence
for this is fairly simple.
Expand the Model node in the Application Navigator.
Within the Model node, expand Application Sources > model. Double-click the
Customers entity object.
In the Attributes node, select the CustomerId attribute.
Change the Type property to DBSequence. Make sure the Updateable option is While
New, and then click Refresh After Update.
4.
Test the new functions by running the browseCustomers page and creating a new
row. Notice that when you create a row, the CustomerId field is populated with
a number that doesn't look like a valid CustomerId. This is because ADF Business
Components is showing you a place holder for the CustomerId. This placeholder
is used by the framework until the row is committed. After a successful commit,
the field is repoplulated from the value stored in the database.
Later in
this workshop, you will change the display properties of the field to make it
more user friendly. For now, we'll leave it the way it is.
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.
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.
Add the Remove button to the browseCustomers page just as you added the
Edit button in an earlier step:
Open the browseCustomers JSP.
Drag a setCurrentRowWithKey operation as a Find Row Link to the last
column in the table.
Add the Remove button by saving this button
to your local disk drive (right-click the image and select Save Picture
As). 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 pick or create an images directory in the public_html
directory.
The result should look like the following:
In the next step, remove the select
text and set the border for this image button.
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 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:
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.
The confirmation page in our application
is named sure.jsp. The page will
be a databound page with a Read Only Form based on the CustomersView1 view object.
Create
the page just as you created the Edit page earlier.
If you need help with
this step and would like to see a demonstration, click here.
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 CustomersView1). 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:
With your JSP page active in the Visual Editor, click the UI Model
tab of the Structure window.
Right-click the root node in the tree and select Create Binding >
Actions > Action from the context menu.
Select your data control in the Data Collection list.
Select Commit as the action using the Select an Action drop-list.
Click OK to continue.
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.
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.
6.
The Page Flow diagram should now look
something like the following:
7.
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.
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.
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.
Right-click the browseCustomers node on the Page Flow diagram.
Select
Go To Code from the context menu.
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:
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) {
if (actionContext.getEventActionBinding() != null)
actionContext.getEventActionBinding().doIt(); }
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 the button that 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.
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.
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.
Select "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.
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.
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
to following code within the if ( !hasErrors) test.
ActionMessages
messages = new Action Messages();
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.
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 CustFirstName, and the other is set to the
value of CustLastName.
} // end of if ( !hasErrors(actionContext) } // end of if (events !=
null && events.size() > 0) } // end of findForward()
7.
You stored the transaction type in a session variable in a previous step.
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:
String type = (String)actionContext.getHttpServletRequest().getSession().getAttribute("type");
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:
if (type == "edit")
{
messages.add("feedback",
new ActionMessage("customers.message.update.success",
firstName, lastName));
}
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:
List events = actionContext.getEvents();
if (events != null && events.size() > 0) { if ( !hasErrors(actionContext)
) // if there are errors, don't create messages {
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:
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.
List events = actionContext.getEvents();
if (events != null && events.size() > 0) { if ( !hasErrors(actionContext)
) // if there are errors, don't create 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:
Now the editCustomers page will store context-sensitive
messages in an ActionMessage object.
14.
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.
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.
Enter "messages" as the value for the
id property and "true" for the value of the messages property.
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 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:
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>.
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.
You
have add 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.
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 two tasks, 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.
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.
The code you need to add goes in the
Action associated with the sure.jsp.
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 select 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.
In the next steps, you will add the code to:
Build a delete message
that includes the name of the customer that was deleted
Execute the standard
delete built-in function
Check for errors
Commit the transaction
if there were no errors
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 classes
to support this (and other) objects. Accept the import proposals.
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 CustFirstName and CustLastName.
You
will need to import the following two classes as JDeveloper prompts you:
Now that you have stored the customer
name, you can execute the built-in delete function. There is a method within Struts
that provides an easy way to execute the default behavior. The method is called
doIt() and is part of the EventActionBinding.
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 that you can
use to check for errors. Use this Boolean (!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.
Add
this code as the last line within the if (!hasErrors(ctx)) statement.
7.
The 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("CustFirstName"); String
firstName = (binding != null) ? binding.toString() : "";
/** * 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.
You can now test your delete and message.
Right-click the browseCustomers node on the Struts diagram and select 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.
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."
if (ctx.getEventActionBinding() != null)
ctx.getEventActionBinding().doIt(); }
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 select Run from the context
menu. Remember that if you choose to delete a customer, make sure that the customer
ID is greater than 200.
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 clearerand more internationalizablemessages
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.
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.
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.
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:
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.
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.
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-26041.
If we find that error, this first thing we want to do is set hasJboErrors
to true.
You also need to deal with non-Jbo
errors. That is why you created the hasJboErrors
Boolean: so that you could use it in a test.
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.
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.
if (!hasJboErrors) { super.reportErrors(actionContext);
} // end of (!hasJboErrors)
} // end of runtimeErrors
}
9.
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.
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 rename it to "OK."
To add the
button, select Rollback from the Operations node in the Data Control palette and
drag it to the page. Accept the form if JDeveloper prompts you.
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.
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
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.
if ( hasErrors(actionContext)) { actionContext.setActionForward("hasError");
}
else { super.findForward(actionContext);
}
} // end of findForward
5.
This method will now set the forward to
"hasErrors" if it finds errors on the context. The next step is to add
the forward to the Struts diagram.
6.
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 Struts diagram should now be as
follows:
9.
Test your application.
Right-click
the browseCustomers node in the Struts diagram and select 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.
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.
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
the header.jsp node 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 the browseCustomers page 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
With 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.
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
Credit
Limit
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=Customer
Id customers.firstName=First Name customers.lastName=Last Name customers.creditLimit=Credit
Limit customers.email=eMail
(You can copy and paste.)
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 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.
Click here
to watch a demonstration of these steps.
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
Credit Limit
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
Credit
Limit
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.
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:
Select the Customer ID field.
Click in the disabled field in the Properties
Inspector.
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.
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:
Building
basic business services using default Oracle ADF components
Building
a Struts Page Flow diagram using the JDeveloper IDE
Creating
a Delete function and using a Delete Confirmation page
Using an ActionMessage object to convey messages to the user
Adding built-in methods to enable the Delete Confirmation page
Intercepting middle-tier messages and saving them as Struts ActionError objects
Using bean:message tags to internationalize an application