September 9th, 2008 by Lincoln Baxter III

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.)

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='&lt;script 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=&#39;text/javascript&#39;%3E%3C/script%3E"));'
			escape="false"/>
		<h:outputText value='&lt;/script&gt;' escape="false" />
 
		<h:outputText value='&lt;script 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

Posted in JSF, OpenSource

27 Comments

  1. Sébastien says:

    I did something similar recently for a project I worked on. The cool thing with Tomcat is that you don’t need to declare the taglib in the target project at all; it will be loaded automatically during startup (now I have to check again if that was a Tomcat specific feature or not, I don’t remember :p).

  2. Lincoln says:

    That’s correct! I’ve packaged the taglib with the JAR file and although Facelets complains, it still find it:

    Sep 11, 2008 12:14:32 AM com.sun.facelets.FaceletViewHandler initializeCompiler
    SEVERE: Error Loading Library: /META-INF/taglib/facelets-common-taglib.xml
    java.io.FileNotFoundException: /META-INF/taglib/facelets-common-taglib.xml
    at com.sun.facelets.FaceletViewHandler.initializeCompiler(FaceletViewHandler.java:272)
    at com.sun.facelets.FaceletViewHandler.initialize(FaceletViewHandler.java:161)
    at com.sun.facelets.FaceletViewHandler.renderView(FaceletViewHandler.java:537)
    at org.ajax4jsf.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:108)
    at org.ajax4jsf.application.AjaxViewHandler.renderView(AjaxViewHandler.java:189)
    at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:109)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:100)
    ———
    But this later:
    Sep 11, 2008 12:14:32 AM com.sun.facelets.compiler.TagLibraryConfig loadImplicit
    INFO: Added Library from: jar:file:/WEB-INF/lib/facelets-common-taglib.jar!/META-INF/taglib/facelets-common-taglib.xml

  3. Mitch says:

    Good tutorial.
    But I can’t get my Taglib to work.
    It doesn’t seem to find my TLD in Netbeans and when I deploy it, it won’t work too. It just doesn’t recognise my namespace.

  4. FkJ says:

    Thanks for the tutorial, helped me a lot.

    I had reRender problems with Tomcat 6 after packing templates in a JAR.

    Here is the solution:

    http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4205508

  5. Keith Moore says:

    I was able to get it to work by not putting the facelets.RESOURCE_RESOLVER context-param in the web.xml. However, I had to put the CustomResourceResolver in my project source code. I would like to put this in a common project since it will be the same for every project. I dont want to have to copy and paste it into every new app and change the name to CustomResourceResolver.java. I could probably modify the build script but the target project would have the package name of the common project. This seems a little ugly. Any thoughts on how to resolve this?

  6. Lincoln says:

    It sounds like the .jar file containing CustomResourceResolver is not on the build path.

  7. Brian LEathem says:

    Creating a custom resource resolver is unnecessary. All you have to do is put your taglib and tags in the META-INF folder of your jar, and facelets will automatically resolve them. You even don’t have to put the taglib in your web.xml.

    See:
    https://facelets.dev.java.net/nonav/docs/dev/docbook.html#taglib-web
    http://thomaswabner.wordpress.com/2008/06/25/reuse-facelets-xhtml-files-and-taglibs-from-jar-archives/

  8. abdellah says:

    Hi Lincoln,

    thank you for this article!
    Have you tried to include resources (js, css or images…) in the JAR file ?
    Where should I put resources in the jar and how to reference it inside my facelet page?

    Thanks
    Abdellah

    1. Lincoln says:

      If you are using JSF2, you can use the built-in resource handling functionality. Such as… image=”#{resource[‘images:back.jpg’]}”

      Which will look for ../WebContent/resources/images/back.jpg, and META-INF/resources/images/back.jpg (depending if you have placed these in your web root or a JAR file), and insert that image into the output.

      If you are not using JSF2, then you can create a custom Resources Servlet. Several example solutions can be found here:

      http://cagataycivici.wordpress.com/2006/05/09/jsf_serving_resources_from_a/

      1. abdellah says:

        Hi lincoln,

        thank u for your Response.
        i’m not using jsf 2.0 ,and i try to crate a custom ResourcePhaseListener ,but it still not load the CSS and javascript files from the JAR-File.
        In one custom Tag i have CSS inside like:

        a.button {
        background: transparent url(images/But_norm_re.png) no-repeat 100% 0;
        font-family: Arial, Helvetica, Geneva, Sans-Serif ;
        }

        and the picture:But_norm_re.png still not appear!

      2. Lincoln says:

        Hey Abdellah,

        You’d have to ask that blog owner for more information. I know he was very successful getting it to work in his PrimeFaces project (there is a very active community on those forums, and he/they may be able to help you out.

        I hope this points you in the right direction!

  9. Marius says:

    Hi lincoln,

    do you know any Java IDE that supports projects for such a library? I’ve tried Eclipse and NetBeans but they give Facelets editing tools only for web projects. It means – only for war files.

    With Regards,
    Marius

    1. Lincoln says:

      NetBeans 6.8 works for me. You can always manually open the type of editor you want in Eclipse, as well.

  10. Stuart James says:

    Hi Lincoln,
    Thanks for posting this article. I found it very useful. I’m trying to extend it a bit further and also include my resource bundle in the jar.

    is however not loading the page resources after I moved it into the jar.

    Any suggestions to make this work?
    Thanks
    Stuart

    1. Lincoln says:

      Hi Stuart,

      I’m not sure. As long as the bundle is on the same place in the ClassPath, it should work fine.

      Wish I had some suggestions, but you can try the “GlassFish WebTier Forum” on SUN’s forums and ask there about JSF2. Lots of discussion there. Someone would probably know.

      –Lincoln

  11. LaLa says:

    I tried your tutorial, but I get errors because of my web.xml:

    facelets.LIBRARIES

    /WEB-INF/custom-components/ris-tags.xml;
    /META-INF/taglib/facelets-taglib-common.xml

    And the error …
    010-07-20 16:41:31,886 WARN [ http-8082-1] Problem loading library:
    /META-INF/taglib/facelets-taglib-common.xml {com.icesoft.faces.facelets.D2DFaceletViewHandler.initializeCompiler() (line 141)}
    java.io.FileNotFoundException:
    /META-INF/taglib/facelets-taglib-common.xml
    at com.icesoft.faces.facelets.D2DFaceletViewHandler.initializeCompiler(D2DFaceletViewHandler.java:131)

    Any idea?

    1. Lincoln says:

      You’d have to ask on the IceFaces forums, they change a lot of the behavior of JSF.

  12. […] Create a Common Facelets Tag Library: Share it across projects […]

  13. Tuukka Mustonen says:

    Hi and thanks for the article. So, it is possible to reference templates from another JAR’s classpath, but what about simply the root of the JAR?

    Also, in order to serve resources directly from another JAR one would need a custom servlet to do this as explained in http://www.ilikespam.com/blog/packaging-your-facelets-in-jars, right?

    1. Lincoln says:

      You don’t necessarily need a custom servlet; JSF can be used to serve resources as well, but you need to get the response object from the External Context to serve any kind of custom resource – whether or whether not directly out of a JAR file.

      See:

      FacesContext.getCurrentInstance().getExternalContext().getResponse()
      1. Tuukka Mustonen says:

        So one can fetch the response object from anywhere in the application during the POST processing? Good to know.

        Thanks for sharing your insight!

  14. Felipe says:

    I’m getting the following error with Mojarra 2.0.2 when I try to use the template from a JAR:

    /newxhtml.xhtml @2,63 Invalid path : /SysProj/faces/javax.faces.resource/main.xhtml?ln=template

    Thanks for any help,
    Felipe

  15. […] How do we get JSF .xhtml files bundled in a JAR file? If we are using JSF 1.2, this article about packaging Facelets in a JAR file will come in handy, but if using JSF 2, then the hard part is already done for us – […]

  16. Leonardo Lobo says:

    How can I do this run in Oracle Weblogic?
    I can do only in Tomcat.
    Do you have any tips?

    thanks

  17. Mirko says:

    Sorry: the code was lost:

    Thanks for the article.

    ist it possible to reference a JAR-packaged .xhtml-file directly from a composite? Doing it by writing

    <ui:composition template=”#{resource[‘templates:masterTmpl.xhtml’]}”>

    and storing the file “masterTmpl.xhtml” as META-INF/templates/masterTmpl.xhtml in th JAR does not work. I always get something like “Invalid path : /project/javax.faces.resource/masterTmpl.xhtml?ln=templates”

    I don’t want to use a taglib here.

    Any idea?

    Thanks,
    Mirko

  18. Mark says:

    Very interesting thank you but I am not sure about the jsp…

    The <facelet-taglib> (*.taglib.xml) and additionally <taglib> (*.tld); The taglib.xml may have also attributes etc so may the packed jar just have *.taglib.xml only if all facelets would have *.xhtml extensions only?

    And the code very begin as …

    <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">

    …can it be replaced with …

    <ui:composition
    	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">

    so no JSP in packed jar will be placed at all?

    Thanks

    p.s I couldn’t download jsf-facelets.jar; The link throws 404 or something 🙁

Leave a Comment




Please note: In order to submit code or special characters, wrap it in

[code lang="xml"][/code]
(for your language) - or your tags will be eaten.

Please note: Comment moderation is enabled and may delay your comment from appearing. There is no need to resubmit your comment.