23
Oct
10

JSF Custom Components, Importing Resources


As the web development world moves to a more “component-based” approach we see more and more usage of Dojo, YUI, jQuery, extJS. These frameworks require css and js files be imported into the page before their components could be used. This page describes the process of creating a “<resources/>” tag that will do this work for you.

Background

The benefit of using “resource tag” is to allow “selective importing” of resources onto the page so you don’t flood the client’s browser with unnecessary JavaScript.

The way it will work is by allowing each JSF component tag to indicate what resources are necessary in their constructors. This information will be communicated to the “ResourceHolder” managed bean.

The “resources” tag will read the information in the “ResourceHolder” during the render phase and import only the JavaScript necessary to render the components.

Requirements

Getting Started

To demonstrate this concept we will add to the existing project described in my “Hello World Posting”. (see above)

If you have not done so already import the project into eclipse. It will make editing a lot easier.

mvn eclipse:clean eclipse:eclipse

Import the as “Existing Project into Workspace”.

Its easy to get lost on this page so here is a summary of what is being done:

  1. Create a request scope managed bean that holds a list of resources for the current request.
  2. Create a Component Class
  3. Create a Component Renderer – this component is responsible for iterating thru the list of resources in the managed bean and rendering only the necessary “script” or “style” tags for the components on the page.
  4. Create a Resource Tag Class
  5. Define the component in the faces config
  6. Define the ResourceTag in the TLD
  7. Modify the HtmlHelloWorld tag to require extJS css and js files
  8. Modify the JSP and insert the resource tag.
  9. Run the code in Jetty

Create the Resource Holder Impl Managed Bean

The ResourceHolder class is simply a collection of URL’s (Strings). The resourceHolder is passed to the page via a request scoped Managed Bean seen above.

src/main/java/org/extjsf/resource/ResourceHolderImpl.java

package org.extjsf.resource;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class ResourceHolderImpl {
	private List<String> resources;
	
	public ResourceHolderImpl() {
		resources = new ArrayList<String>();
	}
	
	public void addResource(String resource) {
		if(!resources.contains(resource))
			resources.add(resource);
	}

	public Collection<String> getResources() {
		return resources;
	}
}

Insert the following towards the top of your faces-config.

src/main/webapp/WEB-INF/faces-config.xml

	<managed-bean>
		<managed-bean-name>myResourceHolder</managed-bean-name>
		<managed-bean-class>org.extjsf.resource.ResourceHolderImpl</managed-bean-class>
		<managed-bean-scope>request</managed-bean-scope>
	</managed-bean>

Create Resources Component Class

src/main/java/org/extjsf/component/resources/Resources.java

package org.extjsf.component.resources;

import javax.faces.component.UIComponentBase;

public class Resources extends UIComponentBase {
	public static final String COMPONENT_TYPE = "org.extjsf.component.Resources";
	public static final String COMPONENT_FAMILY = "org.extjsf.component";
	
	@Override
	public String getFamily() {
		return COMPONENT_FAMILY;
	}
}

Define the Component Renderer

src/main/java/org/extjsf/component/resources/ResourcesRenderer.java

package org.extjsf.component.resources;

import java.io.IOException;

import javax.el.ValueExpression;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.Renderer;

import org.extjsf.resource.ResourceHolderImpl;

public class ResourcesRenderer extends Renderer {
	@Override
	public void encodeEnd(FacesContext facesContext, UIComponent component)
			throws IOException {
		ResourceHolderImpl resourceQueue = getResourceHolder(facesContext);
		ResponseWriter writer = facesContext.getResponseWriter();
		Resources resources = (Resources) component;
		
		writer.write("\n");

		for(String resource : resourceQueue.getResources()) {
			if(resource.endsWith("css")) {
				renderCSSDependency(facesContext, resource);				
			}
			else if(resource.endsWith("js")){
				renderScriptDependency(facesContext, resource);				
			}
		}
		
		writer.write("\n");
	}
	
	// for now these scriptPath references are absolute urls
	protected void renderScriptDependency(FacesContext facesContext, String scriptPath) throws IOException{
		ResponseWriter writer = facesContext.getResponseWriter();
		writer.startElement("script", null);
		writer.writeAttribute("type", "text/javascript", null);
		writer.writeAttribute("src", scriptPath, null);
		writer.endElement("script");
		writer.write("\n");
	}
	
	// for now these cssPath references are absolute urls
	protected void renderCSSDependency(FacesContext facesContext, String cssPath) throws IOException{
		ResponseWriter writer = facesContext.getResponseWriter();
		writer.startElement("link", null);
		writer.writeAttribute("rel", "stylesheet", null);
		writer.writeAttribute("type", "text/css", null);
		writer.writeAttribute("href", cssPath, null);
		writer.endElement("link");
		writer.write("\n");
	}

	protected ResourceHolderImpl getResourceHolder(FacesContext facesContext) {
		ValueExpression ve = facesContext.getApplication()
				.getExpressionFactory().createValueExpression(
						facesContext.getELContext(),
						"#{myResourceHolder}", ResourceHolderImpl.class);

		return (ResourceHolderImpl) ve.getValue(facesContext.getELContext());
	}
}

Resources Tag Class

src/main/java/org/extjsf/component/resources/ResourcesTag.java

package org.extjsf.component.resources;

import javax.faces.webapp.UIComponentELTag;

import org.extjsf.component.resources.Resources;

public class ResourcesTag extends UIComponentELTag {

	public String getComponentType() {
		return Resources.COMPONENT_TYPE;
	}

	public String getRendererType() {
		return "org.extjsf.component.ResourcesRenderer";
	}
}

Define the Component in the faces-config.xml

Put the following xml after the component that is currently defined in the file.
src/main/webapp/WEB-INF/faces-config.xml

	<component>
		<component-type>org.extjsf.component.Resources</component-type>
		<component-class>org.extjsf.component.resources.Resources</component-class>
	</component>

Define the Renderer in the faces-config.xml

Put the following xml after the renderer that is currently defined in the file.

		<renderer>
			<component-family>org.extjsf.component</component-family>
			<renderer-type>org.extjsf.component.ResourcesRenderer</renderer-type>
			<renderer-class>org.extjsf.component.resources.ResourcesRenderer</renderer-class>
		</renderer>

Define the resources tag in the tag library descriptor

Insert the following into:
src/main/webapp/WEB-INF/htmlHelloWorld.tld

<tag>
	<name>resources</name>
	<tag-class>org.extjsf.component.resources.ResourcesTag</tag-class>
	<body-content>JSP</body-content>
</tag>

At this point the resources tag is defined to print resources that are required by each of the components.

HtmlHelloWorld

Next we will modify the HelloWorld Component Class to require the extJSF resources in the default constructor. This component will indicate that the extjs css and js files should be included by the resource renderer. It will store this information in the resourceHolder managed bean seen above.

Your HtmlHelloWorld should look like this…

src/main/java/org/extjsf/component/helloworld/HtmlHelloWorld.java

package org.extjsf.component.helloworld;
 
import javax.el.ValueExpression;
import javax.faces.component.UIComponentBase;
import javax.faces.context.FacesContext;

import org.extjsf.resource.ResourceHolderImpl;

 
public class HtmlHelloWorld extends UIComponentBase {
    public static final String COMPONENT_FAMILY = "org.extjsf.component";
 
    @Override
    public String getFamily() {
        return COMPONENT_FAMILY;
    }
    
	protected ResourceHolderImpl getResourceHolder() {
		FacesContext facesContext = getFacesContext();
		if (facesContext == null)
			return null;

		ValueExpression ve = facesContext.getApplication()
				.getExpressionFactory().createValueExpression(
						facesContext.getELContext(),
						"#{myResourceHolder}", ResourceHolderImpl.class);

		return (ResourceHolderImpl) ve.getValue(facesContext.getELContext());
	}
	
	public HtmlHelloWorld() {
		ResourceHolderImpl resourceHolder = getResourceHolder();

		if(resourceHolder != null) {
			// Note: In an actual production code you would create a resource
			// servlet and use relative url's. The way its done here is 
			// just for demonstration purposes only.
			resourceHolder.addResource("http://dev.sencha.com/deploy/ext-3.3.1/resources/css/ext-all.css");
			resourceHolder.addResource("http://dev.sencha.com/deploy/ext-3.3.1/adapter/ext/ext-base.js");
			resourceHolder.addResource("http://dev.sencha.com/deploy/ext-3.3.1/ext-all.js");
		}
	}
}

And Finally the JSP Page

Finally we create a JSP page that uses the “resources” tag…

src/main/webapp/examples/resources.jsp

<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
<%@ taglib prefix="e" uri="http://extjsf.org/components" %>
 
<f:view>
    <html>
    <head>
        <title>Resources JSF Example</title>        
		<e:resources/>        
	</head>
<body>
<e:helloworld/>
    </body>
    </html>
</f:view>

Run the example

Drop to the command line and “cd” to the project’s base folder (where the pom.xml file is).

Type the following command to start the jetty servlet engine.
mvn jetty:run
Navigate to: http://localhost:8080/examples/resources.jsf

View source on the page should show something like this…
src/main/webapp/examples/resources.jsp

<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html>
    <head>
        <title>Resources JSF Example</title>        
		
<link rel="stylesheet" type="text/css" href="http://dev.sencha.com/deploy/ext-3.3.1/resources/css/ext-all.css" />
<script type="text/javascript" src="http://dev.sencha.com/deploy/ext-3.3.1/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="http://dev.sencha.com/deploy/ext-3.3.1/ext-all.js"></script>

        
	</head>
<body>
<div style="color: red">HelloWorld! from renderer.</div>
    </body>
    </html>

To demonstrate Dynamic Resources

To demonstrate that the inclusion of the resources is dynamic, just remove the helloWorld tag from the body of the jsp page. Hit refresh on your browser and you will see that the head of the page no longer includes the css and the javascript files.

To productionalize the code

The example above can be improved by introducing a “ResourceServlet” that is responsible for resolving the libraries via “relative” links. The Resource Servlet can be programmed to return the files from the classpath (within the jar) thus simplifying the deployment.

If you run into trouble

The code on this page has been tested and worked at the time of this writing. If its not working for you please make sure you double check each step to make sure nothing was missed. If you are still having trouble just write a small note about it below and I will try to help you out.

That’s all for now

Advertisements

0 Responses to “JSF Custom Components, Importing Resources”



  1. Leave a Comment

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 78 other followers

October 2010
S M T W T F S
« Sep   Nov »
 12
3456789
10111213141516
17181920212223
24252627282930
31  

Blog Stats

  • 822,102 hits

%d bloggers like this: