Dennis has posted 2 posts at DZone. View Full User Profile

JSF Anti-Patterns and Pitfalls

12.12.2008
| 41909 views |
  • submit to reddit

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.

Published at DZone with permission of its author, Dennis Byrne.

(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

Using JSF is itself an anti-pattern.

Manrico Corazzi replied on Tue, 2008/12/16 - 9:11am

So it turns out I'm not the only one utterly disliking JSF... :)

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: Bruce Fancher

Wich alternatives do you propose that gets same tool support and ajax integration? I'm looking to speed-up time-to-develop projects and I really want to know about the alternatives to JSF-Ajax frameworks. Hope anyone could give a hand here.

Dzmitry Churbanau 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"? :)

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.