Level: Intermediate David Geary, President, Clarity Training, Inc. June 25, 2009 templates and composite components are the two powerful features of Java™server Faces (JSF) 2, and with these two features you can implement user interfaces that are easy to modify and extend. In the 2nd installment of this article-a total of three parts-the JSF 2 Expert Group David Geary will show you how to leverage templates and composite components in your WEB application. As early as 2000, when I was an active member of the JavaServer Pages (JSP) mailing list, I met Craig McClanahan, who was busy developing a new WEB framework called Struts. At the time, I was also moving from Swing to server-side Java programming, so I've implemented a very small framework to separate the JSP view layout and its content, much like the swing layout manager idea. Craig asked me if I would like to include my template library in Struts, and I readily agreed. Thus, the Struts Template library, bundled with Struts 1.0, became the foundation of the popular Tiles Library of struts, and the Tiles library eventually became a top-level Apache framework. JSF 2 is now the default display technology-facelets-is a template framework, to a large extent, based on the Tiles. JSF 2 also provides a powerful mechanism, called a composite component, built on top of the Facelets template features, so that custom components can be implemented without any Java code and XML configuration. In this article, I'll introduce you to templates and composite components, and also give you three tips on how to make the most of JSF 2: Tip 1: Adhere to the DRY principle tips 2: Tips for using combinations 3: Keep in mind the idea of LEGO assembled toys
|
Facelets and JSF 2
While standardizing open source Facelets implementations, the JSF 2 Expert group also made changes to the underlying Facelets APIs, but retained a back-to compatibility with the tag library. This means that the existing views implemented with open source Facelets should apply to JSF 2. More information about Facelets's many features can be found in these two articles in Rick Hightower, "Facelets is perfect for JSF" and "Advanced Facelets programming." |
|
Tip 1: Follow the DRY principle In my first job as a software developer, my task was to implement a GUI for UNIX® based computer-aided design and computer-aided manufacturing (CAD/CAM) systems. Initially, everything went smoothly, but after a while, my code began to question constantly. By the time the code was released, the system was pretty fragile, and I was even afraid to fix bugs, and the code release was naturally accompanied by a series of bug reports. If I follow the DRY principle in this project-Don't repeat myself (Don t Repeat yourself), I could have kept myself from being so miserable. The DRY principle was originally presented by Dave Thomas and Andy huntprinciple (see Resources), which requires that each piece of knowledge must have a single, clear and authoritative representation within the system. My CAD/CAM application does not conform to the DRY principle-it has too many points of intersection-so changes made in one place often cause unexpected changes elsewhere. JSF 1 violates the DRY principle in several ways, for example, it forces you to provide two representations of a managed beans-one that uses XML and one that uses Java code. The need for multiple representations makes it more difficult to create and change managed beans. As I described in part 1th of this series, JSF 2 enables you to configure managed beans with annotations instead of XML, so that managed beans have a single, authoritative representation. In addition to managed beans, even seemingly beneficial practices-including the same stylesheet in all views-violate the DRY principle and can lead to confusion. For example, if you want to change the name of a style sheet, you must change multiple views. If possible, it is best to encapsulate this style sheet containing. The DRY principle also applies to code design. If multiple methods contain code that traverses the tree, a good practice is to encapsulate the algorithm for traversing the tree (for example, in a subclass). Adherence to the DRY principle is particularly important when implementing the UI because most of the changes occur during the development process. JSF 2 Template JSF 2 supports the DRY principle in many ways, one of which is through templates. Templates can encapsulate features that are very common in application view, so this functionality is only specified once. In JSF 2 applications, a template is available for multiple assemblies (compositions) for creating views. The places application I described in part 1th has three views, as shown in Figure 1: Figure 1. View of the Places application: Login, source Viewer, and places
Like many Web applications, this places application contains multiple views with the same layout. The JSF template feature allows you to encapsulate the layout within a template-and other shared artifacts, such as JavaScript and cascading Style Sheets (CSS). Listing 1 is the template for the three views shown in Figure 1: Listing 1. Places Template:/templates/masterlayout.xhtml
<! DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 transitional//en" "Http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >
Xmlns:h= "Http://java.sun.com/jsf/html" xmlns:ui= "Http://java.sun.com/jsf/facelets" >
<title> <ui:insert name= "WindowTitle" > #{msgs.placeswindowtitle} </ui:insert> </title>
<div class= "Pageheading" > <ui:insert name= "Heading" > #{msgs.placesheading} </ui:insert> </div>
<div class= "Menuandcontent" > <div class= "Menuleft" > <ui:insert name= "Menuleft"/> </div>
<div class= "Content" style= "Display: #{places.showcontent}" > <ui:insert name= "Content"/> </div>
<div class= "Menuright" > <ui:insert name= "Menuright" > <ui:include src= "/sections/shared/sourceviewer.xhtml"/> </ui:insert> </div> </div>
|
The template in Listing 1 provides the following infrastructure for all views of the application: HTML As shown in Listing 1, the template inserts content into the layout through the <ui:insert> tag. If you specify a body for the <ui:insert> tag, as I did in Listing 1 for the window title, header, and right menu, JSF will take the body of this tag as the default. With the <ui:define> tag, those packages that use this template can define content or override default content, as shown in Listing 2, which gives the login view tag: Listing 2. Login View
<ui:composition xmlns= "http://www.w3.org/1999/xhtml"
Xmlns:ui= "Http://java.sun.com/jsf/facelets" Template= "/templates/masterlayout.xhtml"
>
<ui:define
name= "Menuleft" >
<ui:include src= "/sections/login/menuleft.xhtml"/>
</ui:define>
<ui:define
<ui:include src= "/sections/login/content.xhtml"/>
</ui:define>
</ui:composition>
|
This login view uses the default content of the template for the title, header, and right menu of the window. It defines only the features that are specific to this login view: The content section and the left menu. By providing a <ui:define> tag for the window title, header, or right menu, I can also overwrite the default content for this template. For example, listing 3 shows the Source-viewer view (picture in the middle of Figure 1): Listing 3. Source-viewer View
<ui:composition xmlns= "http://www.w3.org/1999/xhtml"
Xmlns:ui= "Http://java.sun.com/jsf/facelets" Template= "/templates/masterlayout.xhtml"
>
<ui:define
name= "Content" >
<ui:include src= "/sections/showsource/content.xhtml"/>
</ui:define>
<ui:define
name= "Menuleft
" >
<ui:include src= "/sections/showsource/menuleft.xhtml"/>
</ui:define>
<ui:define
name= "Menuright" >
<ui:include src= "/sections/showsource/menuright.xhtml"/>
</ui:define>
</ui:composition>
|
The Source-viewer view defines the Content section and the contents of the right menu. It also overrides the default content for the left menu defined by the template in Listing 1. Listing 4 shows the places view (the picture at the bottom of Figure 1): Listing 4. Places View
<ui:composition xmlns= "http://www.w3.org/1999/xhtml"
Xmlns:ui= "Http://java.sun.com/jsf/facelets" Template= "/templates/masterlayout.xhtml"
>
<ui:define
name= "Menuleft" >
<ui:include src= "/sections/places/menuleft.xhtml"/>
</ui:define>
<ui:define
name= "Content" >
<ui:include src= "/sections/places/content.xhtml"/>
</ui:define>
</ui:composition>
|
|
JSF 2 Template features
The concept behind template functionality is simple. Defines a template to encapsulate features that are common in multiple views. Each view consists of one assembly and one template. When JSF creates a view, it loads the assembled template and then inserts the content defined by the assembly into the template. |
|
Please note the similarity between listings 2, 3, and 4. All three views specify a template and define the content. Also, note that it is easy to create a new view because most of the view's infrastructure is encapsulated within the template and included files. Another interesting thing about using the JSF template feature is that these views in listings 2, 3, and 4 do not change too much over time, so most view code does not need maintenance at all. Like a view using a template, the template itself changes very little. Because a large number of common features are encapsulated in almost no maintenance code, you can focus on the actual content of the view-for example, what the left menu of the login page should be. Focusing on the actual content of the view is the main thrust of the next technique.
|
back to the top of the page |
|
Tip 2: Use the combination approach Shortly after my CAD/CAM GUI was released, I spent months working with another developer, Bob, on a new project. We're based on Bob's code and, incredibly, we can easily make changes and fix bugs. I quickly realized that the biggest difference between Bob's code and my Code was that he wrote a small method-usually between 5 and 15 lines of code-and that his entire system was made up of these little methods. As I was still working on a long way to revise my previous project with a lot of focus, Bob had been smart about combining small methods with atomic functionality. Bob's code and my Code are fundamentally different in terms of maintainability and scalability, and since then I have come to believe in small ways. Although Bob and I didn't realize it, we used to use a design pattern of Smalltalk, called composed method (see Resources): At an abstract level, divide the software into multiple methods that perform a single task. The benefits of using the composed method model have been documented in large numbers (see "Evolutionary Architecture and emergent design: Combinatorial methods and SLAP" of Neal Ford) for more information. Here, I will focus on how to use the composed method pattern in JSF view. JSF 2 encourages the use of smaller view segments to assemble views. Templates encapsulate common functionality, which in turn divides the view into smaller chunks. JSF 2 also provides a <ui:include> tag, as I showed in my previous code listing, which allows you to further divide the view into smaller pieces of functionality. For example, Figure 2 shows the left menu of the login page for the places application: Figure 2. The left menu of the login page
Listing 5 shows the file that defines the contents of the menu: Listing 5 The implementation of the left menu of the login view
<ui:composition xmlns= "http://www.w3.org/1999/xhtml"
Xmlns:h= "Http://java.sun.com/jsf/html"
xmlns:ui= "Http://java.sun.com/jsf/facelets" >
<div class= "Menulefttext" >
#{msgs.welcomegreeting}
<div class= "Welcomeimage" >
</div>
</div>
|
The markup in Listing 5 is simple, making the file easier to read, understand, maintain, and extend. If the same code is buried in a very long XHTML page that contains all the content required to implement the login view, it can be cumbersome to change. Figure 3 shows the left menu of the places view: Fig. 3 The left menu of the places view
The implementation of the Places view's left menu is shown in Listing 6: Listing 6 The implementation of the left menu of the places view
<ui:composition xmlns= "http://www.w3.org/1999/xhtml" Xmlns:h= "Http://java.sun.com/jsf/html" Xmlns:ui= "Http://java.sun.com/jsf/facelets" Xmlns:util= "Http://java.sun.com/jsf/composite/components/util" >
<div class= "Placessearchform" > <div class= "Placessearchformheading" > #{msgs.findaplace} </div>
#{msgs.streetaddress}
#{msgs.city} #{msgs.state} #{msgs.zip} Style= "Font-family:palatino;font-style:italic" action= "#{place.fetch}"/>
</div>
<util:icon image = "#{resource[' images:back.jpg ']}" Actionmethod = "#{places.logout}" Style= "Border:thin solid LightBlue"/>
</ui:composition>
|
Listing 6 implements a form, and the form uses a single icon component. (I will then discuss the icon component in detail in the Icon Component section.) Currently, just know that the page author can use an icon to correlate images and methods. The image of this logout icon appears at the bottom of Figure 3, and the method of this logout icon-places.logout ()-as shown in Listing 7: Listing 7. Places.logout () method
Package com.clarity;
...
@ManagedBean ()
@SessionScoped
public class Places {
Private arraylist<place> places = NULL;
...
private static selectitem[] Zoomlevelitems = {
Public String logout () {
Facescontext FC = Facescontext.getcurrentinstance ();
Elresolver elresolver = Fc.getapplication (). Getelresolver ();
User user = (user) Elresolver.getvalue (
Fc.getelcontext (), NULL, "user");
User.setname ("");
User.setpassword ("");
Setplaceslist (NULL);
}
}
|
For me, the implementation of the left menu of the listing 6-places view-already very close to 30 lines of code length limits. This list is a bit difficult to read, and the forms and icons within the code fragment can be made to form their own files. Listing 8 shows the refactoring version of Listing 6, where forms and icons are encapsulated in their respective XHTML files: Listing 8. Refactoring the left menu of the places view
<ui:composition xmlns= "http://www.w3.org/1999/xhtml"
xmlns:ui= "Http://java.sun.com/jsf/facelets" >
<div class= "Placessearchformheading" >
#{msgs.findaplace}
</div>
<ui:include src= "addressform.xhtml
" >
<ui:include src= "logouticon.xhtml
" >
</div>
</ui:composition>
|
Listing 9 shows the addressform.xhtml: listing 9. addressform.xhtml
<ui:composition xmlns= "http://www.w3.org/1999/xhtml"
Xmlns:h= "Http://java.sun.com/jsf/html"
xmlns:ui= "Http://java.sun.com/jsf/facelets" >
#{msgs.streetaddress}
#{msgs.city}
#{msgs.state}
#{msgs.zip}
Style= "Font-family:palatino;font-style:italic"
action= "#{place.fetch}"/>
</ui:composition>
|
Listing 10 shows the logouticon.xhtml: List logouticon.xhtml
<ui:composition xmlns= "http://www.w3.org/1999/xhtml"
Xmlns:ui= "Http://java.sun.com/jsf/facelets"
Xmlns:util= "Http://java.sun.com/jsf/composite/components/util" >
<util:icon image= "#{resource[' images:back.jpg ']}"
Actionmethod= "#{places.logout}"
Style= "Border:thin solid LightBlue"/>
|
When you assemble views from multiple small files, you can enjoy the benefits of the Smalltalk composed method model. You can also organize these files to make it easier to react to changes. For example, Figure 4 shows the files that make up these three views within the places application: Figure 4. View of the places application
The three directories I created-views, sections, and templates-contain most of the XHTML files used to implement the places application view. Because the files in the views and templates directory rarely change, I am more concerned about the sections directory. For example, if I want to change the icon in the left menu of the login page, I know where to change it: sections/login/menuleft.xhtml. Of course, you can use any directory structure to organize your XHTML files. If the organization is reasonable, locating the code you want to modify will be very easy. In addition to following the DRY principle and using the composed method model, it is also a good practice to encapsulate functionality within a custom component. Components are a powerful reuse mechanism, and you should take full advantage of this power. Unlike JSF 1, it is easier to implement custom components using JSF 2.
|
back to the top of the page |
|
Tip 3: Keep in mind the idea of LEGO assembled toys When I was a boy, I had two favorite toys: one was a chemical combination (chemistry set) and one was a LEGO assembled toy. These two toys allow me to create things by combining basic building blocks, and this has been my lifelong hobby, but it is now a cover for software development. The advantage of JSF has always been in its component model, but this advantage is not fully implemented until now, because it is difficult to implement custom components with JSF 1. You have to write Java code, specify an XML configuration, and have a deep understanding of the lifecycle of JSF. With JSF 2, you can easily implement custom components: No configuration, no XML, no other. No Java code required. Developers can attach functionality to them. Perform a hot deployment after modification. In the remainder of this article, I'll show you how to implement three custom components for places applications: An icon, a login panel, and a panel that displays address maps and weather information. But first, let me start by outlining the JSF 2 composite component. Implementing Custom Components JSF 2 combines Facelets templates, resource processing (discussed in part 1th), and a simple naming convention to implement composite components. The composite component, as its name shows, allows you to assemble a new component from an existing component. In general, you implement composite components in XHTML within the resources directory and link them to a namespace and tag entirely by convention. Figure 5 shows how I organized these composite components for the places application: Figure 5. Components of the places application
To use a composite component, you need to declare a namespace and use a tag. This namespace is usually the Http://java.sun.com/jsf/composite plus directory name, which is where the component resides in the resources directory. The component name itself is the name of its XHTML file, except that there is no. XHTML extension. This Convention eliminates the need for configuration. For example, to use the login component in an places application, you should do this:
...
Xmlns:util= "Http://java.sun.com/jsf/composite/component/util
" >
...
<util:login
.../>
...
|
and to use the icon component, you need to do something like the following:
...
Xmlns:util= "Http://java.sun.com/jsf/composite/components/util
" >
...
<util:icon
.../>
...
|
Finally, to use the place component, you can do so as shown below:
...
Xmlns:util= "Http://java.sun.com/jsf/composite/component |
|