Differences between revisions 6 and 7
Revision 6 as of 2008-01-16 08:44:50
Size: 7633
Editor: tchize
Comment:
Revision 7 as of 2009-09-20 23:00:44
Size: 7633
Editor: localhost
Comment: converted to 1.6 markup
No differences found!

Introduction

Some myfaces components needs to reference javascript, css and images for some components. To leverage the usability of these components myfaces introduces the AddResource component which can be used to add references to external resources to a rendered html page. These resources where loaded by the browser through separate requests. Currently AddResource can be used for the following tasks:

  • add inline javascript and css to page head definition (same content is rendered only one time)
  • add external javascript and css files to page head definition (same reference is rendered only one time)
  • add external javascript and css files to current location (same reference may be rendered multiple times)
  • handles external references to images, javascript and css files who are located in myfaces jar files.
  • handling of any other external references through implementations for ResourceHandler and ResourceLoader

To get an instance of org.apache.myfaces.component.html.util.AddResource call

AddResourceFactory.getInstance(FacesContext);
AddResourceFactory.getInstance(HttpServletRequest);

Interface ResourceHandler

A ResourceHandler instance controls how a ResourceLoader class identifies which resource must be loaded. This is normally done by applying paramters to the url through param=value or by defining a custom uri. AddResource will prefix this uri with data to identify that the request is an AddResource request and to know which ResourceLoader class is responsible for serving the response for the request.

public interface ResourceHandler
{
    /**
     * Returns the resource loader class which is used to load the resource
     *
     * @return a class which implements org.apache.myfaces.component.html.util.ResourceLoader
     * @see ResourceLoader
     */
    Class getResourceLoaderClass();

    /**
     * Returns the uri part which is used by the resourceloader to identify the resource to load.
     *
     * @param context
     * @return the returned resource uri will be passed as the resourceUri parameter for
     * the ResourceLoader.serveResource method (omitting request parameters)
     * @see ResourceLoader#serveResource(HttpServletRequest, HttpServletResponse, String)
     */
    String getResourceUri(FacesContext context);

Interface ResourceLoader

A ResourceLoader is responsible to render the content to the response. How the content is loaded or created depends on the implementation of the ResourceLoader instance. Since AddResource needs to create an instance of the ResourceLoader it is nessessary that a default constructor is defined for the implementation.

public interface ResourceLoader
{
    /**
     * Called by AddResource to render external resource data
     *
     * @param request the request
     * @param response the response to write the resource content to
     * @param resourceUri contains the uri part after the uri which
     * is used to identify the resource loader
     *
     * @throws IOException
     */
    void serveResource(HttpServletRequest request, HttpServletResponse response, String resourceUri) throws IOException;
}

Towmahawk ExtensionsFilter

See http://myfaces.apache.org/tomahawk/extensionsFilter.html how to setup the MyFacesExtensionsFilter to serve the external resources.

Using AddResources to load ressource in your custom component

If you already tried to use add ressource to load javascript / css / picture for your custom component, you probably tried to use the most simple forms:

import org.apache.myfaces.renderkit.html.util.AddResourceFactory;
....
AddResourceFactory.getInstance(context).addJavaScriptHere(context,uri);
....
String pictureUrl = AddResourceFactory.getInstance(context).getResourceUri(context,uri);
....

That's how tomahawk components use it. Unless the uri your want to load resources from starts with org/apache/myfaces/custom, it won't work for your custom component. The reason is that, by default, myfaces uses the MyFacesResourceHandler, which itself uses MyFacesResourceLoader. It's that ResourceLoader that, by security, limit access to that signel part of classpath. This is a security feature, it prevents specially crafted urls to be used to retrieve your configurations files, etc. Keep this in mind when using the solution suggested below, you will have to implement your own security rules to protect your configuration files.

Suppose you classes and resources are in com.company.jsf. You will need 3 things to be able to use them in custom components:

  1. create a custom ResourceLoader, that will serve your ressources and do some security checks

  2. create a custom ResourceHandler, that will mainly serve as pointing out to your RessourceLoader class

  3. change a bit your use of AddRessource class

Custom ResourceLoader

ResourceLoader, implement security by limiting content type served

public class CompanyResourceLoader implements ResourceLoader {

    public static Map documentTypes = new HashMap();

    static {
        documentTypes.put(".js","text/javascript; charset=utf-8");
        documentTypes.put(".html","text/html; charset=utf-8");
        documentTypes.put(".htm","text/htm; charset=utf-8");
        documentTypes.put(".gif","image/gif");
        documentTypes.put(".png","image/png");
        documentTypes.put(".jpg","image/jpeg");
        documentTypes.put(".jpeg","image/jpeg");
    }
    private String getContentType(String name) {
        if (name.lastIndexOf('.')<=0)
            return null;
        /* extract extension and use it to locate content type */
        return (String)documentTypes.get(name.substring(name.lastIndexOf('.')));
    }
    /**
     * Serve a .jar specific ressource to client. 
     */
    public void serveResource(ServletContext context, HttpServletRequest request,
            HttpServletResponse response, String uri) throws IOException {
        
        String contentType;
        /* only return ressource with known types */
        if ((contentType=getContentType(uri))!=null){
            response.setContentType(contentType);
            byte[] buffer = new byte[256];
            InputStream is = getClass().getResourceAsStream(uri);
            OutputStream os = response.getOutputStream();
            int i;
            while ((i = is.read(buffer))>-1)
                os.write(buffer, 0, i);
        } else {
            System.err.println("uri "+uri+" won't be served, unspecified content type");
        }
    }    
}

Custom ResourceHandler

Just point out to our CompanyResourceLoader

public class CompanyResourceHandler implements ResourceHandler {

    private String uri;

    public HtmlResourceHandler(String uri) {
        this.uri=uri;
    }
    public Class getResourceLoaderClass() {
        return CompanyResourceLoader.class;
    }

    public String getResourceUri(FacesContext ctx) {
        return uri;
    }

}

The code

Now, when you need a ressource, you will have to provide your Handler to AddResource. Use the example code below:

import org.apache.myfaces.renderkit.html.util.AddResourceFactory;
....
AddResourceFactory.getInstance(context).addJavaScriptHere(context,new CompanyResourceHandler("/company/javascript/component.js"));
....
String pictureUrl = AddResourceFactory.getInstance(context).getResourceUri(context,new CompanyResourceHandler("/pictures/companyLogo.png"));
....

External_Resources (last edited 2009-09-20 23:00:44 by localhost)