Curious how JSF loads without requiring a listener in web.xml?
October 07, 2010
I always wondered how Mojarra (the JSF Reference Implementation) managed to initialize without requiring a Servlet lifecycle listener entry (using the <listener> element) in web.xml. I figured it was either initializing on the first request or otherwise relying on some container integration. People would have likely complained already if the first approach was used, and the second doesn't make sense because Mojarra boots even in a Servlet container.
I finally discovered the secret while trying to get JSFUnit to work in Jetty from an Arquillian test. Mojarra wasn't loading. I switched to MyFaces and it also failed to start. But this time, I got an interesting error message along with it:
java.lang.IllegalStateException: No Factories configured for this Application. This happens if the faces-initialization does not work at all - make sure that you properly include all configuration settings necessary for a basic faces application and that all the necessary libs are included. Also check the logging output of your web application and your container for any exceptions!
If you did that and find nothing, the mistake might be due to the fact that you use some special web-containers which do not support registering context-listeners via TLD files and a context listener is not setup in your web.xml.
Aha! That's the ticket. The JSF implementations are using a TLD file in the JSF implementation to register the listener so that it doesn't need to be setup in web.xml!
That prompted me to take a look at the JavaDoc for MyFaces' StartupServletContextListener. There, the full strategy is laid out.
This context listener is registered by the JSP TLD file for the standard JSF "f" components. Normally, servlet containers will automatically load and process .tld files at startup time, and therefore register and run this class automatically.
Some very old servlet containers do not do this correctly, so in those cases this listener may be registered manually in web.xml. Registering it twice (ie in both .tld and web.xml) will result in a harmless warning message being generated. Very old versions of MyFaces Core do not register the listener in the .tld file, so those also need a manual entry in web.xml. However all versions since at least 1.1.2 have this entry in the tld.
That prompted me dig into the documentation for the listener element in TLD files. I learned that this capability was first introduced in JSP 2.0, part of Java EE 5. The Java EE 5 tutorial explains the function of the listener element as follows:
A tag library can specify some classes that are event listeners. The listeners are listed in the TLD as listener elements, and the web container will instantiate the listener classes and register them in a way analogous to that of listeners defined at the WAR level. Unlike WAR-level listeners, the order in which the tag library listeners are registered is undefined. The only subelement of the listener element is the listener-class element, which must contain the fully qualified name of the listener class.
The reason JSF wasn't loading in my Arquillian JSFUnit test was because I inadvertently left out the TLD processor (TaglibConfiguration) from the Jetty context in the Arquillian container adapter. I guess I now have motivation to add it back in ;)
So now you know.