<< Contents< PreviousNext >Index >> Lutris Technologies, Inc.


 

Chapter 5
The DiscRack Example Application

 

 

This chapter introduces the DiscRack application, and uses it as a comprehensive example to illustrate some key concepts of Enhydra application development.

Building and Running DiscRack

Enhydra 3.0 includes the DiscRack application, which is installed to the <enhydra_root>/examples/DiscRack directory. Throughout this chapter, this top-level directory containing DiscRack will be referred to as <DiscRack_root>.

To build and run DiscRack, you need to make the following modifications to the installed files:

Important: DiscRack uses the database and corresponding application data layer described in Chapter 4, "Tutorial: Building Enhydra Applications." Before you can run the application, you must load the database schema, as described in "Loading the Schema." Alternatively, you can load the Microsoft Access database in <DiscRack_root>/discRack/data/discRack.mdb.

To run DiscRack, enter the following commands:

To access the application, enter the URL http://Localhost:5555 in your browser location field. Your browser displays the following screen:

[Picture]

Play around with the application to get a sense for how it works. Click on the "Sign Up!" button to add yourself as user, then add some discs to your inventory. Try viewing your inventory and editing one of the discs.

Process and Preliminaries

Before discussing the workings of the DiscRack application, it is useful to understand how you go about developing an Enhydra application in general. You can adapt the traditional software development process to Enhydra application development to ensure that:

An in-depth discussion of software methodology is beyond the scope of this book, but it is instructive to understand the basic principles and how they apply to the simple DiscRack application, so that you can reap the benefits when developing a more complex real-world application.

The following process is loosely based on Lutris Technologies' Structured Delivery Process (SDP), a rigorous methodology that Lutris developed over the course of many projects. The simplified process described below may be suitable for small projects; for more information on methodology for large, team development projects, see the information on the SDP on the Lutris web site at http://www.lutris.com.

A simplified Enhydra application development process consists of the following steps:

This abbreviated methodology is presented here to illustrate the key aspects of the development process. Complex, real-world applications generally call for a more comprehensive process that includes project milestones, cost analysis, documentation, and so on.

DiscRack Requirements Definition

The Otter family needs a way to track their compact disc collections. Each family member has a CD collection, and they sometimes get mixed up: Otters forget who owns what. They decide that an Enhydra application would be the perfect way to help them manage their CDs. After some discussion, they arrive at a brief requirements statement:

    DiscRack will enable each user to keep track of his or her individual CD inventory; and to add, modify, and delete CDs as needed. The application will keep track of all the pertinent information about each CD, including artist and title.

DiscRack Functional Specification

Briefly, DiscRack will meet its requirements as follows:

Design and Storyboard

The bulk of this step consists of the engineering design for the application, including the design of database schema and corresponding data layer, business logic, and presentation logic. The user interface design can be largely encapsulated by a storyboard.

A storyboard is a visual way of describing a user's navigation paths through the application. It provides an outline of the application's user interface, and a framework from which the rest of the application design can proceed. A conceptual storyboard that is largely an application flow chart is sometimes referred to as a site map, in contrast to a mocked-up HTML storyboard. This book will refer to both as a storyboard.

The storyboard for DiscRack is shown in Figure 5.1.

[Picture]

Figure 5.1    DiscRack Storyboard

You can see from the storyboard that there are five HTML pages in the application. You can also see that the DiscCatalog page that shows the CD inventory is the central page in the application. The first page the user sees will always be the Login page; the last page will always be the Logout page.

DiscRack includes a working storyboard (or application "mockup") in the resources directory. It is a set of static HTML pages that illustrate how the application works. To see the storyboard, load this file in your browser:

This displays DiscRack login page.

Click on the "Login" button to "log in" and see the disc catalog; click on the "Sign Up" button to display the Signup page. Click around on the links, and you can see the rest of the storyboard. The flow of the HTML pages follows Figure 5.1. Of course, none of the back-end logic is activated--all the HTML is static. But the storyboard gives you a good feel for how the application works.

Development, Testing, and Deployment

The remaining steps are development, testing, and finally deployment. Rather than go through the exercise of describing these steps for DiscRack, the rest of this chapter describes the DiscRack application itself.

When you build an application from the top level, the make files create an output directory containing the configuration files and the start script. Also, there will be a lib directory with a JAR file that contains all the application's class files, along with any other files (for example, GIFs or style sheets).

To deploy the application, you just need to copy these files to the server on which you want the application to run, and make the appropriate changes to the configuration files to reflect the new location. Of course, Enhydra must be installed on this server, and you need to have any ancillary libraries (such as your database's JDBC driver) available.

Overview of DiscRack

The DiscRack application consists of 23 classes in nine packages:

Layer/package Class or package name Description
discRack package DiscRack The application object
  DiscRackException A simple base exception class
 
presentation layer/package BasePO An abstract base class for all presentation objects
  DiscRackSessionData A container for session data
  ErrorHandler A class to handle exceptions not caught elsewhere in the application
  DiscRackPresentationException A presentation layer exception class
  presentation.personMgmt package A package that contains classes for managing presentation related to the PERSON table: Register and Login
  presentation.discMgmt package A package that contains classes for managing presentation related to the DISC table: Edit and DiscCatalog
 
Business layer/package DiskRackBusinessException A business layer exception class
  business.person package A package that contains two classes: Person, which represents a person PersonFactory, which has a single method that returns the Person object for a user name
  business.disk package A package that contains two classes: Disc, which represents a disc. DiscFactory, which has methods to return a Disc object for an ID or for the owner's name.
 
Data layer   Described in "Loading the Schema."

The six HTML files are in the resources directory. These correspond to the five HTML pages shown in the storyboard, plus an error page that appears when an error occurs that is not handled by an exception.

The Presentation Layer

The presentation layer includes all of the HTML, Java, and JavaScript that defines the user interface of the application.

Presentation Base Class

All of the Presentation objects in DiscRack are derived from a common base class, BasePO, which is an implementation of the Enhydra interface HttpPresentation. This interface has one method, run( ), which takes the HTTP request as a parameter.

A Presentation base class enables the application to group common functionality in one place. Notice that BasePO is an abstract class, so it cannot be instantiated itself, only sub-classed. Also, some of its methods are declared abstract, so subclasses must implement them.

BasePO has methods to handle some of DiscRack's key tasks:

It is important to realize that you are not required to use a base Presentation class. An alternative is to use the Enhydra Application object to perform common tasks.

The central method in BasePO is run( ), which makes method calls to perform session maintenance and event handling:

Every time a client browser requests a presentation object URL, the application calls this method. Its logic is very simple:

Each of these methods is explained in the following sections.

The run method has a parameter, comms, that is an object containing information about the HTTP request. Its member properties include: application, exception, request, response, session and sessionData. These six properties provide all of the information for the request. For example, you can retrieve session data with getComms( ).sessionData.get( ) and query string parameters with getComms( ).request.getParameter( ).

Session Data and Log In

The basics of Enhydra session maintenance were introduced in "Maintaining Session State." In contrast to the way session information was handled in that previous example, DiscRack stores all its session information in a single object, DiscRackSessionData, and saves that object in the user's session.

DiscRackSessionData is a simple container class that has two properties: a Person object that represents the user and a string called userMessage for error messages such as "Please choose a valid disc to edit." DiscRackSessionData has member properties for these data, and methods to get and set them.

There are several advantages of keeping session data in one object:

The initSessionData( ) Method

The first thing each PO does is to call initSessionData( ). The main portion of this method is shown here:

The first statement in this code snippet gets the session data object, using the session key "DiscRackSessionData." If the session data object exists, it gets type cast to DiscRackSessionData; otherwise, the code creates a new DiscRackSessionData object and saves it to the user's session with set( ).

The loggedInUserRequired( ) Method

BasePO has an abstract method called loggedInUserRequired( ) that returns a boolean value. Thus, every PO is required to implement this method, which indicates whether a user is required to be logged in to access the associated page. In BasePO.run( ), if this method returns true, then checkForUserLogin( ) is called.

The checkForUserLogin( ) Method

The checkForUserLogin( ) method determines if a user has a valid login. If not, then it redirects the browser to the Login page:

Several statements that write debug messages to a log channel have been removed from this code for clarity. The call to getUser( ) is really just a call to getSessionData( ).getUser( ), which retrieves the Person object saved in the current session. If the user has not logged in, or the session has timed out, then this method will return null, and the code will throw a ClientPageRedirectException with the URL to the Login page as the argument to the constructor.

When a client browser is redirected by a ClientPageRedirectException, any parameters from a query string that were available to the original presentation object are lost. So if you want to pass an error message, you must put the information in the user's session or directly into the query string of the redirected URL.

Event Handling

In this context, an "event" refers to the task a user is performing. While you could create a separate PO for each task in an application, in many cases it makes sense to have a single PO handle multiple events. For example, the Edit PO responds to four events: showing the add page, showing the edit page, actually adding a disc to the database, and deleting a disc from the database. The Login PO handles three events: show page, login, and logout.

Setting the Event Parameter

DiscRack keeps track of the event it is processing with the "event" parameter, which is sent in the query string of a request. For example, the URL

specifies the event "showAddPage."

DiscRack illustrates several techniques for setting the event:

Although DiscRack does not demonstrate it, you can also set the event when you throw a PageRedirectException. You use this exception to transfer control from one PO to another. To specify an event, add the string "?event=someEvent" to the URL string passed to the constructor of PageRedirectException.

The handleEvent( ) Method

Once the event is set, the handleEvent( ) method of BasePO performs the actual event handling:

This method gets the "event" parameter from the request query string and calls the appropriate event handler. If it does not find "event" in the request query string, it calls handleDefault( ) which is an abstract method, and so must be implemented by all BasePO subclasses. Otherwise, it calls getPageContentForEvent( ) which returns the string content for the specific event and PO. This method contains the following three lines:

This code uses reflection (defined in the java.lang.reflect package) to call the method in the PO corresponding to the current event. Reflection allows you to call a method whose name is defined at runtime.

The call to toMethodName( ) returns a string "handleXxx" where "xxx" is the current event (for example "handleShowAddPage" for "showAddPage"). The call to method.invoke( ) then calls this method.

Reflection allows BasePO to call methods in its subclasses without knowing in advance the names of the methods. This scheme works as long as the presentation object code follows the appropriate naming conventions: for every event "foo," there must be a method handleFoo( ) in the PO class that needs to handle that event.

HTML Pages

The HTML pages for DiscRack are in the <discRack_root>/discRack/resources directory. Keeping the HTML pages there rather than in the presentation directory cleanly separates the HTML files from the Java files. Although this is superfluous for small applications, it is a key advantage for large applications with a graphic design team and a programming team.

The make files in the presentation layer control how the application uses the HTML files. There are a total of three make files in the presentation layer, one in the top level, and one in each sub-directory. To keep the HTML files in a directory separate from the presentation classes, the make files use the HTML_DIR directive that specifies the relative path to the directory containing the HTML files. For example, in presentation/Makefile, you'll see:

And in presentation/discMgmt/Makefile:

The make rules will also find any HTML files in the presentation directories (for example discMgmt/DiscCatalogScript.html).

The HTML_CLASSES directive indicates the names of the class files that XMLC creates, as explained in "Adding a New Page to the Application."

Notice there is a presentation/media directory that contains only a make file. This directory mirrors the final package structure for the JAR file. A line in the make file copies the GIF into the finished jar file:

Maintaining the Storyboard

The storyboard is initially just a mockup of the application. But with a few simple steps, you can maintain the working storyboard throughout the entire development process. This capability becomes particularly important for large applications created by a team of programmers and graphic designers: each team can work on their part of the application separately from the other.

After the graphic designers complete their work, you can then replace the old, "mock up" user interface with the new improved interface, that may include improved graphics, JavaScript special effects, style sheets, and so on. An example of doing this is illustrated in "Replacing the User Interface."

In addition to keeping the HTML files separate from the Java code, as described in the previous section, there are three steps you have to follow to maintain the storyboard during development:

  1. Define rules to map URLs like Login.html to Login.po

  2. Remove dummy data from the HTML files

  3. Replace JavaScript, if necessary

Each of these steps is described in detail in the following sections.

URL Mapping

In the working storyboard, as in any static HTML pages, hyperlinks reference other HTML pages. That is, the URLs in hyperlinks end in .html. However, in the working application, links to dynamic pages reference presentation object URLs that end in .po.

So, you need to do something to convert the "normal" URLs in the storyboard to .po URLs. You do this by using the XMLC -urlmapping option to map URLs from one form to another. You use this option like this:

To use this option in the make process, you must create an XMLC options file, and then identify the file in the make file with the XMLC_HTML_OPTS_FILE directive. For example:

The presentation/discMgmt/options.xmlc file contains the lines:

When XMLC compiles the files in this directory, it will replace occurrences of the first string (for example, "Edit.html") with the second string (for example, "Edit.po") in hyperlink URLs and FORM ACTION attributes.

Removing Dummy Data

HTML files often contain "dummy" data to make the storyboard pages look more representative of their actual runtime appearance. You need to remove this dummy data from the production application.

Look in presentation/discMgmt/options.xmlc again; in particular, look at the last line:

The -delete-class option tells XMLC to remove any tags (and their contents) whose CLASS attribute is "discardMe." For example, if you look in resources/discMgmt/DiscCatalog.html, you see this HTML:

It's not that we don't love Sonny and Cher: the CLASS attribute in the table row definition marks the row for deletion. Unlike ID, the value of a CLASS attribute does not have to be unique in the page. You can remove all of the dummy in the application with the same "discardMe" value.

Replacing JavaScript

In addition to replacing URLs, you often need to replace JavaScript in the storyboard with JavaScript to be used in the "real" application. For example, resources/DiscCatalog.html contains the following script:

These functions help to keep the storyboard working. At runtime, though, the application needs to use the "real" functions, which are defined in presentation/DiscCatalogScript.html, for example:

Because XMLC views JavaScript as a comment, the URL mapping option will not work on this URL inside the JavaScript function. So, you have to replace it at runtime with the following code in DiscCatalog.java:

This is an example of replacing a node with a node from another document. This implementation uses the XMLCUtil class.

Because this action happens at runtime, it may have a slight effect on performance. If performance is critical, you may wish to replace the JavaScript in the final deployed version of the application.

Maintaining the storyboard seems like additional unnecessary work, but it is worth the work when your HTML is evolving in parallel with the Java code. As an example of the power of a working storyboard, you can exchange the HTML in Disk Rack from the basic HTML to designed HTML.

Replacing the User Interface

Once the graphic design is completed, you can replace the user interface of the application with its final version. DiscRack includes a resources_finished directory containing "finished" versions of the HTML pages, along with a graphic and a stylesheet.

To replace the original storyboard resources with the "finished" resources:

    This ensures that the new JPEG graphics files and the style sheet file are included in the packaged application JAR file.

    The make clean command will remove all the old classes, so that make will completely rebuild the application from scratch.

Now, restart and access the application. You see the new and improved user interface:

[Picture]

Populating a List Box

The DiscCatalog page illustrates how to populate a SELECT list box, which is a common task. First, look at the HTML for the SELECT tag in DiscCatalog.html:

Now look in DiscCatalog.java for the code that populates the list box.

The first line above retrieves the DOM object corresponding to the template OPTION tag. The second line calls getParentNode( ) to get the container SELECT tag. Since the SELECT tag has an ID attribute, this line could have also been:

Then, following some code for populating the table, there is one line to remove the template row.

The other OPTION tags have CLASS="discardMe," which causes XMLC to remove them at build time, as explained in "Removing Dummy Data."

Then, within the for loop that iterates over the discs belonging to the current user, the following lines actually populate the list box:

The first line copies (clones) the template option element into a DOM object of type HTMLOptionElement. The second line sets the VALUE attribute to the value returned by getHandle( ), which is the disc's OBJECTID, an unique identifier. The third (very long) line creates a text node consisting of "artistName: titleName." Finally, the last two lines append the text node to the option node, and then append the option node to the select node.

The resulting runtime HTML will look something like this:

Although this example might seem obscure, it is fairly short, and you can extend its basic functionality to handle more complex situations. For example, you modify it to set the default selection based on a second query.

Populating a Form

When a user chooses a disc from the list box and clicks on the "Edit Disc" button, a form appears that is populated with the existing values for that disc. The user can then edit the values and submit them back to the database.

[Picture]

Here is the HTML for the form elements in Edit.html. The TABLE tags have been omitted for clarity:

In Edit.java, the event-handling method handleDefault( ) calls showEditPage( ) with a null parameter to populate the form with the selected disc's values. Ordinarily, the only request parameter (other than the event type) is the disc ID, accessed by this statement:

These statements also access the other request parameters, but ordinarily they are null (but see the error-handling case discussed later):

Then, a call to findDiscByID( ) retrieves a Disc data object that has that ID:

Then, there is a series of if statements that check the values of title, artist, genre, and isLiked, which are normally null. Therefore, the following statements are executed (the surrounding if statements are not shown for brevity):

These statements use XMLC calls to set the VALUE attributes of the form elements; the values are retrieved from the disc object.

When the user finishes editing, and clicks "Save this Disc Info," handleEdit( ) processes the changes. This method calls saveDisc( ), which attempts to save the new values--if successful, it redirects the client to the DiscCatalog page; if any of the new values are null, though, saveDisc( ) throws an exception. The catch clause then calls showEditPage( ) with an error string and with request parameters.

Note that ClientPageRedirectException is a subclass of java.lang.Error, so it is not caught by the catch clause when it is thrown.

The result is that when a user tries to edit a disc and delete some of the values, the edit page will re-display, maintaining all the non-null form element values, and restoring the previous values to the null-valued form elements. The page will also display the error string.

The Business Layer

The DiscRack business layer is simple, consisting primarily of two packages, Disc and Person, and two corresponding factory classes DiscFactory and PersonFactory. A factory is an object whose primary role is to create other objects.

The Business Objects

The business objects Disc and Person are largely wrappers for the corresponding data layer classes, DiscDO and PersonDO, with get and set methods for each property in the data objects (or column in the database tables). For example, Disc has getArtist( ) and setArtist( ) methods.

The objects in the business layer perform all the interfacing with the data layer. So, if the data layer needs to change, nothing in the presentation layer is affected; conversely, if the presentation layer changes, nothing in the data layer is affected.

DiscFactory has two static methods:

PersonFactory has one static method, findPerson( ), that returns a Person object that has the user name specified in the method's argument. If the method finds more than one person in the database, then it writes an error message to the log channel and throws an exception.

Using Data Objects

To help understand ow DiscRack uses DODS data layer code, look at the findPerson( ) method in PersonFactory. The comments have been removed from this code for brevity.

First, this method instantiates a new PersonQuery object. PersonQuery is a data layer object used to construct and execute a query on the person table. It has a number of setQueryXXX( ) methods for qualifying the query parameters (that is, setting the values to be matched in the WHERE clause of the SELECT statement). For example, the above code calls setQueryLogin( ), with username as a parameter, to set the value to be matched in the LOGIN column.

Next, the method calls requireUniqueInstance( ), which indicates that the query is to return a single row, and will throw an exception otherwise. Then, it calls getDOArray( ), which executes the query, returning an array of PersonDO objects. Finally, the method returns a single Person object returned by the query; if the query did not return any rows, it returns null.


Lutris Technologies
http://www.lutris.com
1200 Pacific Ave., Suite 300
Santa Cruz, CA 95060
Voice: (831) 471-9753
Fax: (831) 471-9754
documentation@lutris.com
<< Contents< PreviousNext >Index >>
© 2000 by Lutris Technologies, Inc. All Rights Reserved