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
- Maven 2
- Java 5 or later
- JSF 1.2
- Successful completion of JSF Custom Components Hello World.
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:
- Create a request scope managed bean that holds a list of resources for the current request.
- Create a Component Class
- 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.
- Create a Resource Tag Class
- Define the component in the faces config
- Define the ResourceTag in the TLD
- Modify the HtmlHelloWorld tag to require extJS css and js files
- Modify the JSP and insert the resource tag.
- 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.
