Version 4:
9 April 2007
Gail Anderson
gail@asgteach.com
Copyright
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.
- Download and install NetBeans IDE 5.5 (http://www.netbeans.info/downloads/index.php). The NetBeans installation should include the Java EE 5 Tools Bundle (NetBeans IDE 5.5, NetBeans Enterprise Pack 5.5, and Sun Java Application Server PE 9).
Note: These libraries are provided in this zip file.
- Download and unzip lab file (4430_jsfcustom.zip) under a directory of your choice, <lab_root>. Read <lab_root>/index.html to proceed.
This is the installation directory of J2SE 5.0 SDK<lab_root>
For Windows, for example, C:\jdk1.5.0_03
For Solaris/Linux, for example, $HOME/jdk1.5.0_03
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.
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/.
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=2545Author(s)
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.
![]()
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.
![]()
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).
![]()
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).
Step 2: Create a Library Descriptor File![]()
Figure 1-3: NetBeans Projects window
1. In the Projects window, select project ImageComponent, right-click, and select New > File/Folder. NetBeans displays the New File / Choose File Type dialog.Step 3: Create a Tag Handler Class
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.
![]()
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.
![]()
Figure 1-5: New Tag Library Descriptor dialog
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.
![]()
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.
![]()
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).
![]()
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.
![]()
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.
![]()
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.Step 4: Create the Component Class
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 methoddoTag()to bring this method into view in the editor pane. Now remove thedoTag()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 methodsgetComponentType()andgetRendererType()as follows. (Copy and paste and use Reformat Code as needed.) After you add these methods, the errors associated with extending classUIComponentELTagshould disappear.
public String getComponentType() {
return IMAGE_COMP_TYPE;
}
public String getRendererType() {
return IMAGE_RENDERER_TYPE;
}
6. Add thesetProperties()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 forUIComponent. 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).
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.
![]()
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.
![]()
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.
![]()
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.
![]()
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 classjavax.faces.component.UIOutput.
3. Add a declaration for fieldIMAGE_COMP_FAMILY, as follows.
private static final String IMAGE_COMP_FAMILY = "IMAGE_FAMILY";
4. Add methodgetFamily(), 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.)
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.
![]()
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 methodencodeBegin(), 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.
Step 6: Modify the faces-config.xml File
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).
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.
1. Add file image1.jpg to your project. Select File > Add Existing Item > Image File . . . NetBeans pops up Add Existing File dialog.Summary:
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.
![]()
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.
![]()
Figure 1-17: JSF Web Application running in a browser
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:
- imageIndex - holds the current index of the image
- imageUrls - an array of images
TheThe ImageTagHandler is modified:saveState()andrestoreState()methods of the component are implemented (to save the ImageComponent's state after a new page submit).
The url attribute is parsed to set the new imageUrls property of ImageComponent.The ImageRenderer is modified:
The ImageComponent's url property is set to the first url from the url attribute.
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:
Step 1: Add additional properties to ImageComponent.
- Add additional properties to ImageComponent.
- Add saveState() and restoreState() methods to ImageComponent.
- Modify ImageTagHandler to set these new properties.
- Modify ImageRenderer encodeBegin() method to include left and right links and decode the submitted form.
- Modify the web page to include additional images using the updated url attribute.
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.
![]()
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.
![]()
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.
![]()
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.
![]()
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 thesaveState()andrestoreState()methods. These methods have been inherited from class UIOutput. InsaveState()you save all the property values in an array of Objects. The first element holds the result ofsuper.saveState(). Note that you must convert imageIndex to Integer, since all saved values must beSerializable.
1. Copy and paste the following code for methodsaveState().
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 classjavax.faces.context.FacesContext.
3. Copy and paste the following code for methodrestoreState(). After retrieving the array of Objects (state), each property is set from the same element to which it was saved. Here is methodrestoreState().
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 (usingsplitandtrim) 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 thesetProperties()method of ImageTagHandler.java.
1. Bring up ImageTagHandler.java in the Java editor. Replace methodsetProperties()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.)
Step 4. Modify ImageRenderer to include left and right links and decode the submitted form.
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).
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.)
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<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>
onClickmouse 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(), andgetNextLinkName(). 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 classjavax.faces.component.UIForm.
3. Replace methodencodeBegin()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 methoddecode()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 classjava.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.
![]()
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.
Summary:
<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).
![]()
Figure 2-6: Image Slide Viewer custom JSF component
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 addedsaveState()andrestoreState()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
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.
- JavaScript that provides user interaction with the browser and mouse events
- XML used to pass the data between the server and the client
- An XMLHttpRequest object that provides the asynchronous communication
- The Document Object Model (DOM) for accessing and manipulating the structure of the page's HTML
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:
Use the<%@taglib prefix="jsfExt" uri="http://java.sun.com/jsf/extensions/dynafaces" %>
scriptscomponent<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 theDynaFaces.fireAjaxTransactionfunction (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.
Figure 3-1: Dynamic Faces and JSF ArchitectureSteps to follow:
Step 1: Add DynaFaces Libraries to the IDE.
- Add DynaFaces Libraries to the IDE.
- Modify the ImageRenderer to invoke the AJAX transaction.
- Modify the project's web.xml file.
- Modify the web page to include the DynaFaces taglib and scripts tag.
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.
![]()
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.
![]()
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.
![]()
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.
![]()
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