The Java Enterprise Edition 5 platform includes JavaServer Faces (JSF)
technology,
which provides a mature and extensible user interface
component model. The design of this model makes it easy for application
developers to create custom components by extending the standard
components included with JSF and to reuse these
components across applications. This entry discusses strategies
that add AJAX support to JSF
components by introducing a servlet to process the AJAX
requests. For more information
about the different ways to access static & dynamic resources, see "Accessing Static and Dynamic Resources".
For alternative and related solutions see "Using JSF with AJAX".
Developers who want to include AJAX support as part of their JSF
components have
more than one strategy to choose from. One way to approach this
problem is to use a servlet to intercept and fulfill the custom
components AJAX requests. The resources required by the custom
JSF component will be packaged with the application bundle and accessed
directly.
We will discuss the strategy of using a Servlet for
incorporating AJAX
support
into a JSF application. This involves creating
a custom component to generate the necessary Javascript to
execute the AJAX interactions on the client-side, with another
server-side component to serve the AJAX request in the form of a Java
Servlet.
This approach can be used in the following cases:
This approach also has its problems, which are as follows::
For these reasons our current recommend approach is to use a
third party library which is described in detail in the "Access Static and
Dynamic Resource" entry. We are still researching evolving
technologies and will update these entries when our recommendations
change.
For a simple example of this component's functionality see the standalone.jsp that contains
all the scripting and functionality in a single JSP page.
/bp-jsf-example/CompAServlet?itemId=test1A2) The Servlet receives the request, retrieves the itemId parameter of the request and create/sends the appropriate response.
Since our component is a Popup that shows detailed information, we need to render some markup that actually constructs the division that is displayed. There are multiple ways of doing this, one is to hard code the markup in the renderer, setting the values that are necessary to maintain the name space conventions. Here, the component's ID is pre-pended to the to keep multiple instances of the component from clashing. The component developer can also code the template directly in the renderer by using the ResponseWriter's startElement and endElement methods. If your component was going to be tooled (for visual use in an IDE) this may be the best way to go, because each startElement call is associated with a component. This enables the tool to keep better track of the markup that belongs to the individual components.Map requestMap=context.getExternalContext().getRequestMap();
Boolean scriptRendered=(Boolean)requestMap.get(RENDERED_SCRIPT_KEY);
// Check to see if resource already rendered
if (scriptRendered != null && scriptRendered.equals(Boolean.TRUE)) {
return;
}
// put flag in requestMap to indicate already rendered
requestMap.put(RENDERED_SCRIPT_KEY, Boolean.TRUE);
String contextRoot=context.getExternalContext().getRequestContextPath();
// Render markup tag for the javascript file
writer.startElement("script", component);
writer.writeAttribute("type", "text/javascript", null);
writer.writeAttribute("src", contextRoot + "/compA.js", null);
writer.endElement("script");
writer.write("\n");
writer.write("<div class=\"bpui_compA_popTop\">");
writer.write(" <div class=\"bpui_compA_cornerTL\"><div class=\"bpui_compA_cornerTR\">");
writer.write(" <center><font color=\"white\"><b><span id=\"" + clientId + "_title\">title</span></b></font></center>");
writer.write(" </div></div>");
writer.write("</div>");
writer.write("<div class=\"bpui_compA_popMid\">");
writer.write(" <table border=\"0\" style=\"width: 100%\">");
writer.write(" <tr>");
writer.write(" <td>");
writer.write(" <table border=\"0\" bgcolor=\"#ffffff\" cellpadding=\"5\" cellspacing=\"5\">");
writer.write(" <tr>");
writer.write(" <td><span id=\"" + clientId + "_message\">Value</span></td>");
writer.write(" </tr>");
writer.write(" </table>");
writer.write(" </td>");
writer.write(" </tr>");
writer.write(" </table>");
writer.write("</div>");
writer.write("<div class=\"bpui_compA_popBot\">");
writer.write(" <div class=\"bpui_compA_cornerBL\"><div class=\"bpui_compA_cornerBR\">");
writer.write(" </div></div>");
writer.write("</div>");
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/xml;charset=UTF-8");
PrintWriter out = response.getWriter();
String itemId=request.getParameter("itemId");
response.setHeader("Pragma", "No-Cache");
response.setHeader("Cache-Control", "no-cache,no-store,max-age=0");
response.setDateHeader("Expires", 1);
if(itemId != null) {
out.println("<response>");// custom response code goes here
...
out.println("</response>");
} else {
out.println("<response>");
out.println("<title><![CDATA[REQUEST ERROR]]></title>");
out.println("<message><![CDATA[The query parameter 'itemId' required]]></message>");
out.println("</response>");
}
out.flush();
out.close();
}
<!-- register the custom renderer for the standard component family -->
<render-kit>
<renderer>
<component-family>javax.faces.Output</component-family>
<renderer-type>CompA</renderer-type>
<renderer-class>com.sun.javaee.blueprints.components.ui.example.CompARenderer</renderer-class>
</renderer>
</render-kit>
<ui:compA id="pop1" url="CompAServlet?itemId="/>
<a href="#" onmouseover="bpui.compA.showPopup('pop1', event, 'test1A')"
onmouseout="bpui.compA.hidePopup('pop1')" style="cursor: pointer"><b>Mouse over link to see popup (test1A)</b></a><br/>
<small><i>(With a Servlet fulfilling AJAX Request)</i></small><br/><br/>>
<div id="pop1" class="bpui_compA_popup">
<div class="bpui_compA_popTop">
<div class="bpui_compA_cornerTL"><div class="bpui_compA_cornerTR">
<center>
<font color="white">
<b><span id="pop1_title">title</span></b></font>
</center>
</div></div>
</div>
<div class="bpui_compA_popMid">
<table border="0" style="width: 100%">
<tr>
<td>
<table border="0" bgcolor="#ffffff" cellpadding="5" cellspacing="5">
<tr>
<td><span id="pop1_message">Value</span></td>
</tr>
</table>
</td>
</tr>
</table>
</div>
<div class="bpui_compA_popBot">
<div class="bpui_compA_cornerBL"><div class="bpui_compA_cornerBR">
</div></div>
</div>
</div>
<script type="text/javascript">
bpui.compA['pop1']=new bpui.compA.createPopup('pop1', '/bp-jsf-example/CompAServlet?itemId=');
</script>
Note the Javascript code that creates a new bpui.compA.createPopup
object and stores the result in the bpui.compA name space under the
"pop1" property. This object is a Javascript closure and is
retrieved when the popup in trigger through the bpui.compA.showPopup
function described below.
// create a closure to maintain the component's id and AJAX url with callback function
bpui.compA.createPopup=function(componentId, urlx) {
this.componentId=componentId;
this.urlx=urlx;
this.ajaxReturnFunction=function() {
// make sure response is ready
if (bpui.compA.req.readyState == 4) {
// make sure it is a valid response
if (bpui.compA.req.status == 200) {
// populate the popup with the info from the response
var resultx=bpui.compA.req.responseXML.getElementsByTagName("response")[0];
document.getElementById(componentId + "_title").innerHTML=resultx.getElementsByTagName("title")[0].childNodes[0].nodeValue;
document.getElementById(componentId + "_message").innerHTML=resultx.getElementsByTagName("message")[0].childNodes[0].nodeValue;;
// show popup with the newly populated information
document.getElementById(componentId).style.visibility='visible';
} else if (bpui.compA.req.status == 204){
// error, just show alert
alert("204 returned from AJAX call");
}
}
}
}
bpui.compA.showPopup=function(popupx, eventx, itemId) {
// Position popup base on event and set timeout so popup isn't flashing all the time
var xx=0;
var yy=0;
if (!eventx) var eventx=window.event;
if (eventx.pageX || eventx.pageY){
xx=eventx.pageX;
yy=eventx.pageY;
} else if (eventx.clientX || eventx.clientY) {
xx=eventx.clientX + document.body.scrollLeft;
yy=eventx.clientY + document.body.scrollTop;
}
document.getElementById(popupx).style.left= (xx + 3) + "px";
document.getElementById(popupx).style.top=yy + "px";
// make sure the popup doesn't show all the time, need to mouseover for at least 1 second.
bpui.compA.timeout=setTimeout("bpui.compA.showPopupInternal('" + popupx + "', '" + itemId + "')", 1000);
}
bpui.compA.showPopupInternal=function(popupx, itemId) {
// initialize the AJAX request
bpui.compA.req=bpui.compA.initRequest();
// retrieve the correct popup object that is being shown
popObject=bpui.compA[popupx];
// concatenate the itemId value to the URI
url=popObject.urlx + escape(itemId);
// set the correct popup's callback function
bpui.compA.req.onreadystatechange = popObject.ajaxReturnFunction;
bpui.compA.req.open("GET", url, true);
// send the request
bpui.compA.req.send(null);
}