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.)
UPDATE – 09/15/2010: In JSF2, this process is no longer required. Simply use JSF2 relocatable resources: Any file placed under the /WebApp/resources directory, and any files placed in the jar:/META-INF/resources directories can be served directly using the JSF2 notation:
#{resource['ocpsoft-jsf2-templates/liquid-blue-2col:images/bodybg.jpg']} |
#{resource['ocpsoft-jsf2-templates/liquid-blue-2col:images/bodybg.jpg']}
Which targets the following resource:
[META-INF or WebContent]/resources/ocpsoft-jsf2-templates/liquid-blue-2col/images/bodybg.jpg |
[META-INF or WebContent]/resources/ocpsoft-jsf2-templates/liquid-blue-2col/images/bodybg.jpg
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;
}
} |
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> |
<?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> |
<!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:
- Add your tag library XML file to the list of Libraries that Facelets will load.
- 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> |
<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='<script type="text/javascript">'
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='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));'
escape="false"/>
<h:outputText value='</script>' escape="false" />
<h:outputText value='<script type="text/javascript">' 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='</script>' escape="false" />
</ui:composition>
</jsp:root> |
<?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='<script type="text/javascript">'
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='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));'
escape="false"/>
<h:outputText value='</script>' escape="false" />
<h:outputText value='<script type="text/javascript">' 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='</script>' 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='<?xml version="1.0" encoding="UTF-8"?>'
escape="false" />
<h:outputText
value='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
escape="false" />
</ui:composition>
</jsp:root> |
<?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='<?xml version="1.0" encoding="UTF-8"?>'
escape="false" />
<h:outputText
value='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
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
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).
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
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.
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
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?
It sounds like the .jar file containing CustomResourceResolver is not on the build path.
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/
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
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/
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!
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!
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
NetBeans 6.8 works for me. You can always manually open the type of editor you want in Eclipse, as well.
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
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
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?
You’d have to ask on the IceFaces forums, they change a lot of the behavior of JSF.
[…] Create a Common Facelets Tag Library: Share it across projects […]
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?
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:
So one can fetch the response object from anywhere in the application during the POST processing? Good to know.
Thanks for sharing your insight!
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
https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=1189
Any workaround?
[…] 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 – […]
How can I do this run in Oracle Weblogic?
I can do only in Tomcat.
Do you have any tips?
thanks
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
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 …
…can it be replaced with …
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 🙁