Accessing Resources From JavaServer Faces Custom Components

   
By Mark Basler, July 2006  

JavaServer Faces technology is great for encapsulating a component's artifacts, which hides complexity from the end user. In the quest to create reusable components, the developer has to make trade-offs between complexity and reusability. When designing components, developers find that most development projects require the repetition of common tasks. One function that is commonly required is accessing static and dynamic resources. Static resources are usually JavaScript technology files, cascading style sheet (CSS) files, image files, presentation fragments, and so on. Dynamic resources are usually associated with functions that change based on the input, such as Asynchronous JavaScript and XML ((Ajax)) calls, form submissions, and link actions.

Depending on the mode of distribution that your component uses, you can use different methods of accessing resources. If the distribution is a Web Archive (WAR) or an Enterprise Archive (EAR), then you have a full array of options to access your resources. You can even have the resources packaged in your archive so that viewers can access them directly from the web page.

Most component distributions involve packaging all related resources in a Java Archive (JAR) that accompanies the application. This type of packaging can limit the options that are available to access resources because the component developer wants to minimize the amount of configuration that the component user will require. In this case, loading the resource by using the direct-access method exposes the component user to some unnecessary configuration, such as copying resource files to a specific location in the application distribution or registering artifacts in the application's deployment descriptors.

This article explores the currently available ways to fulfill static and dynamic resource requests. Future blogs and articles will discuss the emerging technologies that have not yet stabilized or entered the mainstream. This article assumes that the reader is familiar with JavaServer Faces technology and has read the pertinent chapters in the Java EE 5 Tutorial.

 
Contents
 
Directly Accessing the Resources
Serving the Resources Through a Renderer
Serving the Resources Through a PhaseListener Object
Using Third-Party Libraries to Serve the Resources
Summary
For More Information
 
Directly Accessing the Resources

The direct-access method involves packaging the resources with the archived distribution. This is the traditional web design, with image files, JavaScript technology files, and CSS files packaged under the web module, accessible through the URL, and served directly by the web or application server. In some cases, the component resources will be accessible through a servlet. This requires the component user to define the servlet or servlet mapping in the web application's standard deployment descriptor.

This is a common paradigm to access resources for web applications, but it requires the developer to perform additional configuration to use the component. There is also a risk that resource names may not be unique across components, which can lead to cases in which the developer cannot combine those components in the same web page. This is appropriate only if no other application is going to use the component or if only a small group of developers will be using it.

Serving the Resources Through a Renderer

The component's renderer can be used to serve resources that are accessed through the JavaServer Faces technology's javax.faces.webapp.FacesServlet. The technology's life cycle consists of six phases:

  1. Restore View
  2. Apply Request Values
  3. Process Validation
  4. Update Model
  5. Invoke Application
  6. Render Response

Process Events can occur after each of the four middle phases -- that is, phases 2 through 5 -- as appropriate. The Java EE 5 Tutorial provides more information about the life cycle. During the Apply Request Values phase, the renderer takes control and can supply the necessary static resources or facilitate the appropriate functionality in a dynamic call. Once the renderer has finished its task, call the responseComplete method on the javax.faces.context.FacesContext to short-circuit the rest of the JavaServer Faces technology life cycle.

This approach has serious consequences: It affects performance and has other side effects. Before the Apply Request Values phase, the Restore View phase has to reconstitute the component tree. This can be time-consuming and can impact performance, especially when maintaining state on the client. When using the responseComplete method, the phase has to finish executing, which could cause undesirable side effects to the component tree. Side effects can also occur when a component's immediate attribute is set to true, which causes the component's logic -- validation, conversion, and events -- to be executed before the Apply Request Values phase ends. Given these limitations, this is not the best approach.

Serving the Resources Through a PhaseListener Object

A PhaseListener object can be registered so that during the Restore View phase, the PhaseListener object can handle the request, possibly delegating tasks to a managed bean through a unified expression language deferred method, which replaced JavaServer Faces version 1.1's method-binding approach. For a static request, the PhaseListener object locates the resource either by using an identifying mechanism such as part of the Request URL that is available through the passed-in javax.faces.event.PhaseEvent object through the getFacesContext().getViewRoot().getViewId() methods or by extracting a value from the Map of the HttpServletRequest's parameters, which can be located through the PhaseEvent argument with the call phasesEvent.getFacesContext().getExternalContext().getRequestParameterMap().

The main problem with coding a PhaseListener object for each component's specific needs is that the PhaseListener objects in all the JARs that are registered in the multiple faces-config.xml files bundled with your application are fired sequentially, with no guarantee to the order. Developers in the Java BluePrints team have run into a problem in which resource requests were served multiple times, which caused the response to be populated with multiple copies of the resource file, placed end to end. The problem often occurs when multiple developers on a project are coding components in different ways. Even in the strictest development environments, conflicts can arise. Also, the very existence of multiple PhaseListener objects creates a performance burden because all the registered PhaseListener objects execute on every request.

Using Third-Party Libraries to Serve the Resources

Because it is hard to keep developers from adding functionality to existing PhaseListener objects, the best approach for accessing static and dynamic resources would be to have no custom PhaseListener objects at all. For this reason, the Java BluePrints team used Shale Remoting libraries in the Java BluePrints Solution Catalog and Java BluePrints Pet Store Demo 2.0 reference applications to satisfy all static and dynamic requests. The Shale-Remoting libraries are a subset of the larger Apache Shale project, which isolates resource allocation and other remoting functionality into stand-alone JARs for use in an application. Shale Remoting uses a single PhaseListener object that doesn't require developer configuration to fulfill static requests and that will delegate dynamic requests to a managed bean's deferred method. If the components in the libraries use this methodology, then all the static and dynamic requests can be satisfied without the programmer having to develop custom code to propagate the request. Also, because the Shale-Remoting approach uses a URL that starts at the web context root, the page designer doesn't have to keep track of the page's location within the web application to make sure it goes through the FacesServlet.

Static Resources Example

The following code snippet of a static resources example comes from the FileUploadRenderer class, with some slight alterations to facilitate clarity. When planning component packaging, developers can decide to store resources in a static location under the component's name to provide name space protection. For example, the FileUpload's JavaScript technology file is stored at /META-INF/fileupload/fileupload.js in the component's JAR file. The snippet shows how two static resources -- a JavaScript technology file, fileupload.js, and a CSS file, fileupload.css -- are accessed using the Shale Remoting APIs.

import org.apache.shale.remoting.Mechanism;
import org.apache.shale.remoting.XhtmlHelper;

/**
 * <p>Stateless helper bean to manufacture resource linkages.</p>
 */
                    private static XhtmlHelper helper = new XhtmlHelper();

public void encodeEnd(FacesContext context, UIComponent component)
    throws IOException {

    if ((context == null) || (component == null)) {
        throw new NullPointerException();
    }
    ResponseWriter writer = context.getResponseWriter();

    ....
     
                   //Shale-Remoting resource retrieval
     
                   helper.linkJavascript(context, component, writer,
         
                   Mechanism.CLASS_RESOURCE, 
         
                   "/META-INF/fileupload/fileupload.js");

     
                   helper.linkStylesheet(context, component, writer,
     
                      Mechanism.CLASS_RESOURCE, 
         
                   "/META-INF/fileupload/fileupload.css");

    ...
    }
                
 

Dynamic Resources Example

The following code snippet of a dynamic resources example comes from the FileUploadRenderer class, with some slight alterations to facilitate clarity. The snippet shows how Shale Remoting is used to dynamically access the managed bean's -- that is, the bpui_fileupload_handler -- handleFileUpload method that is registered in the application's faces-config.xml file. Once the dynamic call String is created, it is used as a parameter in the onsubmit JavaScript event-handler function that populates the XMLHttpRequest URL facilitated by the Dojo bind function. Executing the dynamic Shale-Remoting call through Ajax -- XMLHttpRequest -- delegates control to the managed bean's method to perform the appropriate functionality. Then the org.apache.shale.remoting.faces.ResponseFactory is used to create a javax.faces.context.ResponseWriter so that elements can be written using the startElement and EndElement convenience methods to facilitate return of the appropriate response.

import org.apache.shale.remoting.Mechanism;
import org.apache.shale.remoting.XhtmlHelper;

                    private static XhtmlHelper helper=new XhtmlHelper();

public void encodeBegin(FacesContext context, UIComponent component)
    throws IOException {

    if ((context == null) || (component == null)) {
        throw new NullPointerException();
    }
    ResponseWriter writer = context.getResponseWriter();
    
    ....
    
     
                   // Shale-Remoting callback for status     String fileUploadCallback = helper.mapResourceId(context,         Mechanism.DYNAMIC_RESOURCE,          "/bpui_fileupload_handler/handleFileUpload");

    outComp.getAttributes().put("onsubmit", 
        "return bpui.fileupload.submitForm(this, '" + 
        retMimeType + "', '" + retFunction + "','" + 
        progressBarDivId + "', '" + fileUploadCallback + "')");  

    ...
}
                
 

Note: When using Shale-Remoting dynamic calls, the response header is set to allow the browser to cache response data when the request URL has not changed. This could cause problems in a component such as the FileUpload ProgressBar, which uses the same URL to poll for status updates, especially in the Microsoft Internet Explorer browser. Shale Remoting will address this in a future release, but until then, you can tell the browser not to cache the response by setting response headers, as depicted in the following code snippet. The snippet comes from the FileUploadHandler class's handleFileStatus method. Modern browsers may not need all of these headers, but this method is the one that Struts uses and that Apache plans to use in Shale Remoting.

FacesContext context=FacesContext.getCurrentInstance();
HttpServletResponse response=
    (HttpServletResponse)context.getExternalContext().getResponse();
response.setHeader("Pragma", "No-Cache");
                  

response.setHeader("Cache-Control", 
    "no-cache,no-store,max-age=0");
                  

response.setDateHeader("Expires", 1);
                
 

Snippet: faces-config.xml

The configured managed beans in this example are used to link the request to the managed bean through Shale Remoting. Also, note that the fileUploadStatus managed bean is prepopulated for use in the bpui_fileupload_handler managed bean.

<managed-bean>
    <managed-bean-name>
        bpui_fileupload_handler
    </managed-bean-name>
    <managed-bean-class>
        com.sun.javaee.blueprints.components.ui.fileupload.FileUploadHandler
    </managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
    <managed-property>
        <property-name>fileUploadStatus</property-name>
        <value>
                   #{fileUploadStatus}</value>
    </managed-property>        
</managed-bean>
<managed-bean>
    <managed-bean-name>
                   fileUploadStatus</managed-bean-name>
    <managed-bean-class>
        com.sun.javaee.blueprints.components.ui.fileupload.FileUploadStatus
    </managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
</managed-bean>
                
 

If you want to have the managed bean populated with a parameter from the HttpServerRequest, the managed-property declaration for a form element named formInputElement would look like this:

<managed-property>
    <property-name>
                   formInputElementValue</property-name>
    <value>#{param.
                   formInputElement}</value>
</managed-property>
                
 

You can find more information on JavaServer Faces technology's implicit objects in the Java EE 5 Tutorial. The HttpServerRequest parameter value could also be retrieved in the managed bean using the Map of the HttpServletRequest's parameters, which can be located through the call FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().

Snippet: FileUploadHandler

Once the Shale-Remoting libraries link the request to the managed bean, the ResponseFactory is used to create a ResponseWriter to facilitate the request's response.

import org.apache.shale.remoting.faces.ResponseFactory;
import javax.faces.context.ResponseWriter;

private static ResponseFactory factory=new ResponseFactory();

public void handleFileUpload() {
    FacesContext context=FacesContext.getCurrentInstance();

    ...

    try {
        ResponseWriter writer=
            factory.getResponseWriter(context, "text/xml");
        writer.startElement("response", null);
        writer.startElement("message", null);
        writer.write(status.getMessage());
        writer.endElement("message");

        ...

        writer.endElement("response");
        writer.flush();
    } catch (IOException iox) {
        getLogger().log(Level.SEVERE, "response.exception", iox);
    }
}
 
Summary

This article has discussed how developers can use JavaServer Faces technology to fulfill static and dynamic resources when creating their custom components.

For More Information
About the Author

Mark Basler , a senior software engineer, is part of the Java BluePrints team and helped create the Java Blueprints Solution Catalog and the Java Pet Store Demo 2.0, reference applications that demonstrate how to design and develop (Ajax)-enabled Web 2.0 applications. His other contributions include the design and development of key components for Sun's Download Center, eCommerce suites, and Sun Java System Application Server.

Rate and Review
Tell us what you think of the content of this page.
Excellent   Good   Fair   Poor  
Comments:
Your email address (no reply is possible without an address):
Sun Privacy Policy

Note: We are not able to respond to all submitted comments.