|
Welcome to the Enterprise Java Technologies Tech Tips for March 24, 2004. Here you'll get tips on using enterprise Java technologies and APIs, such as those in Java 2 Platform, Enterprise Edition (J2EE).
This issue covers:
Improving Designs with the MVC Design Pattern
Introducing JavaServer Faces Technology
These tips were developed using Java 2, Enterprise Edition, v 1.4 SDK. You can download the SDK at http://java.sun.com/j2ee/1.4/download-dr.html.
This issue of the Tech Tips is written by Mark Johnson, president of elucify technical communications, and co-author of Designing Enterprise Applications with the J2EE Platform, 2nd Edition. Mark Johnson runs an open forum for discussion of the tips.
You can download the sample archive for these tips. The context root for the application is ttmar2004, and the index.html welcome file indicates how to use the sample code. Any use of this code and/or information below is subject to the license terms.
IMPROVING DESIGNS WITH THE MVC DESIGN PATTERN
Most applications contain code that handles input, output, and logic. Input code retrieves (or at least receives) data, output code presents data, and logic code decides what the application should do next. Small applications typically contain a mix of instructions that perform these activities. For example, a single program loop might read a value, decide what to do with it, and print a result.
As applications grow, they tend to entangle the parts of the code that access and process data, present data, and make decisions. Such applications become increasingly fragile (they break easily), unreliable, and difficult to extend.
In addition, code that intertwines data access, data presentation, and input logic is difficult to maintain. Here are some of the reasons why:
- When classes are tightly coupled to one another, a change in any one of them causes ripple effects everywhere.
- Business logic that is in presentation code or data access code can be reused only by copying and pasting it somewhere else. When you copy and paste a business rule, you have to maintain that rule in two places. And you usually bring some presentation code along, as well. This results in yet another place to have to maintain presentation.
- If the code that accesses or presents data is sprinkled throughout business logic, it's often extremely hard to change data sources or data access methods. You never know if a change to data access logic in one part of the system breaks presentation logic elsewhere.
- Determining what the system needs to do next can be challenging if the data needed to make the decision isn't centralized.
The problems that result from code coupling are particularly common in Web applications. For example, imagine you had an application with a JSP page (call it authenticate.jsp). The JSP page receives a login name and password from an HTML form, authenticates the user, and forwards the request to the application start page or to an error page. Simple, right? Now, imagine that the following additional requirement needs to be met: if a user enters more than three incorrect passwords in ten minutes, the account must be disabled for an hour and the user must be notified by email. Also, the system must show an authenticate page each time the user requests a protected page. The policy on what pages are protected keeps changing.
Get the idea? Before long, you're copying and pasting scriptlets all over your application. Obviously, this makes the application difficult to maintain.
The solve this problem, you need to improve the design of the application. You can do this by separating user input logic and data presentation from data access and business logic. This is the key idea behind the MVC (Model, View, Controller) design pattern.
MVC originated in the late 1970's in the object-oriented language Smalltalk. It remains one of the most popular architectural patterns for object-oriented design.
The MVC design pattern splits an application design into three separate parts:
- The Model handles data and logic. Model code accesses and represents data, and handles business operations. "Business operations" include changes to both persistent business data, and to things like shopping cart contents.
- The View handles output. View code displays data to the user.
- The Controller handles input. Controller code manipulates the Model or changes the view accordingly in response to user input (the Controller can also do both).
These parts have specific relationships to one another:
- The View accesses the Model to retrieve data for display.
- The Controller receives user input, and makes business method invocations on the Model.
- The Controller updates the View, or selects a new View, based on the state of the Model and on the its own View navigation rules.
- The Model provides data to the View, but knows nothing about how that data is presented. The Model also provides business services to the Controller, but knows nothing about user events the Controller might have received.
This strict separation has many benefits:
- You can control a user's sequence of views in a single place: the Controller.
- You can change the business rules in the Model, and change only the affected Views and parts of the Controller. Often, changes to business rules have no impact on Views or Controllers. Even when Views, Controllers, or both are affected, the required changes tend to be minimal, localized, and easy to find.
- You can easily add new Views to the system simply by creating them and wiring them into the Controller.
- You can have multiple Controllers for different types of users.
- You can get data from multiple sources by changing the Model. The other two components are unaffected.
- You can easily control aspect-oriented features of your application such as logging and security. You add features like this by wrapping the Model interfaces or by intercepting Controller invocations.
- You can debug an MVC application more easily because the flow of invocations and events is consistent.
This discussion is just a brief introduction to the MVC architectural design pattern. You can find out more about the MVC architecture in the following resources:
Also, read the following tip, "Introducing JavaServer Faces Technology," to learn about the newly-approved MVC standard framework for J2EE applications. The tip also shows an example of a JSF application -- an application that conforms to the MVC architecture.
INTRODUCING JAVASERVER FACES TECHNOLOGY
Applications that are based on JSP pages, and that follow what's termed a "Model 1 architecture," can typically be developed easily and in a short amount of time. A Model 1 architecture is a good choice if page navigation and business logic are simple, and if the application is unlikely to change significantly. But as discussed in the tip "Improving Designs with the MVC Design Pattern," an application composed entirely of JSP pages can quickly become difficult to manage.
Early in the history of J2EE, enterprise application developers learned that an MVC architecture improved their designs. They used JavaBeans for the application Model, and JSP pages for views. They also introduced a Controller servlet for user interaction, business logic dispatch, and view selection. This came to be known as a "Model 2 architecture." Soon after, various groups of developers created a variety of commercial and open-source MVC Web application frameworks: notably Struts, Velocity, and WebWork. These popular platforms have revolutionized server-side application development. The only thing lacking was a standard -- until now.
JavaServer Faces (JSF) technology is the newly-approved user interface framework for J2EE applications. It is particularly suited, by design, for use with applications based on an MVC architecture. JSF began as Java Community Process (JSR-127), and was approved earlier this month (March 2004) by the Executive Committee. A reference implementation (considerably different from the Beta releases) is now available for download at the JavaServer Faces Technology Download page. The JSF Reference Implementation is free to use and redistribute. (Read the license that comes with the distribution.) An open-source implementation called Smile is also available from SourceForge. JSF is still not a standard part of J2EE, however it is likely to be supported by all major platform vendors in the near future.
The committee that created the JSF specification included many of the experts that created the existing frameworks, so JSF complements, rather than supersedes, existing solutions. JSF brings the benefits of standardization to the world of MVC application frameworks.
JSF Features
JSF's scope is ambitious. It provides the following tools to application designers and developers:
- A standard GUI component framework for tool integration
- Simple, lightweight classes to represent input events and stateful, server-side GUI components
- A set of HTML form input elements that represent the server-side GUI components
- A JavaBeans model for translating input events to server-side behavior
- Validation APIs for both the client side and server side
- Internationalization and localization
- Automatic view presentation, customized to client type (for example, browser or media type)
- Integrated support for accessibility
The Basic Idea Behind JSF
JSF defines a set of APIs that model GUI components on the server. These components can have state, for example, a component called "phoneNumber" would contain a string representing a phone number. An application developer focuses on coding application-specific modules. At runtime, the framework interacts with the user, dispatches and generates views, and invokes business functions.
JSF usually uses JSP pages to generate browser-specific HTML. The input elements in the JSP pages correspond to the server-side GUI components. Although JSP pages are the most common form of presentation, JSF is specifically designed to be independent of JSP pages. A JSF application could use any presentation technology to interact with a user.
To create a JSF application, you typically perform the following steps:
- Define and implement the application Model classes
- Describe the Model to the framework
- Create application Views using JSP pages
- Define data validation rules
- Define View navigation for the Controller
The remainder of this tip covers these steps in detail, using as examples, the sample code that accompanies the tip. The sample code is a very simple JSF application. The application requests three values from the user: a string, an integer, and a real number. It validates those values, and then provides error messages or reports that the values are valid.
Defining the Model
Application Models in JSF are implemented as server-side JavaBeans. The Model class represents a collection of data from the application, and operations on that data. The application Model doesn't have to be a single class, some Models have hundreds of classes.
The Model for the sample code is a simple JavaBean:
package com.elucify.tips.mar2004;
public class DataBean {
protected String _string;
protected long _n_long;
protected double _n_real;
public DataBean() {
_string = "Default";
_n_long = 1000;
_n_real = 0.5;
}
public String getString() { return _string; }
public void setString(String string) {
_string = string; }
public long getLongnumber() { return _n_long;}
public void setLongnumber(long n_long) {
_n_long = n_long; }
public double getRealnumber() {
return _n_real; }
public void setRealnumber(double n_real) {
_n_real = n_real; }
}
This class represents application data. It would also implement the application's functionality (if there were any).
Describe the Model to JSF
You configure the JSF controller using a configuration file named faces-config.xml. This file resides in the Web Application Archive (WAR file) in the WEB-INF directory, alongside the Web deployment descriptor file, web.xml. Among other things that it does, faces-config.xml describes the model objects to the Controller, and defines page flow.
To tell the JSF Controller about a Model bean, use the <managed-bean> tag in faces-config.xml. Here is the <managed-bean> tag in the sample code:
<managed-bean>
<managed-bean-name>data</managed-bean-name>
<managed-bean-class>
com.elucify.tips.mar2004.DataBean
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
This tag defines a new Model class for the Controller of the given class. It tells the Controller to name the bean "data", and to store the bean in session scope. For each user session, the first time an application component (for example, a JSP page) accesses the name "data", the Controller automatically creates one and stores it in session scope. Later accesses of that name refer to the existing bean.
Create Application Views
JSP pages are the most common technology for JSF views. JSF defines two sets of standard tags. One set of tags, called core, handles such functions as converting between types, listening for user events, and validating user inputs. The other set of tags, called html, is a general input model that is used for generating HTML (or other) views. Notice where each of the two types of tags are used in the example below. (You can distinguish them by their namespace prefixes.)
The sample code input form, values.jsp, declares the two tag libraries, and then defines the view presentation. The first of the three input forms appears below. (The other two form elements are similar.)
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<html>
<head>
<title>Validating JSF Page</title>
</head>
<body>
<h1>Please enter the requested data</h1>
<f:view>
<h:form>
<b>Enter a string from three to twelve
characters in length:</b><br/>
<h:inputText id="string" required="true"
value="#{data.string}" size="20">
<f:validateLength minimum="3" maximum="12"/>
</h:inputText>
<h:message for="string" style="color: red;"/>
<br/>
<p>
...
</f:view>
</body></html>
Notice first the tag that says <f:view>. This tag must always be present around any collection of JSF tags. Just inside that tag is an <h:form> tag, which defines an input form. An HTML form would usually have METHOD and ACTION attributes, but the JSF runtime defines these attributes for you. Remember that in MVC designs, the Controller is responsible for translating inputs and selecting views. When the Controller receives inputs from a form, the Controller (not the form) decides what happens next. So the form doesn't need METHOD and ACTION attributes.
Next in the form is an element called <h:inputText>, which defines a textual input element. Its id attribute uniquely identifies it on the page. It is in this tag that the View is bound to the Model. The following attribute assignment:
value="#{data.string}"
binds the contents of this input field to the value of the "data" bean's string property. Each time the Controller generates an HTML view from this JSP page, it includes the value indicated by the expression "#{data.string}" as the text item's value. If the user enters a valid value into the text item, the Controller updates the server-side representation of data.string when it receives the form.
The other attributes and contents of this tag define data validation rules.
Defining Data Validation
Take another look at the inputText tag in values.jsp:
<h:inputText id="string" required="true"
value="#{data.string}" size="20">
<f:validateLength minimum="3" maximum="12"/>
</h:inputText>
<h:message for="string" style="color: red;"/>
<br/>
This block of code defines two data validation rules. The code also prints an error message if the variable's value fails validation.
The inputText tag's "required" attribute is "true". This means that the Controller requires that the user enter something in this input. The tag inside of inputText, <f:validateLength>, constrains the input to no less than three, and no more than twelve characters. This is a standard validation from the core tags package. The framework provides interfaces for you to define custom validation rules if you need them.
When the user posts an input form to the Controller, the Controller validates each of the inputs. If any inputs are not valid, the Controller serves the same page again. Before it generates the new page, the Controller marks each failed input as invalid, attaching an appropriate error message.
The <h:message> tag in the code sample above includes an error message for the view HTML. The "for" attribute (in this case, string) matches the id attribute of one of the other components on the page. In this case, the Controller supplies <h:message> with any error message from the <h:inputText> element whose id is string. The style attribute in <h:message> indicates that the error message should be rendered in red. (JSF also supports Cascading Style Sheets, so you could use a class attribute here, instead.)
This example shows how to perform server-side validation. JSF also provides a way to perform the requested validations on the client side. The default implementation of client-side validation runs as JavaScript in the Web browser.
Defining View Navigation for the Controller
The final development step is to tell the Controller which views to dispatch in response to user inputs. The previous section explained what the Controller does when it finds input validation errors. If all of the inputs are valid, the Controller uses the action it received from the form to determine what to do next. This "action" is essentially a semantic event sent by the HTML component (the commandButton) to the Controller.
The JSP page, values.jsp, includes a <h:commandButton> tag in its form. This is the button that posts the form to the Controller. The action attribute in the tag is a symbolic command that tells the Controller what to do if all of the inputs validate:
<h:commandButton id="submit"
action="validated"
value="Submit values"/>
In this case, the commandButton tells the Controller to execute the "validated" action if all inputs are valid.
As mentioned earlier, page navigation is defined in faces-config.xml, as a series of navigation rules. Here is the rule that applies in this case:
<navigation-rule>
<from-view-id>/jsp/values.jsp</from-view-id>
<navigation-case>
<from-outcome>validated</from-outcome>
<to-view-id>/jsp/valid.jsp</to-view-id>
</navigation-case>
</navigation-rule>
This rule tells the Controller the following: if you receive valid inputs from a form in the page /jsp/values.jsp, and the action is 'validated', then go to page /jsp/valid.jsp.
Additional Plumbing
Because a JSF application is a Web application, it requires a web.xml deployment descriptor. A JSF application's descriptor uses a servlet mapping to map the JSF controller to the URL /faces/, relative to the context root. The Controller removes this part of the URL when it serves JSP pages. Although the URL in your browser indicates:
http://localhost:8080/ttmar2004/faces/jsp/values.jsp
the file path inside the WAR file is actually:
/jsp/values.jsp
This detail can be confusing. The servlet mapping in the web.xml deployment descriptor looks like this:
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
Every WAR file that uses the JSF Reference Implementation must include the two libraries, jsf-api.jar and jsf-impl.jar, in its directory /WEB-INF/lib. These JAR files implement the Controller servlet and all of its support classes and files. They come as part of the JSF Reference Implementation download.
Differences From Earlier Releases
If you used last summer's early access (EA) release of JavaServer Faces, you'll notice that release 1.0 is quite different. In fact, if you have existing applications written using the EA version, you need to port your application to version 1.0. This section discusses a few of the changes between the two versions.
- The DTD for the configuration file faces-config.xml has changed. Many of the tag names are different. For example, the
<from-tree-id> tag is now <from-view-id>.
- The tag that enclosed a JSF view was
<f:use_faces> in the EA version. That tag was changed to <f:view>. The change was made because the name "view" is more descriptive of the tag's role in the application.
- Previously, tag names were all lower case, with words separated by underscores
('_'). The 1.0 release changed this convention, redefining tag names to use so-called "modified Camel case", the same naming convention recommended for Java method names. The new naming convention eliminates underscores, and instead separates words within a tag name by capitalizing the first letter of each word. For example, the tag <selectone_radio> in the EA version is now called <selectOneRadio>.
See the JSF 1.0 Reference Implementation Release Note for details on all of the changes.
RUNNING THE SAMPLE CODE
Download the sample archive for these tips. The application's context root is ttmar2004. The downloaded ear file also contains the complete source code for the sample.
You can deploy the application archive (ttmar2004.ear) on the J2EE 1.4 Application Server using the deploytool program or the admin console. You can also deploy it by issuing the asadmin command as follows:
asadmin deploy install_dir/ttmar2004.ear
Replace install_dir with the directory in which you installed the war file.
You can access the application at http://localhost:8000/ttmar2004.
For a J2EE 1.4-compatible implementation other than the J2EE 1.4 Application Server, use your J2EE product's deployment tools to deploy the application on your platform.
When you start the application, you should see a page that looks like this (only part of the page is shown):
Click on the link for "Tip 2: JavaServer Faces." Notice that the resulting page displays input elements that already contain data.
The JSF controller created the data bean in session scope when values.jsp first accessed one of its fields. The data you see is the default values for the fields.
Try typing some invalid data into the input fields and hitting the command button. You'll see that the Controller catches the invalid entries and adds an appropriate error message.
Then enter valid data for all fields and click the button again. This time all fields validate, so the Controller receives a "validated" action. It uses the navigation rules defined in faces-config.xml to find the next page, /jsp/valid.jsp, and it forwards the request there. That file reports, however anticlimactically, that all data elements were valid.
Application note: If you are working on a UNIX or UNIX-derived system, especially Mac OS 10.2, you need to be sure that the server has enough file descriptors available. Before starting the J2EE server, execute the command:
$ ulimit -n 1024
This will prevent "too many open files" errors when you deploy your application.
IMPORTANT: Please read our Terms of Use, Privacy, and Licensing policies:
http://www.sun.com/share/text/termsofuse.html
http://www.sun.com/privacy/
http://developers.sun.com/dispatcher.jsp?uid=6910008
Comments? Send your feedback on the Enterprise Java Technologies Tech Tips to: http://developers.sun.com/contact/feedback.jsp?category=newslet
Subscribe to other Java developer Tech Tips:
- Core Java Technologies Tech Tips. Get tips on using core Java technologies and APIs, such as those in the Java 2 Platform, Standard Edition (J2SE).
- Wireless Developer Tech Tips. Get tips on using wireless Java technologies and APIs, such as those in the Java 2 Platform, Micro Edition (J2ME).
To subscribe to these and other JDC publications:
- Go to the JDC Newsletters and Publications page, choose the newsletters you want to subscribe to and click
"Update".
- To unsubscribe, go to the subscriptions page, uncheck the appropriate checkbox, and click "Update".
ARCHIVES: You'll find the Enterprise Java Technologies Tech Tips archives at:
http://java.sun.com/developer/EJTechTips/index.html
Copyright 1994-2004 Sun Microsystems, Inc. All
rights reserved.
4150 Network Circle, Santa Clara, CA 95054 USA.
This document is protected by Copyright 1994-2004 Sun Microsystems, Inc. in the United States and other countries.
|