JSF Anti-Patterns and Pitfalls
The Déjà vu PhaseListener
One of the most active mailing lists for the Apache Software Foundation is users@myfaces.apache.org . It is a wonderful place for exchanging new ideas, trading technical solutions, and flame wars. After all these years, MyFaces remains an open source project where many of my teammates on the development team still interact with application developers. A classic problem on the mailing list is the infamous "Deja Vu PhaseListener Effect". If you are at a computer right now, google "MyFaces PhaseListeners twice" - you'll see.
Let's look at how this happens. First, PhaseListeners are registered in a JSF configuration file.
<lifecycle>
<phase-listener>net.dbyrne.PhaseListenerImpl</phase-listener>
</lifecycle>
Next, JSF configuration files are in turn specified via the javax.faces.CONFIG_FILES context parameter in the deployment descriptor.
<context-param>
<description>comma separated list of JSF conf files</description>
<param-name>javax.faces.CONFIG_FILES</param-name>
<param-value>
/WEB-INF/faces-config.xml,/WEB-INF/burns.xml
</param-value>
</context-param>
Why are the PhaseListeners firing twice? Because all JSF implementations automatically parse /WEB-INF/faces-config.xml, if it exists, as well as all files specified in the comma separated list of the javax.faces.CONFIG_FILES context parameter. When /WEB-INF/faces-config.xml is specified in the javax.faces.CONFIG_FILES context parameter it can be parsed twice. The PhaseListeners configured in this file are consequently registered twice at startup and invoked twice at runtime.
XML Fetishism
Let's be real. JSF is all about hundreds of lines of XML, not thousands. I once visited a project with a configuration file similar to the following. The "contact us" page was accessible from every page on the site and a separate action rule was used for each page.
<navigation-rule>
<from-view-id>/home.xhtml</from-view-id>
<navigation-case>
<from-outcome>contact_us</from-outcome>
<to-view-id>/contact.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/site_map.xhtml</from-view-id>
<navigation-case>
<from-outcome>contact_us</from-outcome>
<to-view-id>/contact.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/about_us.xhtml</from-view-id>
<navigation-case>
<from-outcome>contact_us</from-outcome>
<to-view-id>/contact.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<!-- continued ... -->
A global navigation rule was used to significantly reduce the size of the configuration file.
<navigation-rule>
<from-view-id>*</from-view-id>
<navigation-case>
<from-outcome>contact_us</from-outcome>
<to-view-id>/contact.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
XML Hell also provides a number of opportunities for runtime exceptions. Let's look at some examples.
<managed-bean>
<managed-bean-name>invalid</managed-bean-name>
<!-- misspelled class name throws ClassNotFoundException -->
<managed-bean-class>net.dbyrne.misspelled.Invalid</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<!-- one side of cyclical reference -->
<property-name>incorrect</property-name>
<value>#{incorrect}</value>
</managed-property>
</managed-bean>
<managed-bean>
<managed-bean-name>incorrect</managed-bean-name>
<managed-bean-class>net.dbyrne.validate.Incorrect</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<!-- other side of cyclical reference -->
<property-name>invalid</property-name>
<value>#{invalid}</value>
</managed-property>
</managed-bean>
Notice the misspelled class name and the cyclical reference between the two managed beans found in this configuration file. A JSF implementation is not required to warn you about either of these problems at startup, but both will result in a runtime exception.
<managed-bean>
<!-- first duplicate -->
<managed-bean-name>duplicate</managed-bean-name>
<managed-bean-class>
net.dbyrne.validate.FirstDuplicate
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<managed-bean> <!-- second duplicate -->
<managed-bean-name>duplicate</managed-bean-name>
<managed-bean-class>
net.dbyrne.validate.SecondDuplicate
</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
This problem was fixed in JSF 1.2 but for people still using 1.1 this causes all sorts of headaches that are real hard to debug. Most 1.1 implementations will silently ignore the first managed bean and simply load the second.
<managed-bean>
<managed-bean-name>requestScopeManagedBean</managed-bean-name>
<managed-bean-class>
net.dbyrne.RequestScopeManagedBean
</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>sessionScopeManagedBean</managed-bean-name>
<managed-bean-class>
net.dbyrne.SessionScopeManagedBean
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<!-- throws runtime exception -->
<property-name>requestScopeManagedBean</property-name>
<value>#{requestScopeManagedBean}</value>
</managed-property>
</managed-bean>
A JSF implementation must also throw an exception at runtime if it encounters a managed bean with a "subscoped" managed property. In the following example, the session scoped managed bean is not valid because it refers to a request scoped managed bean.
Many of these configuration pitfalls can be detected with a set of generic JUnit tests distributed as part of JBoss JSFUnit. You can also avoid some of these problems with annotations libraries found in Seam or Shale.
- Login or register to post comments
- 15227 reads
- Printer-friendly version
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)










Comments
Bruce Fancher replied on Mon, 2008/12/15 - 2:20am
Manrico Corazzi replied on Tue, 2008/12/16 - 9:11am
Marc Stock replied on Tue, 2008/12/16 - 2:06pm
+1 to Bruce Fancher
Asking about JSF pitfalls is like sticking your hand in a fire and asking, "What are the pitfalls of sticking your hand in fire?"
Omar Palomino S... replied on Thu, 2008/12/18 - 2:19pm
in response to: bf44704
nett_by replied on Mon, 2008/12/22 - 11:46pm
Bruce Fancher, Manrico Corazzi and Marc Stock: everybody can criticize (read write this is bad and so on) but not everybody can give any valuable reason to it.
So, what is your reason? Can you give any reason, or it is just "i don't like it"? :)