Sun Microsystems Logo

 

 
LAB-4430:  Developing Custom JSF Components For NetBeans Visual Web Pack

Version 4: 9 April 2007
Gail Anderson
gail@asgteach.com

Expected duration: 60 minutes

JavaServerTM Faces technology is a standard-based server-side user interface component framework for JavaTM technology-based web applications. In this hands-on lab, you will use NetBeans 5.5 to build a custom JSF component. In each exercise, you will build on the previous component, providing enhancements to the component (adding slide capabilities to an image component) and adding AJAX capabilities for a better user experience.

Copyright                     


Copyright 2007 Sun Microsystems, Inc. All rights reserved. Sun, Sun Microsystems, the Sun logo, Solaris, Java, the Java Coffee Cup logo, JavaOne, the JavaOne logo, and all Solaris-based and Java-based marks and logos are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries.


Prerequisites

This hands-on lab assumes you have some basic knowledge or programming experience on the following technologies.

Exercise 0: Configure Software

Software needed for the lab:

Please download and install the following software prior to starting Exercise 1 of the lab.
If you have any questions on installation, please feel free to send questions to the email alias mentioned below.

          Note: These libraries are provided in this zip file.
Notations used in this documentation

<JAVA_HOME> (or %JAVA_HOME% for Windows and $JAVA_HOME for Solaris/Linux)
This is the installation directory of J2SE 5.0 SDK
For Windows, for example, C:\jdk1.5.0_03
For Solaris/Linux, for example, $HOME/jdk1.5.0_03
<lab_root>  
This document uses <lab_root> to denote the directory under which you have unzipped the lab zip file of this hands-on lab. The name of the lab zip file of this hands-on lab is 4430_jsfcustom.zip.
Once you unzipped the lab zip file under <lab_root>, it will create a subdirectory called jsfcustom. For example, under Windows, if you have unzipped the lab zip file in the root of drive C:\, it will create C:\jsfcustom.  Under Linux/Solaris, if you have unzipped the lab zip file in the $HOME directory, it will create $HOME/jsfcustom directory.

Lab Exercises:

This lab is made of the several exercises. Please proceed through the lab by doing the exercises in sequence. In the first exercise, you will build a simple image JSF component. You will then add web page "slide" enhancements to the image component. In Exercise 3, you will add AJAX capabilities to the slide component.

Exercise 1: Build Simple JSF Image Component (30 minutes)

Exercise 2: Extend the Simple JSF Component to Make It an Image Slide Viewer (15 minutes)

Exercise 3: Provide AJAX Capabilities to the Image Slide Viewer (15 minutes)


Where to send questions or feedback on this lab and public discussion forums:
If you have any questions or feedback on this lab, consult the 4430_JSF_CustomComponent 
(https://hands-on-labs.dev.java.net/servlets/ForumMessageList?forumID=2545
forum under Project hands-on-labs at https://java-net.dev.java.net/.
RSS Feed  This Project's discussion forum is available in RSS format at the following URL:
https://hands-on-labs.dev.java.net/servlets/ProjectRSS?type=message&forumID=2545  Author(s)

  Exercise 1: Build Simple JSF Component (30 minutes)

Introduction:

In this exercise, you will build a custom JSF component using NetBeans. A JSF component is typically made up of three classes that work closely together. The renderer creates the client-side representation of the component. It takes any input (for input-type components) from the client and transforms it into something the component can understand. The typical renderer implementation (as in our example) will generate HTML. JSF components, however, are not limited to generating HTML.

The second class is a subclass of IOComponent (the component class). This class is responsible for the data and behavior of the component on the server side. Usually anything that the component is responsible for that is not related to rendering is found in the component class.

The third class is the tag handler. This class is responsible for creating the component, attaching the renderer to the component, and setting the fields on the component based on the values supplied in the JSP tag. The Tag Library Descriptor (tld) exposes the tag handler's tags. You will build these classes (and the TLD) in the following order:
  1. Tag Library Descriptor file (tld)
  2. Tag Handler
  3. Component Java Code (to maintain the component's state)
  4. Component Renderer (to build output for the JSP page) 
Note: For a graphic overview of the JSF artifacts you will either build or customize, see Figure 1-18 found in the Summary for this exercise.

Steps to follow:
  1. Create a new NetBeans™ Web Application Project 
  2. Create a Tag Library Descriptor File
  3. Create a Tag Handler Class
  4. Create the Component Class
  5. Create the Renderer Class
  6. Modify the faces-config.xml File
  7. Modify the JSF Client Web Application Page

Step 1: Create a new NetBeans Web Application Project

1. Choose File from menu bar and select New Project (Ctrl-Shift-N).

2. NetBeans displays the New Project dialog. Under the Choose Project pane, select Web (under Categories) and Web Application (under Projects).  This creates an empty Web application in a standard IDE project. A standard project uses an IDE-generated build script to build, run, and debug your project. Click Next.

New Project dialog

Figure 1-0: Create a new Web Application project.

3. NetBeans brings up the New Web Application window. For Project Name, specify ImageComponent. For Project Location click the Browse button and navigate to <lab_root>/jsfcustom. For Server, select Sun Java System Application Server 9, as shown in Figure 1-1 below.

New Web Application dialog
    Figure 1-1: New Web Application dialog.

4. Click Next. NetBeans brings up the next New Web Application window. In the Frameworks Panel, select JavaServer Faces, as shown in Figure 1-2 below. When you select JavaServer Faces, NetBeans builds a web application structure that includes configuration and JSP files needed for JSF (such as faces-config.xml).

New Web Application dialog

Figure 1-2: New Web Application - Frameworks dialog
5. Click Finish. NetBeans initializes a new Web Application project, as shown in the Projects window below (Figure 1-3).
Projects Window

Figure 1-3: NetBeans Projects window
Step 2: Create a Library Descriptor File
1. In the Projects window, select project ImageComponent, right-click, and select New > File/Folder. NetBeans displays the New File / Choose File Type dialog.

2. Under Categories, select Web. Under File Types, select Tag Library Descriptor, as shown in Figure 1-4. This will create a Tag Library Descriptor (TLD) file, which can be used with JavaServer Pages (JSP) files. A tag library descriptor is an XML document that contains information about a library as a whole and about tags and tag files contained in the library. You'll use the Tag Handler wizard to define the tags.  Click Next.

New File dialog

Figure 1-4: New File - Choose File Type dialog

3. NetBeans displays the New Tag Library Descriptor dialog. Specify TLD Name: image, as shown in Figure 1-5 and click Finish. You'll now see file image.tld in the Projects window under WEB-INF / tlds and it also appears in the XML editor.

New Tag Library Descriptor dialog  

Figure 1-5: New Tag Library Descriptor dialog
Step 3: Create a Tag Handler Class
Tag handlers are server side Java components used to create custom tags for JSP files.

1. In the Projects window, select project ImageComponent, right-click, and select New > File/Folder. NetBeans displays the New File / Choose File Type dialog.

2. Under Categories, select Web. Under File Types, select Tag Handler, as shown in Figure 1-6. This will create a Java class file that will be the tag handler class for your custom component.  Click Next.

New File dialog

Figure 1-6: New File - Choose File Type dialog

3. NetBeans displays the New Tag Handler dialog. For Class Name specify ImageTagHandler and for Package type in com.examples.image (Figure 1-7). Click Next.

New Tag Handler dialog  

Figure 1-7: New Tag Handler dialog

4.  NetBeans displays the New File - TLD Information dialog. An error message tells you to select the corresponding TLD file, which you'll do now. Click the Browse button.
5.  NetBeans displays a Browse Files window. Expand the WEB-INF and tlds nodes. Select file image.tld and click Select File (Figure 1-8).

Browse and Select image.tld file

Figure 1-8: Browse Files and select file image.tld

6.  You'll now provide the TLD information. Use Figure 1-9 and Figure 1-10 as guides. For Tag Name, specify image. To the right of the Attributes box, click New to define attribute url, as shown in Figure 1-10. For Attribute Name, specify url. Check required attribute. (This is the only attribute that is required. The other attributes will not be required.) Leave all other values unchanged, as shown in Figure 1-9. Click OK. This adds attribute url to the Attributes box in the TLD Information dialog.

Add new attribute dialog

Figure 1-9: Add New Attribute dialog

7. Now add attributes width, height, and style by clicking the New button again, as shown in Figure 1-10 below. Accept the defaults for these attributes. When you have finished adding these attributes, click Finish.

TLD Information dialog  

Figure 1-10: TLD Information dialog

You'll now modify the tag handler class (ImageTagHandler.java) that you just created.

Note that class ImageTagHandler contains the properties (and their respective setters and getters) that you specified in the TLD Information dialog (Figure 1-10 above).

1.  Working with ImageTagHandler.java in the Java editor, replace
 

  public class ImageTagHandler extends SimpleTagSupport {

   


with


  public class ImageTagHandler extends UIComponentELTag {

   

This allows the tag handler class to access the JSF expression factory to set the properties of the component. You can then use JSF's Expression Language (EL) binding mechanism to set  the component's property values. This creates a few errors in your Java file, which you will fix in the steps below.

2. Add the following import statement (or right-click and select Fix Imports from the context menu).


  import javax.faces.webapp.UIComponentELTag;

   


3.  In the Navigator window, double-click method doTag() to bring this method into view in the editor pane. Now remove the doTag() method and its body.

4. Scroll the editor window back up and add the following code after the class declaration. (Copy and paste the code from this document in the lab directory. To fix any code formatting, right-click anywhere inside the NetBeans editor and select Reformat Code from the context menu.)


    private static final String IMAGE_COMP_TYPE = "IMAGE_COMPONENT";
  private static final String IMAGE_RENDERER_TYPE = "IMAGE_RENDERER";

   


5. Implement the abstract methods getComponentType() and getRendererType() as follows. (Copy and paste and use Reformat Code as needed.) After you add these methods, the errors associated with extending class UIComponentELTag should disappear.
  

public String getComponentType() {

  return IMAGE_COMP_TYPE;

}

public String getRendererType() {
  return IMAGE_RENDERER_TYPE;
}
   

6. Add the setProperties() method and implement its body as follows. (Copy and paste and use Reformat Code as needed.) After adding the code, right-click and select Fix Imports from the context menu. This will add an import statement for UIComponent. You will resolve the reference to class ImageComponent in the next step.


    protected void setProperties(UIComponent component) {
        super.setProperties(component);
        ImageComponent imgComponent =
                ((ImageComponent)component);
        if (url != null) {
            imgComponent.setUrl(url);
        }
      
        if (width != null) {
            imgComponent.setWidth(width);
        }
      
        if (height != null) {
            imgComponent.setHeight(height);
        }
      
        if (style != null) {
            imgComponent.setStyle(style);
        }
    }


Note: You have not yet defined Java class ImageComponent, resulting in an error in the Java class ImageTagHandler.java that you're editing. This error will be resolved after you create the ImageComponent class (in the next step).

7. Save your work by clicking the Save All icon on the icon toolbar (or select File > Save All from the main menu bar).
Step 4: Create the Component Class

The component class controls the server-side behavior of a JSF component.

1. In the Projects window, expand node Source Packages. Right-click on package com.examples.image and select New > Java Class from the context menu. NetBeans displays the New Java Class dialog.

2. For Class Name, specify ImageComponent, as shown in Figure 1-11. Click Finish. NetBeans creates class ImageComponent.java under package node com.examples.image and brings up this file in the Java Editor.

New Java Class dialog

Figure 1-11: New Java Class dialog

NetBeans lets you add properties to a Java class file from the Bean Patterns node of the Java class in the Projects window.

1.  From the Projects window, expand nodes Source Packages > com.examples.image > ImageComponent.java > ImageComponent. Right-click Bean Patterns and select Add > Property from the context menu, as shown in Figure 1-12.

Add Property using Bean Patterns node

Figure 1-12: Add Property using the Bean Patterns node

2. NetBeans displays the New Property Pattern dialog. For Name, specify url. Accept the defaults for all of the other dialog fields. Click OK to add property url and its getter and setter to ImageComponent.java, as shown in Figure 1-13.

New Property Pattern dialog

Figure 1-13: New Property Pattern dialog

3. Repeat steps 1 and 2 above to add properties width, height, and style to ImageComponent.java. When you're finished, the Bean Patterns node (expand it) should show all four properties, as shown in Figure 1-14 below.

Bean Patterns display

Figure 1-14: Expanding Bean Patterns node for ImageComponent

You'll now modify the component class (ImageComponent.java) using the Java Editor.

1. File ImageComponent.java should be active in the Java Editor. Modify the class declaration so that ImageComponent extends UIOutput.

  public class ImageComponent extends UIOutput{


2. Right-click inside the editor and select Fix Imports. This will add an import statement for class javax.faces.component.UIOutput.
3. Add a declaration for field IMAGE_COMP_FAMILY, as follows.


  private static final String IMAGE_COMP_FAMILY = "IMAGE_FAMILY";



4. Add method getFamily(), as follows.


  public String getFamily() {
      return IMAGE_COMP_FAMILY;
  }
 


5. Save your work by clicking the Save All icon on the icon toolbar (or select File > Save All from the main menu bar). (After you save your work from these steps, the Java Editor errors will no longer appear in file ImageTagHandler.java.)

Step 5: Create the Renderer Class
Recall that the renderer creates the client-side representation of the component. In our example, the renderer class will write HTML code to render the custom image component.

1. From the Projects window, right-click the package node com.examples.image and select New > Java Class. NetBeans displays the New Java Class dialog.

2. For Class Name, specify ImageRenderer and click Finish, as shown in Figure 1-15.

New Java Class dialog

Figure 1-15: New Java Class dialog

NetBeans creates Java class ImageRenderer.java and makes it active in the Java Editor. You will now modify the renderer class (ImageRenderer.java) using the Java Editor.

1. File ImageRenderer.java should be active in the Java Editor. Modify the class declaration so that ImageRenderer extends Renderer. (Ignore the error for right now.)

  public class ImageRenderer extends Renderer {



2. Add method encodeBegin(), as follows. (Copy and paste from this document file.)


   public void encodeBegin(FacesContext context, UIComponent component)
                    throws IOException {
        ImageComponent imgComponent = (ImageComponent)component;
       
        ResponseWriter writer = context.getResponseWriter();
        writer.startElement("div",component);
        String id = (String)component.getClientId(context);
       
        // get image elements height and width
        String width = imgComponent.getWidth();
        String height = imgComponent.getHeight();
       
        // Add code to implement "style" attribute for image
        String style = imgComponent.getStyle();
        style = (style!=null) ? style + ";" : "";
       
        if (width != null)
            style += "width:" + width + ";";
       
        if (height != null)
            style += "height:" + height + ";";
       
        writer.writeAttribute("style", style, null);
       
        ServletContext servletContext =
              (ServletContext) context.getExternalContext().getContext();
        String contextPath = servletContext.getContextPath();
        writer.startElement("img", imgComponent);
        writer.writeAttribute("src",
               contextPath + imgComponent.getUrl(), "url");
       
        writer.writeAttribute("width", imgComponent.getWidth(), "width");
        writer.writeAttribute("height", imgComponent.getHeight(), "height");
 
        writer.endElement("div");

    }  


3. Right-click inside the editor and select Fix Imports. This will add the following import statements to the class. Note: When prompted, be sure to select javax.faces.render.Renderer for class Renderer.
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.Renderer;
import javax.servlet.ServletContext;
 

4. Save your work by clicking the Save All icon on the icon toolbar (or select File > Save All from the main menu bar).

Step 6: Modify the faces-config.xml File
1.  In the Projects window, double-click file faces-config.xml under the Web Pages > WEB-INF nodes. NetBeans brings up the file in the XML Editor.
2.  Add the following configuration information to the file (inside the <faces-config> tags). Copy and paste from this document file. This tells the JSF system the information it needs to access the component and its renderer.


  <faces-config . . . >
<component>
  <component-type>
    IMAGE_COMPONENT
  </component-type>
  <component-class>
    com.examples.image.ImageComponent
  </component-class>
</component>

<render-kit>
  <renderer>
   <description>
     Renderer for the image component.
   </description>
   <component-family>
     IMAGE_FAMILY
   </component-family>
   <renderer-type>
     IMAGE_RENDERER
   </renderer-type>
   <renderer-class>
     com.examples.image.ImageRenderer
    </renderer-class>
  </renderer>
</render-kit>  
  </faces-config>


4. Save your work by clicking the Save All icon on the icon toolbar (or select File > Save All from the main menu bar).

5. Build the project. Select Build > Build Main Project from the main menu bar. This step is necessary, since the IDE accesses the class files you created for your component when it parses the JSP client page.

Step 7: Modify the JSF client web application page
1.  Add file image1.jpg to your project.  Select File > Add Existing Item > Image File . . .   NetBeans pops up Add Existing File dialog.

2.  Navigate to <lab_root> and select file image1.jpg. Click Add. NetBeans adds file image1.jpg under the Web Pages / resources node in the Projects window, as shown below in Figure 1-16.

Projects resources node  

Figure 1-16: resources node after adding image file to Project

You'll now modify the welcomeJSF.jsp file to access your custom JSF component.

1. In the Projects window under Web Pages, double-click file welcomeJSF.jsp. NetBeans makes it active in the XML Editor.

2.  Add the image taglib element (after the JSF/Html taglib element).


  <%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%>
  <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%>
 
  <%@taglib prefix="image" uri="/WEB-INF/tlds/image"%>



3. Add the following <image> tag inside the <f:view> tags, as follows. (Note that if you type this code in by hand, that the XML editor helps with the attributes.)


  <f:view>
      <h1><h:outputText value="JavaServer Faces" /></h1>
      <image:image url="/resources/image1.jpg" width="320px"/>
  </f:view>




4. Click the Run icon in the icon toolbar (or select Run > Run Main Project from the main menu bar). NetBeans deploys your application and brings it up in a browser.

5.  Click on the link JavaServer Faces Welcome Page in the browser. This brings up page welcomeJSF.jsp. Here is the web application running in a browser with the test image file displayed.

Browser output  

Figure 1-17: JSF Web Application running in a browser

Summary:

In this exercise, you created a custom JSF component using the NetBeans IDE. The component required a Tag Library Descriptor (image.tld), a tag handler class (ImageTagHandler), a component class (ImageComponent), and a renderer class (ImageRenderer). Figure 1-18 shows the relationship among the JSF artifacts you created in this project.

Relationship among JSF Artifacts    

Figure 1-18: Relationships among the JSF files you created in this exercise

                                                                                               
                                                                                              return to the top


Exercise 2: Extend the Simple JSF Component to Make It an Image Slide Viewer (15 minutes)

Introduction:

In this exercise you will extend the simple image component so that it displays a sequence of images one at a time, much like a slide viewer. This enhanced image component will have previous (<) and next (>) links that allow the user to step through a set of images. Each time the next/prev link is clicked, the HTML form is submitted and the page is rerendered with the new image.

No new attributes are added to the image tag; the url attribute is reused. You specify multiple images by concatenating filenames together. For example, to implement a slide viewer with images image1.jpg, image2.jpg, and image3.jpg, you use the following image tag:

<image:image url="/resources/image1.jpg,/resources/image2.jpg,/resources/image3.jpg"/>

Several modifications to the original image component implement this behavior.

The ImageComponent class is modified:
Two additional properties are added to the ImageComponent:
The saveState() and restoreState() methods of the component are implemented (to save the ImageComponent's state after a new page submit).
The ImageTagHandler is modified:
The url attribute is parsed to set the new imageUrls property of ImageComponent.
The ImageComponent's url property is set to the first url from the url attribute.
The ImageRenderer is modified:
An HTML <table> element is rendered to hold the previous link, the image, and the next link.
JavaScript is used to submit the form when the prev or next link is clicked.
A hidden field is rendered to hold information used to determine if the prev or next link is clicked.

Steps to follow:
  1. Add additional properties to ImageComponent.
  2. Add saveState() and restoreState() methods to ImageComponent.
  3. Modify ImageTagHandler to set these new properties.
  4. Modify ImageRenderer encodeBegin() method to include left and right links and decode the submitted form.
  5. Modify the web page to include additional images using the updated url attribute.
Step 1: Add additional properties to ImageComponent.

Note: Before you start with Exercise 2, make sure that you have successfully completed the steps in Exercise 1. You will start with project ImageComponent that you built in Exercise 1. If  you were unable to complete Exercise 1, use project <lab_root>/jsfcustom/Exercise1Project/ImageComponent as the starting point for this exercise.

You will add two properties to ImageComponent.java.

1.  From the Projects window, expand nodes Source Packages > com.examples.image > ImageComponent.java > ImageComponent. Right-click Bean Patterns and select Add > Property from the context menu, as shown in Figure 2-1.

Bean Patterns Add Property  

Figure 2-1: Add Property using the Bean Patterns node

2. NetBeans displays the New Property Pattern dialog. For Name, specify imageIndex. For Type, specify int (lower case). Accept the defaults for all of the other dialog fields. Click OK to add property imageIndex and its getter and setter to ImageComponent.java, as shown in Figure 2-2.

New Property Pattern dialog  

Figure 2-2: New Property Pattern dialog (imageIndex)

3. Repeat steps 1 and 2 above to add property imageUrls, an array of Strings to ImageComponent. For Name, specify imageUrls. For Type, specify String[]. Click OK as shown below in Figure 2-3.

New Property Pattern dialog - imageUrls

Figure 2-3: New Property Pattern dialog (imageUrls)


When you're finished, the Bean Patterns node (expand it) should show seven properties, as shown in Figure 2-4 below.

Bean Patterns Display  

Figure 2-4: Expanding Bean Patterns node for ImageComponent

Step 2. Add saveState() and restoreState() methods to ImageComponent.
You'll now modify the component class (ImageComponent.java) using the Java Editor.

File ImageComponent.java should be active in the Java Editor. You will implement the saveState() and restoreState() methods.  These methods have been inherited from class UIOutput. In saveState() you save all the property values in an array of Objects. The first element holds the result of super.saveState(). Note that you must convert imageIndex to Integer, since all saved values must be Serializable.

1. Copy and paste the following code for method saveState().

 public Object saveState(FacesContext context) {
     Object values[] = new Object[6];
     values[0] = super.saveState(context);
     values[1] = imageUrls;
     values[2] = new Integer(imageIndex);
     values[3] = width;
     values[4] = height;
     values[5] = style;
     return values;
 }


2. Right-click inside the editor and select Fix Imports. This will add an import statement for class javax.faces.context.FacesContext.

3. Copy and paste the following code for method restoreState(). After retrieving the array of Objects (state), each property is set from the same element to which it was saved. Here is method restoreState().

 public void restoreState(FacesContext context, Object state) {
    Object values[] = (Object[]) state;
    super.restoreState(context, values[0]);
    imageUrls = (String[]) values[1];
    imageIndex = ((Integer)values[2]).intValue();
    width =  (String) values[3];
    height = (String) values[4];
    style = (String) values[5];
 }



4. Save your work by clicking the Save All icon on the icon toolbar (or select File > Save All from the main menu bar).
Step 3. Modify ImageTagHandler to set these new properties.
Recall that it is the job of the Tag Handler class to process the JSP tags in the client's web page and set the appropriate properties in the component. In this step, you'll modify ImageTagHandler.java to parse the url property (using split and trim) and set ImageComponent's imageUrls property. You'll set ImageComponent's url property to the first element of the tag handler's imgUrls array. These changes are all in the setProperties() method of ImageTagHandler.java.

1.  Bring up ImageTagHandler.java in the Java editor. Replace method setProperties() with the following updated code. (Only the code in blue has changed. You can just replace the code that sets the url property, as shown below in blue.)

   protected void setProperties(UIComponent component) {
        super.setProperties(component);
       
        ImageComponent imgComponent = ((ImageComponent)component);

        if (url != null) {
            String[] imgUrls = url.trim().split(",");
            imgComponent.setUrl(imgUrls[0]);
            imgComponent.setImageUrls(imgUrls);

        }
      
        if (width != null) {
            imgComponent.setWidth(width);
        }
      
        if (height != null) {
            imgComponent.setHeight(height);
        }
      
        if (style != null) {
            imgComponent.setStyle(style);
        }
    }

   


2. Save your work by clicking the Save All icon on the icon toolbar (or select File > Save All from the main menu bar).
Step 4. Modify ImageRenderer to include left and right links and decode the submitted form.
The ImageRenderer is responsible for rendering the component. To handle the "image slide," the renderer builds HTML code that is similar to the following. (Both the Javascript and HTML use the generated ID which changes. For clarity we omitted the "id" names.)
<table>
   <tr>
      <td>
         <a href="javascript:PostBack('Prev')"> (prev link)
      </td>
      <td>
         <img> (image box)
      </td>
      <td>
         <a href="javascript:PostBack('Next')"> (next link)
      </td>
   <tr>
</table>

The image component is nested within a form on the JSP page. When the user clicks either the previous or next link, the Javascript associated with the onClick mouse event submits the form. A hidden field is also rendered along with the table to hold information (provided by the Javascript) about whether the previous or next link is clicked. The rendered Javascript is similar to the following.
var form = document.forms['form'];
function PostBack(navigation) {
   if (form.onsubmit == null || form.onsubmit()) {
       document.getElementById(hidden_id).value
= navigation;
       form.submit();
   }
}

1.  Bring up ImageRenderer.java in the Java editor. Add the following helper functions: getForm(), getHiddenFieldName(), getPrevLinkName(), and getNextLinkName(). Copy and paste from this document file.

   private UIForm getForm(UIComponent component) {
        UIComponent parent = component.getParent();
        while (parent != null) {
            if (parent instanceof UIForm) {
                break;
            }
            parent = parent.getParent();
        }
        if (parent == null) {
            throw new IllegalStateException("Not nested inside a form!");
        }
        return (UIForm) parent;
    }
   
    private String getHiddenFieldName(FacesContext context, UIComponent component) {
        String formClientId = getForm(component).getId();
        String componentClientId = component.getId();
        return formClientId + "_" + componentClientId + "_hidden1";
    }
   
    private String getPrevLinkName(FacesContext context, UIComponent component){
        String formClientId = getForm(component).getId();
        String componentClientId = component.getId();
        return formClientId + "_" + componentClientId + "_prev";
    }
   
    private String getNextLinkName(FacesContext context, UIComponent component){
        String formClientId = getForm(component).getId();
        String componentClientId = component.getId();
        return formClientId + "_" + componentClientId + "_next";
    }

   



2. Right-click inside the editor and select Fix Imports. This will add an import statement for class javax.faces.component.UIForm.

3. Replace method encodeBegin() with the following code in ImageRenderer.java. Copy and paste from this document file. After the style attribute is built, the renderer builds the Javascript and the hidden field. Finally, the HTML table is rendered with the next/previous links and the img element.


   public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
        ImageComponent imgComponent = (ImageComponent)component;
       
        ResponseWriter writer = context.getResponseWriter();
        writer.startElement("table", imgComponent);
        String id = (String)imgComponent.getClientId(context);
        writer.writeAttribute("id", id, null);
       
        // get image elements height and width
        String width = imgComponent.getWidth();
        String height = imgComponent.getHeight();
       
        // Add code to implement "style" attribute for image
        String style = imgComponent.getStyle();
        style = (style!=null) ? style + ";" : "";
       
        if (width != null)
            style += "width:" + width + ";";
       
        if (height != null)
            style += "height:" + height + ";";
       
        writer.writeAttribute("style", style, null);
       
        // Render the Script element that holds Java Script code for form postback
       
        UIForm form = getForm(imgComponent);
        String formClientId = form.getClientId(context);
        String postBackFuncName = imgComponent.getId() + "PostBack";
       
        writer.startElement("script", imgComponent);
        writer.writeAttribute("type", "text/javascript", null);
        String varFormName = form.getId() + imgComponent.getId() + "Form";
        String script = "\nvar " + varFormName + " = document.forms['" + formClientId + "'];" +
                "\nfunction" + " " + postBackFuncName + "(element) {\n" +
                "   if (" + varFormName + ".onsubmit == null || " + varFormName +
                ".onsubmit()) {\n" +
                "       " + varFormName +
                "." + getHiddenFieldName(context, imgComponent) +
                ".value = element.id; \n" +
                "       " + varFormName + ".submit();" +
                "\n   } \n} \n";
        writer.writeComment(script);
        writer.endElement("script");
       
        // Hidden field to hold prev/next information
       
        writer.startElement("input", imgComponent);
        writer.writeAttribute("type", "hidden", null);
        writer.writeAttribute("name", getHiddenFieldName(context, imgComponent), null);
        writer.writeAttribute("value", "", null);
        writer.endElement("input");
       
        // Render the tr Element
        writer.startElement("tr", imgComponent);
       
        // Render the td Element for prev link
        writer.startElement("td", imgComponent);
       
        // Render Next Image link
        writer.startElement("a", imgComponent);
        writer.writeAttribute("onClick", "javascript:" + postBackFuncName + "(this)", null);
        writer.writeAttribute("id", getPrevLinkName(context, component), null);
        String prevStr = "<";
        writer.writeText(prevStr.toCharArray(),0,prevStr.length());
        writer.endElement("a");
       
        writer.endElement("td");
       
        // Render the td Element for image
        writer.startElement("td", imgComponent);
       
        // Render the Image
        ServletContext servletContext = (ServletContext)
                   context.getExternalContext().getContext();
        String contextPath = servletContext.getContextPath();
        writer.startElement("img", imgComponent);
        writer.writeAttribute("src", contextPath + imgComponent.getUrl(), "url");
        writer.writeAttribute("width", imgComponent.getWidth(), "width");
        writer.writeAttribute("height", imgComponent.getHeight(), "height");
       
        writer.endElement("td");
       
        // Render the td Element for next link
        writer.startElement("td", imgComponent);
       
        // Render Next Image link
        writer.startElement("a", imgComponent);
        //writer.writeAttribute("href","",null);
        writer.writeAttribute("onClick", "javascript:" + postBackFuncName + "(this)", null);
        writer.writeAttribute("id", getNextLinkName(context, component), null);
        String nextStr = ">";
        writer.writeText(nextStr.toCharArray(),0,nextStr.length());
        writer.endElement("a");
       
        writer.endElement("td");
       
        writer.endElement("tr");
       
        writer.endElement("table");
    }

   



4. Add method decode() to ImageRendered.java. Copy and paste from this document file. This method "decodes" the form. It adjusts the imgUrls index value depending on whether the hidden field value corresponds to the previous or next link. It also sets the ImageComponent's url property using the new index value.



   public void decode(FacesContext context, UIComponent component) {
        if (context == null || component == null) {
            throw new NullPointerException();
        }
        ImageComponent imgComponent = (ImageComponent)component;
       
        String clientId = imgComponent.getClientId(context);
        String hiddenFieldName = getHiddenFieldName(context, imgComponent);
        Map requestParameterMap = context.getExternalContext().getRequestParameterMap();
        String value = (String) requestParameterMap.get(hiddenFieldName);
       
        String[] imgUrls = imgComponent.getImageUrls();
       
        int imgIndex = imgComponent.getImageIndex();
       
        if (value.equals(getPrevLinkName(context, component))){
            if (imgIndex > 0){
                imgComponent.setImageIndex(--imgIndex);
            }
        }else if (value.equals(getNextLinkName(context, component))){
            if (imgIndex < imgUrls.length - 1){
                imgComponent.setImageIndex(++imgIndex);
            }
        }
        imgComponent.setUrl(imgUrls[imgIndex]);
    }

   



5. Right-click inside the editor and select Fix Imports. This will add an import statement for class java.util.Map.

6. Save your work by clicking the Save All icon on the icon toolbar (or select File > Save All from the main menu bar).

Step 5. Modify the web page to include additional images using the updated url attribute.
1.  Add file image2.jpg to your project.  Select File > Add Existing Item > Image File . . .   NetBeans pops up Add Existing File dialog.

2.  Navigate to <lab_root> and select file image2.jpg. Click Add. NetBeans adds file image2.jpg under the Web Pages / resources node.

3.  Repeat steps 1 and 2 above and add files image3.jpg, image4.jpg, and image5.jpg. When you have finished adding these files, expand the resources node to see the added files in the Projects window, as shown below in Figure 2-5.

resources node with added jpg files    

Figure 2-5: resources node after adding image file to Project

You'll now modify the welcomeJSF.jsp file to access your custom JSF component.

1. In the Projects window under Web Pages, double-click file welcomeJSF.jsp. NetBeans makes it active in the XML Editor.
2. Add the following <h:form> and <image> tags inside the <f:view> tags, as follows. Copy and paste from this document's file.  Note: the url attribute should be on a single line. It should not span two lines in the JSP page source.

  <body>
        <f:view>
            <h1><h:outputText value="JavaServer Faces" /></h1>
            <h:form>
                <image:image
                    url="/resources/image1.jpg,/resources/image2.jpg,/resources/image3.jpg,/resources/image4.jpg,/resources/image5.jpg"
                    width="320px"/>
            </h:form>
   
        </f:view>
    </body>




4. Click the Run icon in the icon toolbar (or select Run > Run Main Project from the main menu bar). NetBeans deploys your application and brings it up in a browser.

5.  Click on the link JavaServer Faces Welcome Page in the browser. This brings up page welcomeJSF.jsp. Here is the web application running in a browser with the third image displayed (the next link was clicked twice).

Exercise 2 running in a browser    

Figure 2-6: Image Slide Viewer custom JSF component
Summary:

In this exercise, you created an Image Slide Viewer component by extending the simple image JSF custom component that you created in Exercise 1. This involved modifications to the component class ImageComponent (you added the imageIndex and imageUrls properties) and the tag handler ImageTagHandler (you added saveState() and restoreState() methods and you provided the code to split the url attribute and set the component's imageUrls property). You also updated the renderer ImageRenderer to render HTML output that included a table, embedded JavaScript to submit a form, and a hidden field to specify next or previous for the links. The web page image tag was modified to include all five image files that were imported into your project.
                                                                                              return to the top


Exercise 3: Provide AJAX Capabilities to the Image Slide Viewer (15 minutes)

Introduction:

The image slide viewer component you built in the previous exercise works fine, but each time you click the previous (<) or next (>) link, JSF submits the page and it is completely reloaded. In this very small example, you don't particularly notice much of a delay since there are only a few items on the page (the slide viewer and the JSF outputText component). To enhance  slider viewer component, you'll now add AJAX capabilities.

What is AJAX?

Asynchronous JavaScript Technology and XML (AJAX) is a set of technologies that together allows a web application to be (or appear to be) highly responsive. AJAX provides a more responsive web application because it supports asynchronous and partial requests and refreshes of a web page. The request/refresh is asynchronous because it can occur at any time without regard to other events that the user may trigger. This means that a user can continue interacting with the web page without having to wait for a response from the server before continuing. In other words, while the asynchronous event is being handled, the user can provide text in another input field, for example. The refresh is partial because only those elements on the page that change are refreshed. A big part of this is inserting specialized JavaScript code to detect specific mouse events and build the asynchronous request. More JavaScript code is then used to present the response to the user.

AJAX-enabled web applications use these techniques:
By using the JSF framework to build the slide viewer custom component, you can encapsulate the required JavaScript inside the component. (And, you've already seen this, since the JavaScript code written for the image slide viewer in the previous exercise resided in the ImageRenderer class, not on the JSP page.) Furthermore, you will use Dynamic Faces to implement the AJAX support for this component.

What is Dynamic Faces?

Dynamic Faces (DynaFaces) extends the JSF lifecycle to work on AJAX requests. It includes a ready to use JavaScript library so that the code to implement AJAX is minimal. To use DynaFaces, you import a set of JAR files (libraries) into your NetBeans project and add additional configuration in the project's web.xml file.

After you have implemented the AJAX enhancements for the slide viewer component, the web application will provide partial refreshes with the component's links. That is, when you click a previous or next link, this request is submitted  asynchronously to the server and new rendering output is produced and injected into the page's Document Object Model (DOM). Only the affected elements are rerendered. For a page that has many components, this provides a much more responsive user experience and the perception of improved performance for the application.

Here is a summary of the modifications to implement the AJAX enhancements.

Four JAR files (libraries) are added to the project. These are located at <lab_root>/lib. These contain all the support for the DynaFaces JSF extension.

The welcomeJSF.jsp page is modified to use DynaFaces:

Include the DynaFaces taglib element:
<%@taglib prefix="jsfExt" uri="http://java.sun.com/jsf/extensions/dynafaces" %>
Use the scripts component <jsfExt:scripts /> . This is a convenience component that renders all the scripts necessary for using the JSF-extensions AJAX feature. You place this inside the <f:view> tag before using any  of the AJAX JavaScript objects.

The ImageTagHandler class: No modifications

The ImageComponent class: No modifications

The ImageRenderer is modified:

The rendered Javascript code is modified so that it invokes the DynaFaces.fireAjaxTransaction function (exposed by the Dynamic Faces JavaScript library). The updated code is similar to the following where the hidden component holds the value of the link (previous or next).
var form = document.forms['form'];
function PostBack(navigation) {
   if (form.onsubmit == null || form.onsubmit()) {
       document.getElementById(hidden_id).value = navigation;
       DynaFaces.fireAjaxTransaction(element,{execute:'id',render:'id',inputs:'hidden_id'}); }
}

Figure 3-1 shows how the DynaFaces JavaScript components interface with the JSF Servlet.
  Dynamic Faces and JSF Architecture
Figure 3-1: Dynamic Faces  and JSF Architecture
Steps to follow:
  1. Add DynaFaces Libraries to the IDE.
  2. Modify the ImageRenderer to invoke the AJAX transaction.
  3. Modify the project's web.xml file.
  4. Modify the web page to include the DynaFaces taglib and scripts tag.
Step 1: Add DynaFaces Libraries to the IDE.

Note: Before you start with Exercise 3, make sure that you have successfully completed the steps in Exercise 2. You will start with project ImageComponent that you built in Exercise 2. If  you were unable to complete Exercise 2, use project <lab_root>/jsfcustom/Exercise2Project/ImageComponent as the starting point for this exercise.
1. In the Projects window, right-click on project ImageComponent and select Properties from the context window. NetBeans displays the Project Properties dialog.

2. In the Categories panel, select Libraries as shown in Figure 3-2.

Project Properties dialog

Figure 3-2: Project Properties dialog.

3. Click the Add JAR/Folder button. NetBeans displays the Add JAR/Folder dialog.

4. Browse to <lab_root>/jsfcustom/libs and select all of the jar files (use <Shift-Click> to multiply select the files). Click Open as shown in Figure 3-3.

Add JAR/Folder Dialog

Figure 3-3: Add JAR/Folder dialog with the DynaFaces support libraries all selected
.


5. NetBeans returns to the Projects Properties dialog. Click OK to add the jar files to your project, as shown below in Figure 3-4.

Project Properties dialog

Figure 3-4: Project Properties dialog after adding the DynaFaces jar files to your project.


6. In the Projects window, expand the Libraries node and make sure that all four jar files have been added to your project as shown in Figure 3-5.

Libraries node expanded

Figure 3-5: Expanded Libraries node in the Projects window showing the added DynaFaces jar files.

Step 2: Modify the ImageRenderer to invoke the AJAX transaction.
You'll now modify the ImageRenderer to invoke an AJAX request that will be called when the user clicks either the previous (<) or next (>) links on the web page. The AJAX reques