|
Level: Intermediate David Geary, President, Clarity Training, Inc. June 15, 2009 with Release 2.0, Java™server Faces (JSF) can now easily implement robust, Ajax-style Web applications. This article is the beginning of a three-part series, and the JSF 2.0 Expert Group David Geary will show you how to take advantage of the new features in JSF 2. In this installment, you'll learn how to streamline development with JSF 2, you'll use annotations and conventions instead of XML configurations, simplify navigation, and easily access resources. And you'll see how to use Groovy in your JSF application. The best place to cradle the WEB application framework has been the debate over whether the ivory tower (from theorists ' fantasy) or the real world. In the latter case, the birth of the framework has withstood the harsh test of actual demand. Judging by intuition, the test of actual demand trumps that of theorists, and I think that intuition will stand a further test. JSF 1 was developed in an ivory tower, so its emergence did not cause much sensation. But JSF does the right thing-it makes a lot of innovations in the marketplace that come from actual development. Earlier, Facelets's debut became a powerful standby for JavaServer Pages (JSP). Then came the Rich Faces, an excellent JSF Ajax library, followed by ICEfaces, a novel approach to combining Ajax with JSF, Seam, Spring Faces, Woodstock components, JSF templating, and so on. All of these open-source JSF projects are built by developers based on the functionality they need. The JSF 2.0 Expert group actually standardizes the best features from these open source projects. Although the JSF 2 specification is indeed written by some theorists, it is also driven by innovations from actual development. In retrospect, the panel's work was actually very relaxing, as we were standing on the shoulders of giants such as Gavin King (Seam), Alexandr Smirnov (Rich Faces), Ted Goddard (icefaces) and Ken Paulson (J SF templating). In fact, all of these giants are members of the JSF 2 Expert group. As a result, JSF 2 combines the ivory tower and real-world strengths in many ways. And it shows this. JSF 2 is a significant improvement on JSF 1. This article is a three-part series that starts with two goals: showcasing the exciting JSF 2 features and demonstrating how best to take advantage of them, so you can take advantage of the functionality provided by JSF 2. By demonstrating the application of JSF 2 and accompanying some of the best use techniques, I'll explain the first two questions. Here's how to do this: Tip 1: Get rid of XML configuration Tips 2: Simplify navigation tips 3: Using Groovy Tips 4: Leveraging resource handlers However, I will first introduce the sample application that runs through the entire series. The application source code for this article can be downloaded. Example of a WEB service mashup based on force mapping Figure 1 shows a JSF mashup-I call it the places application-it uses the Yahoo! Web service to convert addresses to maps and display zoom levels and weather forecasts: Figure 1. View maps and weather information from Yahoo! Web Services
To create a location, you need to fill out the address form, activate the Go button, and the application will send the address to two WEB services: Yahoo! Maps and Yahoo! Weather. The map service returns 11 map URLs to address mappings on the Yahoo! server, using different zoom levels. The Weather service returns some of the pre-assembled HTML. Both the image URL and the HTML content are easily displayed in a JSF view, thanks to The places application allows you to enter any number of addresses. You can even use the same address multiple times, as shown in Figure 2, which actually demonstrates the zoom level: Figure 2. Zoom level
Key points for the application The places application has 4 managed beans (managed beans), as shown in table 1: table 1. Managed beans in places applications
Managed Bean name |
class |
Range |
Mapservice |
Com.clarity.MapService |
Application |
WeatherService |
Com.clarity.WeatherService |
Application |
Places |
Com.clarity.Places |
Session |
Place |
Com.clarity.Place |
Request |
|
running the places application
To run the places application, you need to access Developer.yahoo.com/maps/ajax and obtain an application ID from Yahoo! in order to use the Yahoo! Web service. Click the Get App ID button in the Yahoo! Maps Web Service. After you get the ID, replace the your_id_here with your ID in Mapservice.java and Weatherservice.java. |
|
The application stores a set of place in session scope, as shown in Figure 1, and maintains a place within the scope of the request. Applications also use the application-scoped mapservice and WeatherService managed beans to provide a simple API for Yahoo! Map and Weather Web services. The location of the creation is simple. Listing 1 shows the code for the address form contained in the view in Figure 1: Listing 1. Address Form
#{msgs.streetaddress}
#{msgs.city}
#{msgs.state}
#{msgs.zip}
Style= "Font-family:palatino;font-style:italic" action= "#{place.fetch}"
/>
|
When the user activates the Go button and submits the form, JSF invokes the action method of the button: Place.fetch (). This method sends information from the WEB service to Place.addplace (), which creates a new place instance, initializes the instance with the data passed to the method, and stores it in the request scope. The Listing 2 shows the Place.fetch (): Listing 2. Place.fetch () method
public class Place { ... Private string[] mapurls Private String weather ... Public String Fetch () { Facescontext FC = Facescontext.getcurrentinstance () Elresolver elresolver = Fc.getapplication (). Getelresolver () //Get maps Mapservice ms = Elresolver.getvalue ( FC . Getelcontext (), NULL, "Mapservice") Mapurls = Ms.getmap (streetaddress, city, State) //Get WEATHER WeatherService ws = Elresolver.getvalue ( Fc.getelcontext (), NULL, "WeatherService") Weather = w S.getweatherforzip (Zip, True) //Get places Places places = Elresolver.getvalue ( Fc.get Elcontext (), NULL, "Places") //ADD new Place to places Places.addplace (streetaddress, city, state, map Urls, weather) return null } } |
Place.fetch () uses the JSF variable factorization (resolver) to find Mapservice and WeatherService managed beans, and uses these managed beans to obtain maps and weather information from the Yahoo! Web service. then fetch () invokes Places.addplace (), which uses maps and weather information and addresses to create a new place within the scope of the request. Note that fetch () returns NULL. Because fetch () is a button-related action method, the null return value causes JSF to reload the same view, displaying all the locations in the user's session, as shown in Listing 3: Listing 3. Show position in view
<ui:composition xmlns= "http://www.w3.org/1999/xhtml" xmlns:f= "Http://java.sun.com/jsf/core" Xmlns:h= "Http://java.sun.com/jsf/html" xmlns:ui= "Http://java.sun.com/jsf/facelets" >
<!--iterate over the list of places--> <ui:repeat value= "#{places.placeslist}" var= "place" >
<div class= "Placeheading" >
<!--address in the top--> <div style= "padding-left:5px;" > <i></div>
<!--zoom level prompt and drop down--> <!--prompt--> <div style= "padding-right:10px;margin-bottom:10px;font-size:14px" > #{msgs.zoomprompt} </div>
<!--dropdown--> Value= "#{place.zoomindex}" Valuechangelistener= "#{place.zoomchanged}" style= "Font-size:13px;font-family:palatino" >
<f:selectitems value= "#{places.zoomlevelitems}"/>
<!--the map-->
<!--the weather--> <div class= "Placemap" > <div style= "margin-top:10px;width:250px;" > Value= "#{place.weather}" Escape= "false"/> </div> </div> </div>
</ui:repeat>
</ui:composition>
|
The code in Listing 3 uses the Facelets <ui:repeat> tag to iterate over the list of locations stored in the user's session. For each location, the output should be as shown in Figure 3: Figure 3. Position displayed in the view
Modify Zoom Level The zoom menu (see Figure 3 and Listing 3) has a onchange= "submit ()" Property, so when the user selects a zoom level, the JavaScript submit () function submits the wrapping (surrounding) Form of the menu. After submitting the form, the JSF invoke the related value of the menu to modify the Listener-place.zoomchanged () method, as shown in Listing 4: Listing 4. Place.zoomchanged ()
public void zoomchanged (Valuechangeevent e) {
String value = E.getcomponent (). GetValue () Zoomindex
= (new Integer (value)). Intvalue ()
}
|
Place.zoomchanged () stores the zoom level in a member variable of a place class named Zoomindex. Because the navigation is not affected by the communication with the server, JSF reloads the page and the map is updated with the new zoom level, as follows: Listing 5. Place.getmapurl ()
Public String Getmapurl () {
return mapurls = null? "": Mapurls[zoomindex
]
}
|
Use a small amount of Facelets If you've ever used JSF 1, you'll probably notice some subtle differences in the JSF 2 code in this article. First, I used the new display technology-facelets-of JSF 2 rather than the JSP. As you will see from subsequent articles in this series, Facelets offers a number of powerful features to help you achieve a robust, flexible, and extensible user interface. But in the previous code list, I did not take advantage of this feature too much. However, one of the many minor improvements that Facelets brings to JSF is the ability to put JSF value expressions directly into XHTML pages; for example, in Listing 1, I put expressions like #{msgs.city} directly into the page. If you are using JSF 1, you must encapsulate the expression in From the Facelets point of view, you also need to note the <ui:composition> tag in Listing 3. The tag specifies that the XHTML page in Listing 3 will be included in other XHTML pages. The Facelets composition is the central component of the Facelets templating, similar to the popular Apache Tiles framework. In a later article in this article, I'll discuss the Facelets template and show you how to build your view based on the composed method Smalltalk pattern. So far, the previous code has not used Facelets, and no significant changes have occurred compared to JSF 1. Now, I'm going to show a bigger difference. The first big difference is in the number of XML configurations that will be written for JSF 2 applications.
|
back to the top of the page |
|
Tip 1: Get rid of the XML configuration The XML configuration of a WEB application is always a hassle-it's very verbose and prone to errors, so it's a good idea to delegate the XML configuration to a framework, such as through annotations, conventions, or domain-specific languages. As developers, we should be able to focus on doing something rather than wasting time on lengthy XML aspects. As a typical example, listing 6 shows the 20 lines of XML code required to declare a managed bean in a places application when JSF 1 is used: Listing 6. Managed Bean declaration for JSF 1
<managed-bean> <managed-bean-class>com.clarity.MapService</managed-bean-class> <managed-bean-name>mapService</managed-bean-name> <managed-bean-scope>application</managed-bean-scope> </managed-bean>
<managed-bean> <managed-bean-class>com.clarity.WeatherService</managed-bean-class> <managed-bean-name>weatherService</managed-bean-name> <managed-bean-scope>application</managed-bean-scope> </managed-bean>
<managed-bean> <managed-bean-class>com.clarity.Places</managed-bean-class> <managed-bean-name>places</managed-bean-name> <managed-bean-scope>session</managed-bean-scope> </managed-bean>
<managed-bean> <managed-bean-class>com.clarity.Place</managed-bean-class> <managed-bean-name>place</managed-bean-name> <managed-bean-scope>request</managed-bean-scope> </managed-bean>
|
For JSF 2,xml to disappear, you will use annotations for the class, as shown in Listing 7: Listing 7. Managed bean annotations for JSF 2
@ManagedBean (Eager=true)
public class Mapservice {
...
}
@ManagedBean (Eager=true)
public class WeatherService {
...
}
@ManagedBean ()
@SessionScoped
public class Places {
...
}
@ManagedBean ()
@RequestScoped
public class Place {
...
}
|
By convention, the name of the managed bean is the same as the class name, and the first letter of the class name is converted to lowercase. For example, the managed created in Listing 7, from top to small, is: Mapservice, WeatherService, places, and place. You can also explicitly specify a managed bean by using the Name property of the Managedbean annotation, for example: @ManagedBean (name = "Place"). In Listing 7, I use the eager property for Mapservice and WebService managed beans. When the Eager property is true, JSF creates the managed bean at startup and puts it into the application scope. You can also use @ManagedProperty annotations to set managed bean properties. Table 2 shows a complete list of JSF 2 managed bean annotations: table 2. JSF 2 Managed Bean annotations (@ ...) Scoped annotation is valid only for @ManagedBean)
Managed Bean Annotations |
Description |
Property |
@ManagedBean |
Registers a class instance as a managed bean, and then puts it in the use of one of the @ ... Within the range specified by the Scoped annotation. If no range is specified, JSF puts the bean into the request scope, and if no name is specified, JSF converts the first letter of the class name to lowercase to form a managed bean name; For example, if the class name is UserBean, JSF creates a managed bean named us Erbean. The eager and name properties are optional. Note You must use a Java class that implements a zero-parameter constructor in combination. |
Eager, name |
@ManagedProperty |
Sets a property for the managed bean. Annotations must be placed before the declaration of a class member variable. The Name property specifies the names of the attributes, which by default are the names of the member variables. The Value property is the values of the attribute, can be a string, or it can be a JSF expression, such as #{...}. |
Value, Name |
@ApplicationScoped |
Store managed beans within the application scope. |
|
@SessionScoped |
Store managed beans in session scope. |
|
@RequestScoped |
Stores managed beans within the scope of the request. |
|
@ViewScoped |
Store managed beans in view scope. |
|
@NoneScoped |
Specifies that the managed bean has no scope. A managed bean with no scope is more useful when it is referenced by another bean. |
|
@CustomScoped |
Store managed beans within a custom scope. A custom scope is a map that can be accessed by the creator of the page. You can programmatically control the visibility and lifecycle of a bean within a custom scope. The Value property points to a map. |
Value |
Removing the managed bean declaration from faces-config.xml will greatly reduce XML, but in JSF 2, almost all of the XML content can be removed by using annotations (as I did with managed beans) or by conventions (such as the simplified navigation processing of JSF 2).
|
back to the top of the page |
|
Tip 2: Simplify navigation In JSF 1, navigation is specified using XML. For example, to go from login.xhtml to places.xhtml, you might use the navigation rules shown in Listing 8: Listing 8. Navigation configuration rules and use cases in JSF 1
<navigation-rule>
<navigation-case>
<from-view-id>/pages/login.xhtml</from-view-id>
<outcome>places</outcome>
<to-view-id>/pages/places.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
|
To get rid of the XML in Listing 8, you can take advantage of the JSF 2 navigation Convention: JSF adds. XHTML to the end of the button action and loads the file. This means that you do not need to use annotations or other content, and you can completely avoid the need to write navigation rules simply by using a convention. In Listing 9, the action of the button is places, so JSF loads the places.xhtml: listing 9. Navigating By Convention
Value= "#{msgs.loginbuttontext}"
action= "Places
"/>
|
For listing 9, no navigation XML is required. The button in Listing 9 loads the places.xhtml, but only if the file and the button are in the same directory. If the operation does not begin with a slash (/), JSF considers this to be a relative path. If you need to be more specific, you can specify an absolute path, as shown in Listing 10: listing 10. Navigation using an absolute path
Value= "#{msgs.loginbuttontext}"
action= "/pages/places
"/>
|
JSF loads the/pages/places.xhtml file when the user activates the button in Listing 10. By default, JSF moves from one XHTML page to another XHTML page, but can be redirected by specifying the Faces-redirect parameter, as shown in Listing 11: listing 11. Navigating through redirects
Value= "#{msgs.loginbuttontext}"
action= "Places?faces-redirect=true
"/>
|
|
back to the top of the page |
|
Tip 3: Use Groovy The biggest advantage of Java technology is not the Java language, but the Java Virtual Machine (JVM). Running powerful, innovative, and innovative languages on the JVM, such as Scala, JRuby, and Groovy, gives you more choices when writing code. Groovy is a strange name, but it's very powerful, blending Ruby, Smalltalk, and the Java language, which is one of the most popular languages in these languages (see Resources). There are many reasons to use Groovy. First, it is much simpler and more powerful than the Java language. There are two other reasons: do not use semicolons and do not require a cast (casting). You may not have noticed that in Listing 2, the place class was written in Groovy. This can be seen in the code without semicolons, but note the following line of code: Mapservice ms = Elresolver.getvalue (...). For Java code, I must cast the result of Elresolver.getvalue () because the method returns type Object. Groovy can automatically complete the conversion for me. You can use Groovy with any JSF artifacts written in Java code-for example, components, renderer, validators, and converters. In fact, this is not new to JSF 2-because groovy source files are compiled into Java bytecode, you only need to use the. class files generated by groovy as if they were generated by Javac. Of course, when Groovy's. class file is working correctly, you need to know how to hot deploy groovy source code, and for Eclipse users, the answer is simple: Download and install the Groovy Eclipse plug-in (see Resources). Mojarra is a Sun JSF implementation that provides explicit support for Groovy from version 1.2_09 (see Resources).
|