Articles
Java Platform, Enterprise Edition
|
| By Rick Palkovic and Mark Basler, November 2006 |
|
| |
This is the fourth in a short series of articles that add Ajax functionality to a Java EE web application developed in the NetBeans IDE.
As discussed in the previous article in this series, the JavaServer Faces technology included in the Java EE platform enables you to create your own custom components, which you can then reuse in different applications. The approach in the previous article required the custom component and its resources to be bundled and distributed with the web application.
This article describes a phase listener approach to building the custom component. In this approach, the component's resources are packaged with the component in a
.jar file that is bundled with the web application. The resources are served through a phase listener on the server. To distinguish this approach from the previous one in the example project, it is called
CompB.
|
Learn About Ajax
For background information about Ajax and strategies for implementation, see Ajax Design Strategies by Ed Ort and Mark Basler. |
|
As in the previous custom component approach, the CompB custom component generates the JavaScript code needed to handle Ajax interactions with the server. Again, you use the same Java Servlet that was used in the do-it-yourself method.
The client component's resources, such as the JavaScript file and CSS, are accessed through the phase listener. The phase listener approach takes advantage of more of the power of JavaServer Faces technology than did the component approach described in the previous article in this series.
The phase listener can delegate responsibility to a managed bean's method or use a legacy servlet. In this example, you use the legacy servlet. Using the legacy servlet has two major advantages:
The disadvantage of using a legacy servlet is that it does not take advantage of the inherently more capable and robust managed bean approach. For information on how to use a managed bean for server-side processing, see the discussion in Using PhaseListener Approach for Java Server Faces Technology with AJAX and Accessing Resources From JavaServer Faces Custom Components
The CompB (phase listener) approach can be appropriate in the following cases:
The CompB approach also has its shortcomings, namely:
.jar files that are registered in all the
faces-config.xml files bundled with your application are fired sequentially, with no guarantee to the order.
| |
Figure 1 shows how resources for the component are accessed from the web container. The cycle begins when the user navigates to the book catalog page.
Figure 1: CompB Resource Information Flow
|
The steps in Figure 1 are as follows:
<script type="text/javascript"
src="/bookstore2/faces/jsf-example/compB/compB.js"></script>
<link type="text/css" rel="stylesheet"
href="/bookstore2/faces/jsf-example/compB/compB.css" />
|
CompB.jar archive.
CompB.jar archive, and returns them to the phase listener.
Figure 2 shows how data for a specific pop-up balloon is obtained through the Ajax request.
Figure 2: Ajax Call Lifecycle
|
onmouseover event handler.
onmouseover event handler calls the
bpui.compB.showPopup() function in the
compB.js file. This function sends a request to the
PopupServlet through the
XMLHttpRequest object.
PopupServlet receives the request and, using the existing
BookDBA object, obtains the book title detail data and formats a response to the request.
PopupServlet then returns an XML response that holds the book detail.
ajaxReturnFunction() is called when the response is returned from the
PopupServlet. The
ajaxReturnFunction() then extracts book detail data from the XML message, populates the pop-up balloon's table, and makes the balloon visible to the user.
| |
This article assumes that you have downloaded and installed the latest NetBeans IDE and the example application that forms the basis of discussion. If you have not done so, refer to the first article in the series, Creating an Ajax-Enabled Application, a Do-It-Yourself Approach, and download the necessary tools now.
Note that the example project already contains files for all four implementation approaches in addition to the original application:
| |
In this, your second implementation of a JavaServer Faces component, you create a component with resources accessed by a servlet. When creating your custom component, you edit the tag library definition file
ui.tld, the JavaServer Faces configuration file
faces-config.xml, the tag handler
CompBTag.java, and the renderer
CompBRenderer.java. You also create a phase listener,
CompBPhaseListener.java. The phase listener is the major difference between the CompA and CompB approaches.
The component CompB has been precompiled into a
.jar file in the project. In the Project window, you can find the file located in the project's bookstore2 > Web Pages > WEB-INF > lib folder. As you examine the source files for CompB, you will open the read-only files in this
.jar file.
The source files used to build
CompB.jar can be seen in the Files window, under the bookstore2 > compB node. The
readme.html file in the
compB folder describes how to compile the source files. In the procedures in this article, you do not edit the source files. Use the files to experiment and as the basis of your own JavaServer Faces components.
To see how the component is implemented, begin by opening the
bookcatalog.jsp file in the NetBeans IDE . If the bookstore2 project is already open in the NetBeans IDE from your exercise in the last article,
Creating an Ajax-Enabled Application, a Component Approach, skip these steps and go directly to the next section.
To open the project:
/examples/web/bookstore2, where
project is the path to your project directory.
/glassfish/lib directory, where
server is the root of your GlassFish server installation.
javaee.jar file and open it. Click Close to resolve the reference and close the window.
bookcatalog.jsp
File
Instead of editing the existing
bookcatalog.jsp file, replace it with the
bookcatalog_compB.jsp file already present in the project.
bookcatalog.jsp file.
bookcatalog_compB.jsp file, right-click, and choose Copy from the contextual menu.
books node, right-click, and choose Paste from the contextual menu. A copy of the
bookcatalog_compB.jsp file appears in the list.
bookcatalog_compB.jsp file, right-click, and choose Rename from the contextual menu. Rename the file to
bookcatalog.jsp to make it part of the project build
bookcatalog.jsp file to open it in the NetBeans Editor.
bookcatalog.jsp
File
To follow the discussion of the
bookcatalog.jsp file, make sure line numbers are displayed in the NetBeans Editor. To display line numbers, right-click in the left margin of the Editor window and choose Show Line Numbers from the contextual menu.
On your first view of the file, note that it is very similar to the version used for Creating an Ajax-Enabled Application, a Component Approach.
The changes in the file appear on lines 33–37, where the
bpui:compB component is identified, along with the servlet that responds to the component's Ajax call:
<%@taglib prefix="bpui" uri="http://java.sun.com/blueprints/ui/compB" %> <f:view> <bpui:compB id="pop0" url="./PopupServlet?bookId="/> </f:view> |
The
onmouseover and
onmouseout event handlers (lines 78–79) also have changed to reference the CompB versions of the
showPopup() and
hidePopup() functions.
In other ways, the
bookcatalog.jsp page is the same as the CompA version.
| |
Now, examine your project's tag library descriptor (
.tld) file. In the CompB approach, the
ui.tld file is contained in the
CompB.jar file. Open the file in the NetBeans Editor:
ui.tld file to open it as a read-only file in the NetBeans Editor.
In the
ui.tld file, you see that tags for
CompB lie between lines 13 and 67. The
id,
url,
style, and
styleClass attributes declared in the file are used the same way as the same attributes declared in the
ui.tld file for CompA. One obvious difference is the definition of the
<name> tag (line 15):
<name>compB</name> |
The name is used with the taglib namespace prefix
bpui in the
bookcatalog.jsp file.
| |
In the CompB approach, you must put the component's resources in a location that the phase listener can find. When you place the
CompB.jar file in the project's Libraries folder, it is deployed to the
.war file's
WEB-INF/lib directory. This directory is automatically searched by the phase listener when it looks for package resources. This feature enables others to easily use the components you create.
The phase listener itself is registered by means of the
faces-config.xml file.
To view the
faces-config.xml file:
faces-config.xml to open the file in the NetBeans Editor.
In the
faces-config.xml file, consider lines 12–30, which configure the CompB component.
Lines 15–22 register the name and location of the renderer for the component.
Lines 26–28 register the name and location of the phase listener for the component. The phase listener handles all of the component's requests for resources and methods.
In summary, the file registers the renderer
CompBRenderer and the lifecycle component
CompBPhaseListener. You now examine the major difference between the CompA and CompB approaches: the phase listener.
| |
The JavaServer Faces framework invokes the phase listener every time a request passes through the FacesServlet. The sole responsibility of the phase listener in your project is to serve the resources needed by the component.
The
.java source files have been included in the
compB.jar file. Such inclusion is not typical, but in discussing this example it is convenient to have the source files close to their corresponding classes.
To view
CompBPhaseListener.java:
CompBPhaseListener.java file to open it in the NetBeans Editor.
Figure 3: Opening
CompBPhaseListener.java
|
Note the
afterPhase() method, beginning on line 55. This method is called by the JavaServer Faces framework at each phase of the lifecycle that is returned by the
getPhaseId() method. In this example, the
afterPhase() method is called during the
PhaseId.RESTORE_VIEW lifecycle phase. In the event that the phase listener is called for on all phases, then the
getPhaseId() method returns
PhaseId.ANY_PHASE, and
afterPhase() is called after every request through the FacesServlet.
In line 60, the
afterPhase() method obtains the application key:
int iPos=rootId.indexOf(APP_KEY); |
The value of
APP_KEY is set to
/jsf-example/ in line 45. The characters
jsf-example are part of the URL resource path set in
CompBRenderer. This value sets the path to the resources for the component and limits the requests for which the phase listener operates, thus reducing possible side effects to other requests being made through the FacesServlet.
For example, when the
afterPhase() method is passed an event, it notes that the value of
APP_KEY for the event is
/jsf-example/. The method then extracts the root ID (
rootId) of the event. The root ID is the actual URL of the resource that is located in the
compB.jar, as constructed by the renderer
CompBRenderer.
In
CompBPhaseListener.java, the URL for the JavaScript resource that is constructed in the
afterPhase() method in line 65 is:
PATH_PREFIX + rootId |
In the project build, the URL resolves to:
http://localhost:8080/faces/jsf-example/compB/compB.js |
The
if-else statements in lines 64–73 of the
afterPhase() method guarantee that JavaScript,
CSS, and image content types are handled properly and can be found by resource consumers. These lines test to see that the application key exists and that the resource URL is one of the types declared in lines 40–44. If the suffix is recognized as a resource,
afterPhase() constructs a relative URL for the resource and passes it to the
handleResourceRequest() method.
The
handleResourceRequest() method (lines 100–127) is straightforward. Line 103 finds the fully qualified URL given the relative URL for the resource:
URL sxURL = CompBPhaseListener.class.getResource(resource); |
The
getResource() method allows the servlet container to make a resource available to servlets from any source. Resources can be located on a local or remote file system, in a database, or in a
.war file. For example, the relative URL of the CompB component's JavaScript file is
/jsf-example/compB/compB.js. The
getResource() method finds the resource in a local class path in the project's
.war file, in the path
web/WEB-INF/lib/compB.jar/META-INF/jsf-example/compB/compB.js.
If the resource cannot be found, the
getResource() method returns null, and the
try-catch block in lines 110–119 prints an error message when the
readWriteBinaryUtil() method throws an exception. That method simply reads in the resources.
Note that you, as the component developer, are responsible for creating resources and placing them in the proper locations. After the component has been successfully packaged, it can be used without regard to such details.
So, the
handleResoureRequest() method looks up the resource, composes its full URL, and returns the URL along with the resource content type. Line 112 sets the response status to 200, which indicates that the resource was found successfully. It then opens a stream and puts back a binary stream.
The method that reads and writes the binary stream is
readWriteBinaryUtil(), shown in lines 142–169. The reason to use a stream is because not all resources are character data. The binary stream allows image resource data to be transferred.
As an aside, note that the
images folder that holds the pop-up balloon's corner images are located in the same directory in your project as the
compB.css style sheet. The style sheet finds the images with a relative path name, not a fully qualified one. Thus you will see references to the images in
compB.css like the following (taken from line 3 of the style sheet):
./images/compB_corner_tl.gif |
You now examine the way actions are mapped in the phase listener.
JavaServer Faces 1.2 introduced a
unified expression language. In line 82, the
afterPhase() method calls the expression factory:
MethodExpression mex=context.getApplication().getExpressionFactory().
createMethodExpression(context.getELContext(),
methodx, null, argTypes);
|
This line creates an expression out of the method for use with a managed bean. In your project you use a legacy servlet rather than a managed bean. For information on how to use a managed bean for server-side processing, see the discussion in Using PhaseListener Approach for Java Server Faces Technology with Ajax.
CompBTag
Tag Class
| |
You now examine the CompBTag class referenced by the
ui.tld file to see how the pop-up component's tag data is used.
To view
CompBTag.java:
CompBTag.java file to open it in the NetBeans Editor.
The
CompBTag class extracts attribute values from the tag, populates the component, and maps to a renderer type that is registered in
faces-config.xml. Its function is identical to the
CompATag class in the CompA approach.
The
setProperties() method, beginning on line 76, extracts the attribute values for
style,
styleClass, and
url. Line 79 of the method extracts the inputs from the tag and puts them into the component itself:
UIOutput outComp = (UIOutput)component; |
Then, through the
getRendererType() method (lines 45–47), the JavaServer Faces framework determines which renderer type to call:
public String getRendererType() {
|
The
getRendererType() method maps the
CompB tag to a renderer type. That renderer type is determined by the
CompBRenderer class.
CompBRenderer
Class
| |
To see how the
CompBRenderer class executes the rendering, you now examine the
CompBRenderer.java file.
CompBRenderer.java file to open it in the NetBeans Editor.
In the
CompBRenderer.java file, scroll to lines 45–47 of the
CompBRenderer class definition:
private static final String COMPB_SCRIPT_RESOURCE=
"/jsf-example/compB/compB.js";
private static final String COMPB_CSS_RESOURCE=
"/jsf-example/compB/compB.css";
private static final String COMPB_TEMPLATE_RESOURCE=
"/META-INF/jsf-example/compB/compB_template.txt";
|
Lines 45–46 show that the class uses the script resources of the
compB.js and
compB.css files. These resources display the HTML markup for the pop-up balloon and provide the information that the balloon displays.
Line 47 shows that the class uses a resource template. Much of the code that was in the
CompARenderer.java file in the previous article is drawn from a template in this example. A resource template is the preferred approach because the component does not need to be recompiled whenever you change the template HTML code. To view the template contents:
compB_template.txt.
Note that the CompA approach could also have made use of a resource template.
Other than renaming instances of CompA to CompB (for example, changing the namespace from
bpui.compA to
bpui.compB), not much is different between the CompA and CompB versions of the renderer.
compB.css
Style Sheet
| |
The CSS file for the CompB implementation differs only slightly from CompA approach. To view the style sheet contents in the NetBeans Editor:
compB.css file.
In the file, note that the class selector namespace has changed to
.bpui_compB. The namespace ensures a unique name for the styles, eliminating the possibility of inadvertent duplication. The name changes are the only difference between the CompB and CompA versions of the style sheet.
Note also that the image file names under the bookstore2 > Web Pages > WEB-INF > lib > compB.jar > META-INF > jsf-example > compB > images node have been changed to begin with
compB_ in order to make them unique.
compB.js
File
| |
The
compB.js file is almost the same as the
compA.js file. Again, the namespace has changed from
bpui.compA to
bpui.compB. Functionally, the script does exactly the same thing as its CompA counterpart.
Note that, because of the separate namespaces, it would be possible to use both components (CompA and CompB) in the same page.
To view the
compB.js file in the NetBeans Editor:
compB.js file.
Dispatcher
Class
| |
The
Dispatcher class finds known URL patterns, alters them if necessary, and forwards them for further processing. The dispatchers for the CompA and CompB approaches are identical. See the
discussion in the previous article for details.
| |
Now, examine the generated HTML markup for the project by building and deploying it.
The HTML code for the deployed page is very similar to that produced in the CompA approach. The only significant differences are the CompB naming conventions and the new URLs of the component resources. For example, in CompB, the JavaScript resource file is:
<script type="text/javascript"
src="/bookstore2/faces/jsf-example/compB/compB.js"></script>
|
Whereas, in the CompA version it is:
<script type="text/javascript"
src="/bookstore2/compA.js"></script>
|
| |
This series of four articles shows a progression for implementing Ajax functionality in a legacy application:
You can build and deploy the project in the NetBeans IDE using any of these approaches by using the appropriate
bookcatalog.jsp file during the build.
The CompB approach, using a JavaServer Faces phase listener, takes greater advantage of the JavaServer Faces framework than did the CompA approach discussed in the previous article. In the CompB approach, the JavaServer Faces component is compiled into a
.jar file that is placed in the application's
Web Pages/WEB-INF/lib project directory. The component sends Ajax requests to the server and handles the replies. The component can be reused by placing it in the
WEB-INF/lib directory of other projects.
On the server side, Ajax calls are handled by the phase listener, which serves resources for the component. To fulfill the Ajax request on the server side, the phase listener requests data from the legacy servlet that was used with the original application. When building an application from scratch, you would more likely use a managed bean instead of a servlet.
In some circumstances, the use of a JavaServer Faces component with a phase listener degrades performance because the phase listener is called on every request through the FacesServlet. In the example that has been the subject of these articles, the performance penalty is minimal because Ajax requests occur infrequently.
Other advantages of the CompB approach are the same as those of CompA, namely:
bookcatalog.jsp file and return to your original application, affecting only one page.
| |
Check the Hands-On Java EE page for a follow-on article that describes state management with JavaServer Faces technology. Managing application state exploits the full capabilities of JavaServer Faces technology.
| |