September 09 2008

Create a Common Facelets Tag Library: Share it across projects

Tutorial - Step By Step

If you’ve learned to use JSF Facelets to create on-the-fly, simple components using XHTML, then you probably have a whole slew of custom components that need to be copied between various projects, and can be somewhat painful to keep up to date. You may have tried to move them into a jar file, but Facelets can’t find them there (without some help from us.)

*The author of this article has requested feedback on the usability of this article. Please post questions, improvements, and/or comments.

Goals

The intent of this tutorial is to explain how to create a packaged jar file, which can be referenced from multiple projects. and which contains all of your tag components and classes for easier maintenance.

(Please note that you may still include non-xhtml based components in this tag library, this does not limit you to use only xhtml facelets.)

Download the following archive

Instructions:

  • Create a new Java Project. We will call it “facelets-taglib-common”
    We recommend the title for your project should be the same as your tag library, since this will be the new home of those custom (shiny) components.
  • Extract the Facelets archive and copy “jsf-facelets.jar” into your project. Make sure that it is added to the class path.
  • Copy and paste the following source files into your project.
  • Create your facelets-taglib.common.xml definition file.
  • Make necessary additions to web.xml
  • Create your first tags.

When you are finished with this tutorial, you should have the following directory structure:

facelets-taglib-common/
+—JavaSource/
|   |   CustomResourceResolver.java
|   -
|
+—lib/
|   |   jsf-facelets.jar
|   -
|
+—META-INF/
|   |   facelets-common-taglib.xml
|   |   facelets-common-taglib.tld
|   |   MANIFEST.MF
|   |
|   +—taglib/
|       |   analytics.xhtml
|       |   doctype.xhtml
|       |   your_custom_tag.xhtml
-       -


—-


CustomResourceResolver.java

This is a required utility class to allow Facelets to find resources that are not in your project folder, but instead, anywhere on the build path.

Put this file in your source folder. The reason putting your XHTML custom components and tag XML files does not work out of the box is because Facelets uses a strict ResourceResolver. The default ResourceResolver looks only in the path of your current project / War. Fortunately, however, we can override the default behavior.

import com.sun.facelets.impl.DefaultResourceResolver;
import com.sun.facelets.impl.ResourceResolver;
 
public class CustomResourceResolver extends DefaultResourceResolver implements ResourceResolver
{
    @Override
    public URL resolveUrl(String resource)
    {
        URL resourceUrl = super.resolveUrl(resource);
        if (resourceUrl == null)
        {
            if (resource.startsWith("/"))
            {
                resource = resource.substring(1);
            }
            resourceUrl = Thread.currentThread().getContextClassLoader().getResource(resource);
        }
        return resourceUrl;
    }
}


—-


facelets-taglib-common.xml

This file defines your tag-library to facelets, it is required. See the facelets documentation. After this step, you should be able to package and export your project as a Jar file.

This file needs to be located in your /facelets-taglib-common/META-INF/ directory.

<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC
  "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
  "facelet-taglib_1_0.dtd">
<facelet-taglib>
	<namespace>http://www.ocpsoft.com/facelets-common</namespace>
	<tag>
		<tag-name>analytics</tag-name>
		<source>analytics.jspx</source>
	</tag>
	<tag>
		<tag-name>doctype</tag-name>
		<source>doctype.jspx</source>
	</tag>
</facelet-taglib>


—-


facelets-taglib-common.tld (optional)

This file describes your tag-library for your IDE’s autocompletion, and for Validation, so that you can check during development to ensure that your tags are being properly used.

Note**: To enable autocompletion in your IDE, you probably need to copy this file into your own Web Application’s WebContent/META-INF/taglib/ directory, this does not affect Facelets.

<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" 
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
 
<taglib xmlns="http://java.sun.com/JSP/TagLibraryDescriptor">
	<tlib-version>1.0</tlib-version>
	<jsp-version>1.2</jsp-version>
	<short-name>rest</short-name>
	<uri>http://www.yoursite.com/facelets-taglib-common</uri>
	<display-name>My Common Tag Library</display-name>
	<tag>
		<name>analytics</name>
		<tag-class>common/analytics.jspx</tag-class>
		<body-content>JSP</body-content>
		<description>
			Render a google analytics identifier tag.
		</description>
		<attribute>
			<name>siteId</name>
			<required>true</required>
			<rtexprvalue>false</rtexprvalue>
			<type>java.lang.String</type>
			<description>
				Your unique google site-id, eg: UA-4723***-*
			</description>
		</attribute>
	</tag>
	<tag>
		<name>doctype</name>
		<tag-class>common/doctype.jspx</tag-class>
		<body-content>JSP</body-content>
		<description>
			Render an XHTML doctype declaration.
		</description>
	</tag></taglib>


—-


web.xml

We need to make two additions to your Web Application’s web.xml file in order for this to work. Assuming that you have already installed Facelets into your faces-config.xml file, you also need to do two things here:

  1. Add your tag library XML file to the list of Libraries that Facelets will load.
  2. Override the default ResourceResolver

After this step, you should have a working configuration. Just make sure that your facelets-taglib-common.jar is on the class path, or that you have referenced the new taglibrary project as a dependency.

<context-param>
	<param-name>facelets.LIBRARIES</param-name>
	<param-value>
		/META-INF/taglib/facelets-taglib-common.xml
	</param-value>
</context-param>
<context-param>
	<param-name>facelets.RESOURCE_RESOLVER</param-name>
	<param-value>CustomResourceResolver</param-value>
</context-param>


—-


Additional Resources:

Just in case you’re wondering how we did doctype and analytics, here is the source:

analytics.xhtml

<?xml version="1.0" encoding="ISO-8859-1"?>
 
<!-- THIS FILE IS LICENCED UNDER THE GPL3
	Authors: 	Derek Hollis
			Lincoln Baxter, III
 
	Version:    1.2
	Date:	        2008/05/04
	 -->
 
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:c="http://java.sun.com/jstl/core"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core" version="2.0">
 
	<ui:composition>
		<h:outputText value='&ltscript type="text/javascript"&gt'
			escape="false" />
		<h:outputText
			value='var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.")'
			escape="false" />
		<h:outputText
			value='document.write(unescape("%3Cscript src=&#39" + gaJsHost + "google-analytics.com/ga.js&#39 type=&#39text/javascript&#39%3E%3C/script%3E"))'
			escape="false"/>
		<h:outputText value='&lt/script&gt' escape="false" />
 
		<h:outputText value='&ltscript type="text/javascript"&gt' escape="false" />
		<h:outputText value='var pageTracker = _gat._getTracker("#{siteId}")' escape="false" />
		<h:outputText value='pageTracker._initData()' escape="false" />
		<h:outputText value='pageTracker._trackPageview()' escape="false" />
		<h:outputText value='&lt/script&gt' escape="false" />
	</ui:composition>
</jsp:root>


doctype.xhtml

<?xml version="1.0" encoding="ISO-8859-1"?>
 
<!-- THIS FILE IS LICENCED UNDER THE GPL3
	Authors: 	Derek Hollis
			Lincoln Baxter, III
 
	Version:    $1.0$
	Date:	        2008/05/04
-->
 
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:c="http://java.sun.com/jstl/core"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core" version="2.0">
 
	<ui:composition>
		<h:outputText value='&lt?xml version="1.0" encoding="UTF-8"?&gt'
			escape="false" />
		<h:outputText
			value='&lt!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt'
			escape="false" />
	</ui:composition>
</jsp:root>


—-



That should be everything. You should now be able to store your common facelets tags and components in a centralized location, allowing for re-use across projects and teams.

References

Comments:

(2) posted on Create a Common Facelets Tag Library: Share it across projects

Post a comment

RSS