Bundling Ajax into JSF components Blog

Version 2


    Any Java developer who has already worked with Java Server Faces has undoubtedly come across the need for a custom component. Most components in JSF implementations are rudimentary and don't really address real-world situations--if data-entry solutions for all problem domains were as simple as a login form, then JSF would be just the right tool out of the box. The saving grace of the framework is the ability to create and extend custom components, and if you couple that with the power and versatility of Ajax, you really start to reap the benefits of the modern Model-View-Controller architecture.

    Why JSF and Ajax?

    Ajax and JSF would on the surface seem to be two technologies that would be difficult to marry. Ajax processing is predominantly client-side, while JSF processing is predominantly server-side. Ajax is mostly JavaScript and JSF is Java. While very different, the two technologies can combine to create a powerful set of reusable user interface tools for developers.

    Let's look at an example. A developer is using JSF to create a form element that requires some specific input. This form element is just a simple input text field, but when the user tabs out of the field, an Ajax validation routine is triggered from theonblur event to test the validity of the typed data. The developer has to use this input field in many places in the application and has to embed the same JavaScript into the component on page every time it is used. Now what if all that code could be embedded into the component instead of the page? The page developer could continue to drop components onto the page and everything else would be taken care of. By shifting the validation processing into the component itself, we've made an extremely reusable, reliable component. The best benefit of this might be that we could also treat the tag as an interface and the implementation would be at the discretion of the tag developer. Any changes to the implementation would be transparent to the page developer. These benefits apply to all custom JSF components.

    Now let's take a look at how to get started creating some of our own.

    Creating a JSF Component from Scratch

    Creating a JSF component can be a bit of a challenge at first, due to the steps involved and the considerations of the JSF lifecycle. Knowing what is going on at a given time is extremely important if you are going to make the proper method calls when they are needed. Later on, we'll discuss a JSFPhaseListener that will allow processing during JSF lifecycle events. There are six events in the lifecycle. A deeper discussion of these events is beyond the scope of this article, but a detailed explanation of these events is located on Sun's Java Server Faces site.

    1. Restore view.
    2. Apply request values; process events.
    3. Process validations; process events.
    4. Update model values; process events.
    5. Invoke application; process events.
    6. Render response.

    Now that we've mentioned the lifecycle events, we'll continue creating our custom component. Creating a component from scratch involves four steps. Later on, we'll use these steps to create our first custom component.

    1. Create a rendering class for your component.
    2. Define the component in faces-config.xml.
    3. Create a tag class for placing the component in a page.
    4. Create the tag library definition in a .tld file.

    Extending Existing JSF Components

    The simplest way to create your own JSF component is to find an existing component that already takes care of most of the grunt work for you. For example, say you want a text field to be a calendar input, so you extend an existing component that renders an input field to do the job. Why go through all the trouble of creating your own from scratch when the largest portion of the rendering work is done for you in an existing component?

    Please note that the terms component and tag can be used interchangeably depending on the context. In this article, "component" refers the item we want to place on a page as a whole. For example, an input text field on a JSF page is a component. "Tag" refers to the tag class or tag placed on the page by the developer. The tag is what is responsible for placing and configuring our component. Also, note that the renderer is the class responsible for writing the component content.

    Let's look at a renderer example for extending an existing component. For this example, we are extending another renderer that creates a input text field. Remember that it doesn't really matter here which implementation of input text we are using because all implementations carry the same attributes as the JSF reference implementation, but most add more attributes for convenience or enhanced functionality. Apache MyFaces Tomahawk, for example, adds attributes for to handle the display of content based on the user's security role.

    public class MyHtmlInputTextRenderer extends HtmlInputTextRenderer public void encodeBegin(FacesContext context, UIComponent component) throws IOException { super.encodeBegin(context, component); //We could create any other component and share attribute values //from this tag to another component //Renderer baseRenderer = // context.getRenderKit().getRenderer("javax.faces.Form", "javax.faces.Form"); //baseRenderer.encodeBegin(context, component); } public void encodeEnd(FacesContext context, UIComponent component) throws IOException { ResponseWriter writer = context.getResponseWriter(); // get properties from UIOutput that were set by the specific taghandler String myMessage = (String)component.getAttributes().get("myattributemessage"); writer.startElement("p", component); writer.writeText("myMessage"); writer.endElement("p"); writer.write("\n"); } //We could create any other component and share attribute values //from this tag to another component //Renderer baseRenderer = // context.getRenderKit().getRenderer("javax.faces.Form", "javax.faces.Form"); //baseRenderer.encodeEnd(context, component); super.encodeEnd(context, component); }

    There's a lot of potential here in this code. We could manipulate the existing component in any fashion we choose. Note that in the encodeEnd method, a call to get the property myattributemessage was retrieved and displayed to the user. By extending an existing component, we not only get all the existing attributes, but we can just add new ones in our .tld definition.

    In order to make extending existing components work, you've got to put all the pieces together. The renderer and tag classes should extend an existing tag and an existing component and you still need to create the component definition in the faces-config.xmland the tag library definition. Note, however, a painful limitation of the JSP tag library specification: if you extend an existing tag, you must create your own tag library definition for that component. The simplest thing to do is to find the .tld definition for the tag you are extending and copy that tag's definition into your own .tld, and then you are free to make modifications. This is cumbersome, especially when you are looking at extending a component that has 50 attributes. A useful future feature of creating tag libraries would be to give the developer the capability to extend another tag's definition through some XML element; this would bring the tenets of object-oriented programming into .tld files. Just be glad that you can pick up all 50 of those attributes by extending an existing tag class, or you might also have had to write all 50 getters and setters as well.

    Let's recap extending existing JSF components by laying out the steps involved in doing so.

    1. Extend the rendering class for a component that is similar to the component you want to create.
    2. Define the component in faces-config.xml.
    3. Extend the tag class of the component you are mimicking.
    4. Copy the original .tld definition for the component you are mimicking into your .tld file. Remember to change it to point to your subclass.

    Now that we know the steps to creating or extending our own JSF components, we can move on creating more advanced components using Ajax.

    Use aPhaseListener to Deliver Ajax Content

    A benefit of using faces is that you don't need to define servlets to deliver content to Ajax calls. APhaseListener fires at each stage of the JSF lifecycle, letting you intercept a request and write to the response. Ajax data can be delivered in many ways, including XML, HTML, and JSON (JavaScript Object Notation). By far the simplest way to deliver content is JSON. JSON syntax is simpler to generate using json_simple.jar, it requires less bandwidth than XML, and doesn't require parsing by JavaScript on the client side like XML does. You'll need to download the source code from json.org and compile it yourself. More information about the JSON syntax can be found at the JSON home page.

    Let's take a look at an example of a PhaseListenerand how it will deliver content to anXmlHttpRequest.

    import java.io.IOException; import javax.faces.context.FacesContext; import javax.faces.event.PhaseEvent; import javax.faces.event.PhaseId; import javax.faces.event.PhaseListener; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.json.*; import java.io.*; public class AjaxPhaseListener implements PhaseListener { public void afterPhase(PhaseEvent event) { String viewId = event.getFacesContext().getViewRoot().getViewId(); if (viewId.indexOf("Ajax") != -1) { handleAjaxRequest(event); } } private void handleAjaxRequest(PhaseEvent event) { FacesContext context = event.getFacesContext(); HttpServletResponse response = (HttpServletResponse) context .getExternalContext().getResponse(); Object object = context.getExternalContext().getRequest(); if (!(object instanceof HttpServletRequest)) { //Only handle HttpServletRequests return; } HttpServletRequest request = (HttpServletRequest) object; // actually render using JSON. try { JSONObject myMessage = new JSONObject(); myMessage.put("message", "I am sending a message"); response.setContentType("text/plain"); response.setHeader("Cache-Control", "no-cache"); response.getWriter().write(secureWrapJSON(myMessage)); event.getFacesContext().responseComplete(); } catch (Exception e) { e.printStackTrace(); } } public void beforePhase(PhaseEvent arg0) { //not used, but implemented to satisfy compiler } public PhaseId getPhaseId() { return PhaseId.RESTORE_VIEW; } } //Secure wrap the JSON to prevent hijacking public String secureWrapJSON(JSONObject json) { return "/*" + json + "*/"; } 

    Don't forget to configure the PhaseListener in thefaces-config.xml:

     <lifecycle> <phase-listener>com.oreilly.ajax.AjaxPhaseListener</phase-listener> </lifecycle>

    The PhaseListener checks the request to see if the view requested contains the word "Ajax." If it does, it delivers a message to the response in the form of JSON text. It is that simple. On the client side, this message can be read into a JavaScript object by calling eval on theresponseText property of the parm of your callback method. JSON can be as complex and as structured as you want it to be. For the sake of this example, we just sent a one-line message. In reality, you will typically be sending much larger data. Below is the message you would see if you hit Ajax.faces with your browser.

     {"message":"I am sending a message"}

    This could just as easily have been XML being returned on the response, but then we would have to parse the XML with JavaScript, making a simple task fairly arduous.

    Creating Your Component and Embedding Your Ajax

    The reality of creating an Ajax-enabled JSF component is that it really isn't any different from creating any JSF component, except that the Ajax code is written in the renderer class. For the sake of simplicity, JavaScript functions should be included in a .js file. Don't attempt to write JavaScript inside the renderer directly. Below is the JavaScript to get the JSON message from ourPhaseListener.

    function getMessage() { var url = "Ajax.faces"; if (window.XMLHttpRequest) { // browser has native support for XMLHttpRequest object req = new XMLHttpRequest(); } else if (window.ActiveXObject) { // try XMLHTTP ActiveX (Internet Explorer) version req = new ActiveXObject("Microsoft.XMLHTTP"); } if(req) { req.onreadystatechange = responseHandler; req.open("Get", url, true); req.onreadystatechange = callback; req.setRequestHeader("content-type","text/plain"); req.send(null); } } function callback() { var jsonData = req.responseText; //omitted code for stripped secure JSON /* json */ var jsonObject = eval('(' + jsonData + ')'); var message = jsonObject.message; alert(message); } } 

    The code is pretty straightforward as Ajax goes. Remember that Ajax is not some new language; it is just merely the concept of making an asynchronous call from JavaScript behind the scenes to get data. It doesn't really have to be any more complicated than that.

    The call to getMessage() will be embedded into our JSF component in the renderer so that it fires with a certain event; onblur(), for example. ThegetMessage() method will make a call to theAjax.faces URL and get our JSON data, and the callback function will get the message text and send an alert.

    One thing to remember is that since the JavaScript function code will be included in a .js file, you should write the JavaScript call for the file into the renderer. Doing this will make the component more modular, but it will also create a redundancy if you have to include your component many times in one JSF page. Another solution would be to create an initializer tag that will write your JavaScript support classes into a page. Either way, it needs to be easy for page developers to use your component.

    public class MyRenderer extends Renderer public void encodeBegin(FacesContext context, UIComponent component) throws IOException { ResponseWriter writer = context.getResponseWriter(); writer.startElement("script", component); writer.writeAttribute("language", "text/javascript", null); writer.writeAttribute("src", "/js/ajax.js", null); writer.endElement("script"); writer.write("\n"); } } 

    Another option to simplify matters would be to use an Ajax framework like Dojo or Prototype to ease the pain of writing all that boilerplate Ajax code. Let's take a look at what Dojo could do for us.

    First we need to include the dojo.js file into our component like we did above with our .js file. We still also need our .js file, because it will contain our callback function. Remember to write out the include for the dojo.js file before our file or our JavaScript will be useless.

    function getMessage(){ dojo.io.bind({ url: ajax.faces, sync: false, load: function (type, data, evt){ callback(data); }, error: function(type, error){ dojo.debug('Error getting JSON data:' + error.message); }, mimetype: "text/json-comment-filtered" }); } function callback(type, data, evt){ alert(data.message); } 

    Now we have seriously cut back on some code. We are now using Dojo to hide all the nasty Ajax interactions for us. This will make our code much cleaner.

    Please make note that the code above is using the MIME typetext/jason-comment-filtered. This will tell Dojo to expect the JSON to be commented and the framework will strip the comments and eval the data for you. Also, note thatsync is set to false. If you set this totrue, the browser will lock until the call finishes. The default for this is always false. There are several informational articleson the Web to get you started using Dojo and JSON.

    Let's take a look at the same article above written using Prototype.

    function getMessage(){ //NOTE: Prototype expects secure as /*-secure- {json} */ //NOTE: IF you send response.setHeader("X-JSON", jsonString) //from server, then AJAX will parse it into a JSON Object, protecting //your JSON from being exposed Ajax.Request({ url: ajax.faces, //parameters: method: 'get', onComplete: function (type, data, evt){ callback(data); }, mimetype: "application/X-JSON" }); } function callback(type, data, evt){ alert(data.message); } 

    Looking At It from Above

    Now that we know what it takes to write a Ajax-enabled JSF component, let's recap with a set of clearly defined steps.

    1. Decide whether you want a new JSF component or you want to extend an existing one and follow the steps for that process.
    2. Create and configure a PhaseListener to deliver Ajax content to the response.
    3. Embed JavaScript includes into the renderer class'encodeBegin() method.
    4. Embed your method call to trigger your Ajax method into the renderer or the tag class.

    We have all the tools necessary to create our own Ajax-enabled JSF components. We have also leveraged some open source tools to simplify the process. Make note that there are already many Ajax-enabled JSF components already out there and most are open source. The ability to create reusable dynamic web components is where the power in the coupling of JSF and Ajax lies.