Tuesday, April 17, 2012

Generated Facelets templates not applying / can't find CSS stylesheets

Using NetBeans, I created a Facelets template and a Facelets template client through the wizards provided. No changes were made to the source code.

However, when navigating in the generated template client with my browser, I noticed that the sections "inserted" (e.g, header, footer, content) were not being stylized in any way.

After a great deal of head banging onto the wall, I finally found a solution for this.
I checked the generated HTML source code in my browser (from /faces/index.xhtml, my template client)  to see where the CSS files were being pointed at:
http://localhost:8080/MyContextRoot/faces/resources/css/default.css
http://localhost:8080/MyContextRoot/faces/resources/css/cssLayout.css

These files were not publicly available, so the browser couldn't load them to process the styles. This was all generated by NetBeans. My /faces/index.xhtml was generated dynamically with sucess, so it wasn't a problem with the mapping of the Faces servlet.

After comparing my project files, specifically the WEB-INF/web.xml, with an empty project I came to the conclusion that the cause of the problem was a conflict in servlet URL patterns. This is how my web.xml looked like:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>
    <servlet>
        <servlet-name>ServletAdaptor</servlet-name>
        <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>ServletAdaptor</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>/faces/*</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>10</session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>faces/index.xhtml</welcome-file>
    </welcome-file-list>
</web-app>

The /* URL pattern from the Jersey servlet will basically hide the stylesheets location, so they will not be accessible through the generated template code.


Fixing it


1. The best fix is the right one. Go to your WEB-INF/web.xml and fix any superposition of URL patterns. In my case I changed the Jersey URL pattern from:
    <servlet-mapping>
        <servlet-name>ServletAdaptor</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
to:
    <servlet-mapping>
        <servlet-name>ServletAdaptor</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>

2. If you don't want to do the above method, open your template file (in my case it was template.xhtml) and replace the following lines:

<link href="./resources/css/default.css" rel="stylesheet" type="text/css" />
<link href="./resources/css/cssLayout.css" rel="stylesheet" type="text/css" />
with:
<h:outputStylesheet name="css/default.css"/>
<h:outputStylesheet name="css/cssLayout.css"/>

The difference is that now the actual HTML CSS include code will be generated in the server with the correct link for the CSS files. I checked the code that was generated in my case, which was:
<link type="text/css" rel="stylesheet" href="/MyProject/faces/javax.faces.resource/css/default.css" />
<link type="text/css" rel="stylesheet" href="/MyProject/faces/javax.faces.resource/css/cssLayout.css" />

And it also works. You can use it but it is less portable and depends on the configuration and future releases.

I hope that in the future Oracle adds a way to auto-detect these conflicts in NetBeans to ease development for beginner Java EE developers.

Useful links:
JSF template not applied
Styles not getting applied for the Facelets template client
Applying a Facelets Template