Articles
Java Platform, Enterprise Edition
|
| By Rick Palkovic and Mark Basler, October 2006 |
|
| |
This is the third in a short series of articles that adds Ajax functionality to a Java EE web application developed in the NetBeans IDE.
The Java EE platform includes JavaServer Faces technology. JavaServer Faces technology provides standard components that you can extend to create your own custom components, which you can then reuse in different applications. Along with the custom component, you also create a custom renderer and a custom tag to associate the component with the renderer and to reference the component from the page.
|
Learn About Ajax
For background information about Ajax and strategies for implementation, see Ajax Design Strategies by Ed Ort and Mark Basler. |
|
When you create the JavaServer Faces custom component in this approach, you package the resources required by the component directly with the application bundle. The custom component generates the JavaScript code needed to handle Ajax interactions with the server. To fulfill the Ajax request, you use the same Java Servlet that was used in the do-it-yourself method. This approach uses the JavaServer Faces framework only as a rendering mechanism, ignoring most of the power of JavaServer Faces technology. For convenience in this article, this component-servlet approach is CompA.
A second JavaServer Faces component approach, for convenience CompB, uses a phase listener to serve the component's static resources. As an option, you can use the phase listener to intercept and fulfill the client component's Ajax requests. The phase listener can delegate responsibility to a managed bean's method or again use the legacy servlet. 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, and is described in the final article in this Ajax series, Creating an Ajax-Enabled Application, a Phase Listener Approach.
The CompA approach can be appropriate in the following cases:
XMLHttpRequest call is used to perform polling action, for example.
The CompA approach also has its shortcomings, namely:
Weighing the advantages and shortcomings of the CompA approach, it could be a sensible way to introduce JavaServer Faces and Ajax technologies into your application.
| |
This section summarizes the life cycle of the book catalog page and the Ajax-created pop-up balloon. The terminology and file names in the explanations are explained in more detail later in this article. If they are unfamiliar, review this section again after you have read the entire article.
The following figure shows the life cycle of the book catalog page, beginning with the user's click on the catalog link.
|
The following steps explain the architecture illustrated in the figure:
Dispatcher servlet.
Dispatcher servlet accepts the
/books/bookcatalog URL, prefixes it with
/faces and suffixes it with
.jsp, mapping the URL to a
JSP page. The Dispatcher then forwards the request through the
RequestDispatcher.
web.xml file, sends the
JSP page identified by the URL to the
FacesServlet.
FacesServlet, the JavaServer Faces framework identifies the
CompA component by its tag and routes the page (along with the component it contains) to the
CompATag tag class.
CompATag class extracts properties from the tag's attributes and populates the properties of the component. It then maps the component to a renderer type that is registered in
faces-config.xml, and sends the request to the renderer for further processing.
The following figure shows the Ajax life cycle of the pop-up balloon, produced when the user mouses over a link on the book catalog page.
|
onmouseover event handler.
onmouseover event handler calls the
bpui.compA.showPopup() function in the
compA.js file. This function sends a request to the
CompAServlet through the
XMLHttpRequest object.
CompAServlet receives the request and, using the existing
BookDBA object, obtains the book title detail data and formats a response to the request.
CompAServlet then returns an XML response that holds the book detail.
ajaxReturnFunction() is called when the response is returned from the
CompAServlet. 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 first implementation of a JavaServer Faces component, you create a component with resources accessed directly from the web application. The component itself is bundled with the web application.
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 Toolkit 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_compA.jsp file already present in the project.
bookcatalog.jsp file.
bookcatalog_compA.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_compA.jsp file appears in the list.
bookcatalog_compA.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 the code is much simpler than either the do-it-yourself version from Creating an Ajax-Enabled Application, a Do-It-Yourself Approach or the toolkit version from Creating an Ajax-Enabled Application, a Toolkit Approach. The file is half the size of the do-it-yourself version.
Lines 32–39 in the file initialize the JavaServer Faces core tag library (taglib). They set the tag prefix to
f and the JavaServer Faces component tag prefix to
bpui for the custom taglib
<bpui:compA>.
Lines 36–38, shown below, provide the necessary view tags.
<!-- BEGIN: popup -->
|
The
<f:view> tag is used to encapsulate all the JavaServer Faces components so the FacesServlet (discussed later) operates on them in the page. Without the view tags, a JavaServer Faces context would be created but wouldn't have an object in the component tree to render.
The
<bpui:compA> tag has associated attributes
id and
url. The
id attribute is the pop-up balloon object name. The
url attribute locates the services that respond to the Ajax request.
Recall that in the do-it-yourself version described in Creating an Ajax-Enabled Application, a Do-It-Yourself Approach, you hard-coded the URL in a JavaScript file. To use that implementation in another application, the URL would have to be changed in many places. Because the component approach encapsulates code for easy reuse, the URL is declared as part of the component tag's attributes in the JSP file rather than in JavaScript code.
Moreover, because the do-it-yourself approach can't distinguish multiple components, the number of pop-up balloons on a page is limited to one at a time. A properly architected component approach lets you use any number of pop-up balloons simultaneously. For example, you could have two
compA components, each identified by a different
id that is specified in the component's
<bpui:compA> tag, each referencing a different URL. One component could use the URL of the pop-up servlet on your server, while another component could use a URL that fulfills the Ajax request in a different way.
Scroll down in the
bookcatalog.jsp file and view the event handlers on lines 76–77:
onmouseover="bpui.compA.showPopup('pop0', event, '${bookId}')"
|
These lines are similar to previous versions of the
onmouseover and
onmouseout event handlers. In this case, though, they pass the
id attribute of the pop-up object
pop0 to the
showPopup() and
hidePopup() functions. Recall that in line 37 the value for the
id attribute of the
<bpui:compA> tag was defined as
pop0. In addition to the pop-up
id attribute, the
showPopup() function is also passed the
event and
bookId attributes. The
bookId parameter is used to obtain information about a specific book, while the
event parameter denotes either an
onmouseover or
onmouseout event.
The
showPopup() and
hidePopup() functions are restricted to the
bpui.compA namespace to avoid naming conflicts. To use more than one pop-up component on the page, you would provide separate
id attributes for each component in a
bpui:compA tag, then use the
showPopup() and
hidePopup() functions to identify which to hide and which to show.
In summary, in this component version of the project, the only changes necessary in the
bookcatalog.jsp file are to declare the taglib, reference the component's tag, and provide a server-side component to fulfill the Ajax request. These changes provide the side benefit of allowing you to show more than one component on a page.
| |
Now, examine your project's tag library descriptor (
.tld) file. Open the file in the NetBeans Editor:
ui.tld file to open it in the NetBeans Editor.
In the
ui.tld file, you see that tags for
CompA lie between lines 14 and 67.
Recall line 35 from
bookcatalog.jsp:
|
For the application server, this line maps to the
URI used for the user interface in line 8 of the
ui.tld file:
|
In line 15 of the
ui.tld file, you see the definition of the
<name> tag:
|
The name defined in line 15 is used with the taglib namespace prefix
bpui in the
bookcatalog.jsp file, as, for example, in line 38 from that file:
|
The
bpui:compA tag maps to the tag class defined in line 16 of the
ui.tld file:
|
Line 17 describes the
compA tag as scriptless, which means there will be no scripting between the opening and closing tags:
|
Take special note of the
id attribute in the definition of
compA (line 19).
Recall how this attribute is used in the
onmouseover and
onmouseout event handlers in
bookcatalog.jsp. When passed as a parameter to the
showPopup() and
hidePopup() JavaScript functions, it references a unique pop-up component. The
id attribute is also used for other component-specific calls. By referencing components by id, the functions can allow more than one pop-up component to be used per page.
The
style and
styleClass attributes (lines 43 and 54) are defined to allow the component to override any styles that might be defined for the page elsewhere.
| |
The CompA component also has an important JavaServer Faces configuration file called
faces-config.xml, which is in the same directory as
ui.tld. Double-click
faces-config.xml in the NetBeans Projects window to view the file in the NetBeans Editor.
CompA uses a standard JavaServer Faces output component (
javax.faces.Output) with a custom renderer that is specified in lines 12–24, shown below. The
component-family and the
renderer-type are mapped to the return of the
CompATag methods
getComponentType() and
getRendererType(), respectively.
<!-- BEGIN configuration for CompA component -->
<!-- Handle requests for script file resource. Will serve script.js. -->
<render-kit>
|
Line 20 names the renderer:
|
In line 21, the renderer is referenced to the
CompARenderer class:
<renderer-class>
com.sun.javaee.blueprints.components.ui.example.CompARenderer
</renderer-class>
|
CompATag
Tag Class
| |
You now examine the CompATag class referenced by the
ui.tld file to see how the pop-up component's tag data is used.
CompATag.java file to open it in the NetBeans Editor.
|
The
CompATag class extracts attribute values from the tag, populates the component, and maps to a renderer type that is registered in
faces-config.xml.
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:
|
Then, through the
getRendererType() method (lines 45–47), the JavaServer Faces framework determines which renderer type to call:
public String getRendererType() {
|
The tag's
getComponentType() and
getRendererType() methods perform the important function of mapping the JavaServer Faces object to a specific renderer. Their return values are mapped to the values entered in the
faces-config.xml file to determine a specific renderer class to be used to render the component's markup.
CompARenderer
Class
To see how the
CompARenderer class executes the rendering, you now examine the
CompARenderer.java file.
CompARenderer.java file to open it in the NetBeans Editor.
In the
CompARenderer.java file, scroll to lines 34–35 of the
CompARenderer class definition:
private static final String COMPA_SCRIPT_RESOURCE="/compA.js"; private static final String COMPA_CSS_RESOURCE="/compA.css"; |
These lines show that the class uses the script resources of the
compA.js and
compA.css files. These resources enable the display and style of the HTML markup for the pop-up balloon and help process the information that the balloon displays.
|
About Components,
Renderers, and Tags See the discussion in the Java EE 5 Tutorial to learn more about how tags and renderers work with custom components. |
|
Scroll to lines 82–103. Here, you see the formatting and content that was hand-coded into the page in the do-it-yourself approach. Now, this information is part of a component and is inserted into the page automatically when the component is rendered.
Scroll down and view lines 146–157. Here, you see that the component also contains references to the JavaScript file (
COMPA_SCRIPT_RESOURCE, which resolves to
/compA.js) and the style sheet (
COMPA_CSS_RESOURCE, which resolves to
/compA.css).
These classes illustrate the advantage of the JavaServer Faces component approach: after you create the component, you (and other developers) can reuse it easily. All of the resource information is contained in the component. By accessing the
CompATag class through the FacesServlet, you eliminate additional programming.
| |
In summary, the
CompARenderer class renders the markup code that displays the component in the page. The JavaServer Page is routed through the FacesServlet. Because the page is within the scope of the
<f:view> tag, the JavaServer Faces framework recognizes that it must operate on the
bpui.CompA tag.
The FacesServlet is included in the libraries that are distributed with the GlassFish application server. The FacesServlet is registered in
web.xml, and is part of the Java EE framework. The JavaServer Faces framework identifies the
CompA component by its
bpui:compA tag and routes the request (along with the component it contains) to the tag class
CompATag. There, the
setProperties() method extracts properties from the tag's attributes and populates the properties of the component.
The renderer (
CompARenderer) outputs the component's markup and returns control to the the JavaServer Faces framework, so the next tag in the
JSP page can be interpreted.
compA.css Style Sheet
| |
The CSS file for the CompA implementation needs to change only slightly from the do-it-yourself or toolkit approaches. Open the file for viewing in the NetBeans IDE:
compA.css file. The file opens in the NetBeans Editor.
In the file, note that the class selector namespace had been changed from
.bpui_alone to
.bpui_compA. The namespace ensures a unique name for the styles, eliminating the possibility of inadvertent duplication. The reasoning is the same as that used in the
bookcatalog.jsp file, where the name
bpui.compA was used to create a separate namespace.
Extending this technique, expand the bookstore2 > Web Pages > images node and note that the images used for the corners of the pop-up balloons have also been given unique names. Best practice dictates that you always create a separate namespace when you design a component to help avoid clashes.
compA.js
File
| |
You now examine the
compA.js file. This file, in particular, illustrates the flexibility of the JavaServer Faces approach.
compA.js file. The file opens in the NetBeans Editor.
In the
compA.js file, note that the
bpui.compA.showPopup() function (line 9) occupies its own namespace. Otherwise, it is identical to the
bpui.alone.showPopup() function in the do-it-yourself version.
The
showPopupInternal() function (line 28) is markedly different, however. In line 32–35, this function retrieves the name of the pop-up object to be displayed and constructs the
url attribute by concatenating the
itemId value to the object's URI property:
|
In the do-it-yourself and toolkit versions, the pop-up balloon is identified by more explicitly coding its
url attribute:
|
As these lines reveal, in the do-it-yourself version a change in the name of the pop-up object or its URL requires a change in the JavaScript code. In contrast, the component approach requires you to make these changes only in the tag's attribute values.
In line 9, the identity of the pop-up object is passed into the
showPopup() function by the arguments
popupx and
itemId.
When setting the timeout period for the component (line 24) you identify the component by name and item identifier (
popupx and
itemId). These values identify the component that is operated upon.
bpui.compA.timeout=setTimeout(
"bpui.compA.showPopupInternal('" + popupx + "', '" + itemId + "')", 1000);
|
Consider line 32 of the
compA.js file, in the
showPopupInternal() function:
|
Here you see that the function is accessing the
bpui.compA[] associative array with the name of the component. The array accounts for situations in which more than one component is present in the page. For example, if the page contained two components named
popup0 and
popup1, the associative array would contain two objects.
Dispatcher
Class
| |
The
Dispatcher class finds known URL patterns, alters them if necessary, and forwards them for further processing.
To understand how the dispatcher does its job, first examine the
web.xml file:
web.xml file to open it in the NetBeans Editor.
web.xml is an XML file, the IDE tries to interpret it rather than display it. Click the XML button in the NetBeans Editor toolbar to show the file in XML view.
web.xml
File
Note lines 35–68 of the file, where URL patterns are mapped to servlets. In these lines, all of the application's pages are mapped to the Dispatcher servlet. Lines 45–48 are typical of this mapping.
In the do-it-yourself and toolkit versions, the pop-up balloon is identified by more explicitly coding its
url attribute:
<servlet-mapping>
|
This mapping is a remnant of the legacy coding for the project and presents a barrier to introducing JavaServer Faces technology into the project. If all URLs are routed through the Dispatcher, then JavaServer Faces processing must take place there along with other page processing. A mapping must be added to route JavaServer Faces components to the FacesServlet for processing.
Lines 25–33 set up mapping to the FacesServlet:
Consider line 32 of the
compA.js file, in the
showPopupInternal() function:
<servlet>
|
As a result of this mapping, any URL pattern that begins with
/faces/ is sent to the FacesServlet class.
Now, examine the Dispatcher to see how the
/faces/ prefix is concatenated to URL patterns.
Dispatcher.java
File
Instead of editing the existing
Dispatcher.java file, replace it with the
Dispatcher.java_compX file already present in the project.
dispatcher folder:
|
The
Dispatcher.java_compX file is used for both CompA and CompB, the the next example in this series.
Dispatcher.java file. Right-click and choose Delete from the contextual menu. In the confirmation pop-up window, click Yes.
Dispatcher.java_compX file, right-click, and choose Copy from the contextual menu.
com.sun.bookstore2.dispatcher node, right-click, and choose Paste from the contextual menu. A copy of the
Dispatcher.java_compX file appears in the list.
Dispatcher.java_compX file, right-click, and choose Rename from the contextual menu. Rename the file to
Dispatcher.java to make it part of the project build.
Dispatcher.java file to open it in the NetBeans Editor.
In the
Dispatcher.java file, note line 74, in the method
doGet():
|
This line is the sole addition to the original
Dispatcher.java file. It prefixes the
/faces prefix to the
/books/bookcatalog URL pattern. The
/faces prefix allows the servlet mapping in the
web.xml file to route the component to the FacesServlet class.
The code following line 62 shows that the prefix is concatenated only to
/books/bookcatalog URL patterns. The bookcatalog page is the only one in the project that contains JavaServer Faces components.
The
Dispatcher servlet
doGet() method interacts with lines 45–48 of the
web.xml file, noted
earlier. In those lines, any URL with the pattern
/books/bookcatalog is routed to the Dispatcher servlet. The Dispatcher servlet processes the URL pattern (line 74) by concatenating a
/faces prefix, changing the value of the
selectedScreen variable to
/faces/books/bookcatalog.
In line 105 of
Dispatcher.java, a JavaServer Pages
.jsp suffix is appended to the URL. The URL is then forwarded to the actual JSP page for processing. In this way, the
Dispatcher servlet lives up to its name: it finds known URL patterns, alters them if necessary, and forwards them.
When such a servlet appears in a legacy application, the URL patterns are typically hard-coded. To change a pattern in order to change information flow, the relevant URL pattern must be changed everywhere it appears. To add a page to the application, the new URL must be coded in several places. The advantage of a JavaServer Faces component approach is that these values are declared in the
faces-config.xml file and don't need to be hard-coded elsewhere.
| |
Now, examine the generated HTML markup for the project by building and deploying it.
Note that the code for this page is almost identical to that produced in the do-it-yourself version of the project. Instead of being hard-coded in JavaScript as it was in the do-it-yourself method, the pop-up rendering code is now generated by the renderer. The most obvious difference in the code is the use of the
compA namespace in the pop-up portion of the file. Another difference comes at the end of the pop-up section, just before the
<!-- END: popup --> comment:
<script type="text/javascript">
bpui.compA['pop0']=
new bpui.compA.createPopup('pop0', '/bookstore2/./PopupServlet?bookId=');
</script>
|
These lines initialize the pop-up balloon object. They create a new a new Popup object with the name of the object (
pop0) and the URL of the servlet to which it is being forwarded (
PopupServlet). These lines are generated by the CompARenderer servlet, in lines 116–122 of
CompARenderer.java.
Later in the HTML code, the
pop0 object name is referenced in the
onmouseover and
onmouseout event handlers for each book title. For example,
onmouseover="bpui.compA.showPopup('pop0', event, '202')"
|
It is instructive to consider the page's HTML code alongside the
compA.js code that helps generate it. View the
compA.js file in the IDE and consider the
showPopup() function beginning on line 9. Lines 11–20 obtain the x-y coordinates of the mouse when the
onmouseover event occurs. In lines 21–22, those coordinates are associated with the name of the object that pops up.
The object name and coordinates are then passed to the
showPopupInternal() function (lines 28–41). That function looks up the object by name (line 32) and operates on it.
The object is actually created by the
createPopup() function (beginning on line 44), more properly called a 'closure' because it contains an inner function,
ajaxReturnFunction(), with variables that can be accessed outside of the
createPopup() function.
| |
Although the CompA approach requires some additional effort to code the JavaServer Faces component, it has several advantages:
bookcatalog.jsp file and return to your original application, affecting only one page.
The CompA approach described here has a disadvantage, namely that all of the JavaScript files, images, and Java classes must be bundled with the application. You address this issue in the next article in the series.
| |
In the
next article iin this series you take better advantage of JavaServer Faces technology by using a phase listener approach to implement the pop-up balloons. In that approach, you can bundle all of the component's resources into a reusable
.jar file.
| |