|
Welcome to the Enterprise Java Technologies Tech Tips for September 23, 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:
Value and Method Binding Expressions in JavaServer Faces Technology
Persisting Model Components With Java Data Objects
These tips were developed using the Java 2, Enterprise Edition, v 1.4 SDK. You can download the SDK at http://java.sun.com/j2ee/1.4/download.html.
This issue of the Tech Tips is written by N. Alex Rupp, a professional Open Source developer and software architect for Open Technology Systems.
See the Subscribe/Unsubscribe note at the end of this newsletter to subscribe to Tech Tips that focus on technologies and products in other Java platforms.
You can download the sample archive for these tips. Any use of this code and/or information below is subject to the license terms.
For more Java technology content, visit these sites:
java.sun.com - The latest Java platform releases, tutorials, and
newsletters.
java.net - A web forum for collaborating and building solutions
together.
java.com - The marketplace for Java technology, applications and
services.
VALUE AND METHOD BINDING EXPRESSIONS IN JAVASERVER FACES TECHNOLOGY
As indicated by its title, the March 24, 2004 Tech Tip
Introducing JavaServer Faces Technology introduced JavaServer Faces (JSF) technology, the new standard framework for web-based user interfaces that use Java technology. The following tip takes a closer look at JSF, focusing on value and method binding expressions in the technology.
One of the aims of JavaServer Faces technology is to support a role-based workflow for application development. The technology is designed to address the unique concerns and needs of page authors, user interface component designers, and business model developers. As such, there are several ways that a developer can approach the technology. This tip focuses on the role of the page author, for whom binding expressions provide a way to access the data model.
Value Binding Expressions
The two different types of binding expressions in JSF are value binding expressions and method binding expressions. Value binding expressions can be used inside of JSF components to:
- Automatically instantiate a JavaBean and place it in the request or session scope.
- Override the JavaBean's default values through its accessor methods.
- Traverse an object tree using an easy-to-learn DOM-style notation.
- Quickly retrieve
Map, List, and array contents from a JavaBean.
- Synchronize form contents with value objects across a number of requests.
The syntax of binding expressions is based on the JavaServer Pages (JSP) 2.0 Expression Language, which itself is based on the object accessing syntax of JavaScript. In JSP, expressions are delimited with "${}", but in JSF they are delimited with "#{}". This is because in JSP 2.0, the JSP container evaluates expressions before the page is executed. However, JSF needs to be able to evaluate the expressions at a time of the view component's choosing.
Page authors can use value binding expressions in combination with the standard JSF user interface component library. In doing that, page authors can easily create prepopulated forms in JSP pages, and synchronize the contents of those forms with JavaBean values in the conversational state of a servlet application. The ability to bind expressions to public methods in arbitrary objects, also allows page authors to easily trigger events on the data model.
A good way to begin working with binding expressions is to look at some examples. The sample WAR file that accompanies this issue of the Tech Tips (ttsep2004.war) contains two JSP files: index.jsp and viewInvoice.jsp. Each of these JSP files contains examples of value and method binding expressions.
Here are some binding expression examples from the index.jsp file:
#{invoice.customerName}
#{invoice.date}
#{invoice.next}
The first two of these binding expressions are standard value bindings for public accessor methods. The third binding expression is a method binding expression (more about that later). The equivalent expression in Java technology of the first binding expression, #{invoice.customerName}, is invoice.getCustomerName(). The second binding expression, #{invoice.date}, differs from the first in that its initial value is described in the faces-config.xml file in the WEB-INF directory:
<managed-bean>
<description> Represents an Invoice </description>
<managed-bean-name> invoice </managed-bean-name>
<managed-bean-class> com.alexrupp.ttsep2004.InvoiceBean
</managed-bean-class>
<managed-bean-scope> session </managed-bean-scope>
<managed-property>
<property-name>date</property-name>
<value>2004-09-06</value>
</managed-property>
</managed-bean>
The date string is initialized in the constructor of the InvoiceBean:
public InvoiceBean() {
date = "2004-08-01";
The managed-property element in the faces-config.xml file overwrites the initial date value before the bean is placed into the session scope.
In addition to being able to read the initial value of a field from an object, value binding expressions can also be used to navigate maps, lists, or arrays. Here are some examples:
#{foo[bar]}
#{foo["bar"]}
#{foo[3]}
#[foo[3].bar}
#{foo.bar[3]}
Finally, you can use value binding expressions to write the value of a field into an object prior to the object being displayed. This is useful when you want to prepopulate form fields with different values, depending on how the user first arrives at the form. You can also use this technique to perform limited mathematical calculations, as shown in the following examples:
#{customer.status == 'VIP'}
#((city.farenheitTemp - 32) * 5 / 9}
Method Binding Expressions
The third binding expression listed previously, #{invoice.next}, is a method binding expression. Unlike a value binding, a method binding does not represent an accessor method. Instead, a method binding represents an activation method. If you're familiar with Struts, then this should not surprise you. Which type of binding expression is appropriate in any given user interface component (such as a custom JSF tag) is determined by the tag author. In certain cases, only value binding expressions are allowed. In other cases, only method binding expressions are allowed. In still others, either type of expression might be allowed. The syntax of a method binding expression has two primary formats:
#{expr-a.value-b}
#{expr-a[value-b]}
The first format is a classic DOM-style object tree structure. The second format can be used to call a method pointed to from an object array, Map, or List. In the example code, a method binding expression is used on the index.jsp page to resolve the action of the enclosing form element. The method called is an arbitrary method on the InvoiceBean object, but it doesn't need to be. In the example, the method is required to return a String value. That value is used by the JSF engine to determine the next view component:
<navigation-rule>
<description> Navigation rules for "index.jsp".
</description>
<from-view-id> /index.jsp </from-view-id>
<navigation-case>
<from-outcome> validated </from-outcome>
<to-view-id> /viewInvoice.jsp </to-view-id>
</navigation-case>
</navigation-rule>
Notice that the actual view component used (viewInvoice.jsp) does not appear in the URL of the following page when the form contents are submitted. This URL shadowing allows you to swap out the underlying view components (or even change the entire information architecture of the underlying site) without upsetting the URL hierarchy. So what the public sees (and perhaps bookmarks) remains consistent.
Forgiving syntax
The JavaServer Faces engine goes to considerable lengths to coax meaning out of the values entered into binding expressions. For instance, if a numeral is entered and a String is expected by a method, the JSF engine coerces the numeral into a String in the process of its evaluation. The JSF engine will look for collection valued references, follow object trees, and even instantiate new objects in the appropriate scope if none exist that are capable of servicing the request. All of this leads to a friendly user experience and higher productivity between page authors and component developers.
For more information about JavaServer Faces technology, see the
JavaServer Faces Technology home page.
PERSISTING MODEL COMPONENTS WITH JAVA DATA OBJECTS
The Java Data Objects (JDO) API provides a standard, straightforward way of achieving object persistence in Java technology. JDO uses a practical combination of XML metadata and bytecode enhancement to ease development complexity and overhead, as compared to other object binding technologies.
One of the strong points of JDO is the simplicity of its API. All you need to learn are a few interfaces to use the technology. The JDO API is standardized, and there is a range of
implementations from which to choose. The JDO Reference Implementation and TCK are available at no cost on the JDO Specification page. The Reference Implementation does not support relational databases. For JDO implementations that support relational databases, see JDOcentral.com.
JDO is distinct from other persistence technologies, such as Enterprise JavaBeans technology, in that it allows you to work with plain old JavaBeans. It doesn't force you to adhere to any special APIs. JDO's careful use of post-compilation bytecode enhancement provides an abstraction barrier between the developer and the persistence container, that frees the developer from the complexity of the surrounding environment. It lets a developer focus on what really matters: the structure and behavior of applications.
JDO distinguishes between the domain object model (the Java classes that represent the persistent data) and the management classes (the Java classes that are responsible for querying the database, managing the life cycle of persistent instances, and demarcating transactions). Persistent classes are not affected by persistence, they simply implement the business logic associated with the model. The management classes must be persistence-aware, but JDO allows you to implement your management classes without database-specific code.
Here are the steps in working with JDO:
- Download the JDORI jars and their dependencies.
- Define a simple model bean and create its metadata in a JDO XML file.
- Compile and enhance the JavaBean.
- Construct a management facade for working with the persistent store.
The sample web application for this issue of the Tech Tips provides a simple example of using JDO. There are several books and dozens of articles available that show how to use the JDO API (see the resources at the end of this tip). So instead of describing how to use JDO API, this tip focuses on the most challenging part of getting started with JDO: setting up the development environment and getting the first object to verifiably persist. The sample web application illustrates how to do that.
Download the JDO JARs and dependencies
The requisite JAR files have all been included in the example WAR file (ttsep2004.war). You can find the JAR files in the WEB-INF\lib directory. Here are the JAR files:
jdo-1.0.1.jar (the JDO API JAR)
jdori-1.0.1.jar (the Sun JDO implementation classes)
jdori-enhancer-1.0.1.jar (bytecode enhancer package)
btree-1.0.1.jar (bean tree JAR for handling the default file object persistence)
antlr-2.7.1.jar (required for parsing queries)
Configure the properties and set up the file database
The JDO reference implementation comes with a file-based data storage mechanism called "FOStore". The web application uses FOStore (this avoids the added complexity of integrating a database). To set up FOStore, you must configure your system properties. In the example, this is done in jdori.properties file in the src/com/alexrupp/ttsep2004 directory. Here are the file contents:
# jdori.properties file
javax.jdo.PersistenceManagerFactoryClass=
com.sun.jdori.fostore.FOStorePMF
com.sun.jdori.option.ConnectionCreate=true
javax.jdo.option.ConnectionUserName=jdotester
javax.jdo.option.ConnectionPassword=jdopassword
# The following property points to your local filesystem:
javax.jdo.option.ConnectionURL=fostore:C:/fostore
javax.jdo.option.Optimistic=false
javax.jdo.option.RetainValues=false
javax.jdo.option.RestoreValues=false
This configuration uses the FOStore PersistenceManager Factory. It stores data files in the C:/ directory of a Windows machine.
Build, compile, and enhance the JavaBean
The web application uses the InvoiceBean object for persistence. To prepare the InvoiceBean class for persistence, it must be run through a bytecode enhancer. The bytecode enhancer injects an interface and several important methods into the class, so that the persistence container can work with it. This technique is preferred over entering these methods manually. It's also preferred over stub class and interface generation used in other persistence technologies. There is a file named maven.xml in the example that contains the necessary lines of code to enhance the bytecode:
<goal name="jdo:enhance">
<ant:java
classname="com.sun.jdori.enhancer.Main"
fork="true">
<ant:arg
line="-v -s target/ttsep2004/WEB-INF/classes
target/ttsep2004/WEB-INF/classes/com/alexrupp/ttsep2004/InvoiceBean.class
-d target/ttsep2004/WEB-INF/classes -v -f"/>
<ant:classpath>
<ant:path
refid="maven.dependency.classpath"/>
<ant:pathelement
location="${basedir}/lib/jdori-enhancer-1.0.1.jar"/>
</ant:classpath>
</ant:java>
</goal>
The goal element calls the com.sun.jdori.enhancer.Main class, and passes it a line of arguments. The class uses the arguments to identify and enhance the bytecode of the InvoiceBean class. You can enter your own classes in addition to the InvoiceBean.
Retrieve a PersistenceManagerFactory using the system properties
The InvoiceManager servlet shows how to retrieve the PersistenceManagerFactory class. It uses the JDOHelper class and the imported properties file assembled earlier. After obtaining a PersistenceManagerFactory, it's a simple matter to retrieve a PersistenceManager and begin using the JDO API:
// ...
// get the JDO properties
Properties props = new Properties();
try {
props.load(CreateInvoice.class.getResourceAsStream
("jdori.properties"));
} catch (IOException e) {
e.printStackTrace();
}
// get the PMF
PersistenceManagerFactory fact =
JDOHelper.getPersistenceManagerFactory(props);
// get the persistence manager
PersistenceManager mgr = fact.getPersistenceManager();
//...
Although used in this example, this method of getting a PersistenceManager is not the best practice. In a real application, you should use a ServletFilter, where the init method obtains the PersistenceManagerFactory once (it's an expensive operation) and saves it as an instance variable in the filter. Each invocation will pass through the filter, and store the PersistenceManager as a property in the request.
Use the PersistenceManager to create a new persistent object
The final step is to build a class for managing the create, read, update and delete processes of the InvoiceBean. You could let the JavaBean class manage its own persistence, but it's better to keep it as a passive data store and leave the active role to another class. You'll find the modest beginnings of this manager functionality in the InvoiceManager servlet. There is enough code in this servlet to put an object in the data store. The servlet then verifies the addition by counting the total number of InvoiceBean objects contained in the store and displaying that information to the screen:
// make the transaction
try {
mgr.currentTransaction().begin();
mgr.makePersistent(savingInvoice);
invoice.setPersistenceStatus(""
+ " Saved Successfully at "
+ System.currentTimeMillis());
/* Count all of the invoices. This is
* Okay for a demo, but in production you should
* Never use an unqualified query.
*/
Query query = mgr.newQuery(InvoiceBean.class);
Collection result = (Collection) query.execute();
invoice.setInvoiceCount("" + result.size());
query.close(result);
// commit the transaction.
mgr.currentTransaction().commit();
} catch (Throwable t) {
invoice.setPersistenceStatus(" Not Saved: " + t);
mgr.currentTransaction().rollback();
}
// forward to a view component
try {
res.sendRedirect("/ttsep2004/faces/viewInvoice.jsp");
} catch (IOException e) {
e.printStackTrace();
}
With a little time and effort, you can build a sophisticated (but not complicated) invoice tracking system using a combination of Java Data Objects for persistence and JavaServer Faces technology for presentation. Each technology reduces complexity and development overhead. Once you master these technologies, you can dramatically increase productivity.
For more information about JDO, see the following resources:
RUNNING THE SAMPLE CODE
Download the sample archive for these tips. The application's context root is ttsep2004. The downloaded WAR
file also contains the complete source code for the sample.
The J2EE 1.4 SDK contains the Sun Java System Application Server Platform Edition 8. You can deploy the Web archive (ttsep2004.war) in Sun Java System Application Server Platform Edition 8 by using the deploytool program or the admin console. You can also deploy it using the asadmin command as follows:
asadmin deploy install_dir/ttsep2004.war
Replace install_dir with the directory in which you installed the war file.
You can access the application at http://localhost:8080/ttsep2004.
For a J2EE 1.4-compliant implementation other than Sun Java System Application Server Platform Edition 8, use your J2EE product's deployment tools to deploy the application on your platform.
|
|
 |
 |
|
|
 |
 |
IMPORTANT: Please read our Licensing, Terms of Use, and Privacy policies:
http://developer.java.sun.com/berkeley_license.html
http://www.sun.com/share/text/termsofuse.html
Privacy Statement: Sun respects your online time and privacy (http://sun.com/privacy). You have received this based on your email preferences. If you would prefer not to receive this information, please follow the steps at the bottom of this message to unsubscribe.
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.
* As used in this document, the terms "Java virtual machine" or "JVM" mean a virtual machine for the Java platform.
|