|
By Rick Palkovic and Mark Basler, November 2006
|
|
|
Articles Index
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:
-
It avoids the need to rewrite server-side functionality.
-
It limits the risk of adopting new technology by making it
easy to return to the original architecture.
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:
- When your legacy application is not based on JavaServer Faces
technology, as is the case with the example application discussed here.
In these cases, the data that may be affected by the Ajax call is
managed by a legacy mechanism. Adding Ajax functionality tied to a
custom component can reduce the risk to an existing production
application by leaving critical data-handling server software in
place.
- When the Ajax call doesn't affect the JavaServer Faces view state
that is associated with the web page.
- When you're not willing to pay the performance price of saving,
transferring and restoring component view state. For example, when an
Ajax call is triggered often to perform polling action.
- When the Ajax call retrieves read-only data.
The CompB approach also has its shortcomings, namely:
- It can't handle Ajax calls that modify the component
view state. Problems arise if the Ajax call modifies
state that is also maintained in the serialized view state that is
reconstituted when a JavaServer Faces form or action is submitted.
- If a library's components are created by different developers, a
different phase listener might be required for each component's
specific needs. Multiple phase listeners could cause requests to be
fulfilled multiple times, corrupting the data being served.
- Phase listeners in the
.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.
- The very existence of multiple phase listeners creates a
performance burden because all that are registered execute on every
request.
Contents
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:
-
The user navigates to the book catalog page, either
by clicking from the bookstore home page or entering the
catalog page's URL directly.
-
The web server serves the page. When the
page is rendered, the HTML markup provides URLs to access the
resources required by the page, including the JavaScript and
CSS files required by the component.
-
As the page is loading, the browser accesses the
resources using the URLs that were rendered with the page.
For example, the component's JavaScript file and CSS file
are referenced by the following lines in the HTML markup:
<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" />
|
-
The resource URLs are received by the FacesServlet. The
FacesServlet forwards the links to the phase listener, which
is packaged with the component in the
CompB.jar archive.
-
The phase listener identifies the location of the
requested resources, fetches them from the
CompB.jar
archive, and returns them to the phase listener.
-
The phase listener returns the resource information
through the FacesServlet to the client browser.
Figure 2 shows how data for a specific pop-up balloon is
obtained through the Ajax request.
Figure 2: Ajax Call Lifecycle |
-
The user mouses over a link on the book catalog page, executing the
onmouseover event handler.
-
The
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.
-
The
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.
-
The component-specific
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.
Getting Started
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:
- The base application, with no Ajax implementation
- The do-it-yourself approach to Ajax
- A toolkit approach to Ajax, using the Dojo toolkit
- The CompA approach, which bundles component resources with the web application
- The CompB phase listener approach that is the topic of this article
Implementing a Component Approach
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.
About CompB
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.
Opening the Bookstore Project in the NetBeans IDE
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:
- Start the NetBeans IDE.
- From the NetBeans toolbar, choose File > Open. The Open Project
window opens.
- Navigate to project
/examples/web/bookstore2,
where project is the path to your project directory.
- Select the bookstore2 project folder and click Open Project
Folder. The IDE opens the project folder and selects bookstore2 as the
main project. The bookstore project is also opened because bookstore2
depends on many of the files in bookstore.
- If the IDE flags the bookstore project and alerts you that
references need to be resolved:
- Right-click the project and choose Resolve Reference
Problems from the contextual menu.
- In the resulting window, click Resolve and navigate
to the server
/glassfish/lib directory, where
server is the root of your GlassFish server installation.
- Select the
javaee.jar file and open it.
Click Close to resolve the reference and close the window.
- If the bookstore2 project also produces an alert:
- Right-click the project name and choose Resolve
Missing Server Problem from the contextual menu.
- In the resulting window, select the GlassFish server
that you registered in
Creating an Ajax-Enabled Application, a Do-It-Yourself
Approach. Click OK to resolve the reference.
Replacing the bookcatalog.jsp File
Instead of editing the existing bookcatalog.jsp file, replace
it with the bookcatalog_compB.jsp file already present in the
project.
- In the NetBeans IDE, click the
Projects tab to open the Projects
view of your project.
- Expand the
bookstore2 > Web Pages > books
node and select the
bookcatalog.jsp file.
- Right-click and choose Delete from the contextual menu. In the
confirmation pop-up window, click Yes.
- Select the
bookcatalog_compB.jsp file, right-click,
and choose Copy from the contextual menu.
- Select the
books node, right-click, and choose Paste
from the contextual menu. A copy of the
bookcatalog_compB.jsp file appears in the list.
- Select the copy of the
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
- Double-click the
bookcatalog.jsp file to open it in
the NetBeans Editor.
Examining the 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.
Tag Library
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:
- In the Projects window of the NetBeans IDE, expand the bookstore2
> Libraries > CompB.jar > META-INF node.
- Double click the
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):
The name is used with the taglib namespace prefix
bpui in the bookcatalog.jsp file.
The JavaServer Faces Configuration 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:
-
View the project hierarchy in the Projects window.
-
Expand the bookstore2 > Libraries > compB.jar > META-INF node.
-
Double-click
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 CompBPhaseListener.java File
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:
- View your project's Projects window.
-
Expand the bookstore2 > Web Pages > WEB-INF >
lib > compB.jar > com
sun > javaee > blueprints > components > ui > example node.
-
Double-click the
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:
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.
The 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:
- View your project's Projects window.
-
Expand the bookstore2 > Web Pages > WEB-INF >
lib > compB.jar > com
sun > javaee > blueprints > components > ui > example node.
-
Double-click the
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() { return ("CompB"); }
|
The getRendererType() method maps the CompB tag to a
renderer type. That renderer type is determined by the CompBRenderer class.
The CompBRenderer Class
To see how the CompBRenderer class executes the rendering,
you now examine the CompBRenderer.java file.
- View your project's Projects window.
-
Expand the bookstore2 > Web Pages > WEB-INF >
lib > compB.jar > com
sun > javaee > blueprints > components > ui > example node.
-
Double-click the
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:
-
From your project's the Project window, expand the
bookstore2 > Web Pages > WEB-INF > lib > compB.jar >
META-INF > jsf-example > compB node.
-
Double-click
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.
The 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:
- In the Projects view of your project, expand the bookstore2 >
Web Pages > WEB-INF > lib > compB.jar > META-INF >
jsf-example > compB node.
-
Double-click the
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.
The 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:
- In the Project view of your project, expand the bookstore2 >
Web Pages > WEB-INF > lib > compB.jar > META-INF >
jsf-example > compB node.
-
Double-click the
compB.js file.
The 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.
Building and Deploying the Application
Now, examine the generated HTML markup for the project by building and
deploying it.
- Build and deploy the application by choosing Run > Run Main
Project from the NetBeans menu bar, or clicking the Run button from the
toolbar.
- The application opens in your client browser. In the deployed
application, click the Start Shopping link to navigate to the book
catalog page.
- In your browser, view the source HTML for the page (typically,
choose View > Page Source or similar menu item).
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>
|
Summary
This series of four articles shows a progression for implementing Ajax
functionality in a legacy application:
-
Directly coding the
web application for Ajax capability.
-
Taking advantage of the Dojo toolkit
libraries.
-
Using a JavaServer Faces component whose resources are bundled
with the web application (CompA).
-
Using a JavaServer Faces component with a phase listener on the server
side to provide static resources for the component (CompB).
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 Phase Listener Approach
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:
- Encapsulation of functionality in the component, which aids in
troubleshooting and limits the risk of adopting new technology. In
the bookstore application, you can remove one line from
the
bookcatalog.jsp file and return to your original
application, affecting only one page.
- Ability to use multiple components on a page.
- Reusability of the component in other pages and applications.
Next Steps
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.
References
Developer Services and Training
|