JSTL with JSF/Facelets

October 18, 2008

In my years of JSF development, especially with respect to Facelets, I have come to both love and hate JSTL. JSTL without Facelets is even more of a nightmare and I would strongly recommend considering otherwise. JSTL with Facelets is a little better, but still requires a full understanding of how Facelets interacts with JSF and JSTL. Without a full understanding, you will most definitely run into unforeseen issues.

First, let’s quickly review the JSF lifecycle. First, restore view will restore a saved UIViewRoot from a postback request. Second, it will apply the request parameters to the input components, perform conversions and validations, update the backing model with the converted values, and invoke actions and listeners accordingly. Finally, it will render the requested view or outcome view.

Now let’s look at Facelets. Facelets defines two different lifecycles: the compile lifecycle and the JSF lifecycle. The compile lifecycle is used to compile a given Facelet and then apply the Facelet to a UIViewRoot in order to construct the component tree. The JSF lifecycle is then activated using the built component tree from the Facelets. Technically the compile phase in Facelets is either part of the restore view phase or the render phase (depending on the facelets configuration and request type). Either way, the compile time phase happens completely before invoking the remainder of the JSF phase. Thus, it is more or less completely independent of each other. The sole purpose of the compile phase is to build the component tree from the Facelet tags. This is similar to how a JSP tag handler creates a UIComponent and adds to the component tree. However, whereas JSP is completely independent of JSF, Facelets is built very well into JSF.

So, why are these phases important? They are because Facelet tags are either TagHandlers (compile-time invocations) or ComponentHandlers (JSF invocations). ComponentHandlers are used to create a UIComponent from the tag and add to the JSF component tree. As a result, the created component is processed as part of the JSF lifecycle (validations, rendering, etc). However, TagHandlers are processed during the compile time phase and do not create components. They merely process some logic that may affect how other handlers get processed. However, they are processed before the JSF rendering lifecycle, which has some big repercussions as I will discuss later.

So what does this have to do with JSTL? Facelets implements JSTL using TagHandlers (yes, Facelets does…it does not use the standard JSTL library which is purely JSP based, not JSF). Thus, every JSTL tag is processed at compile time, not at render time within the JSF lifecycle. This means you need to know how each of the tags operates and how it impacts the component tree. After all, the primary purpose of tag handlers is to affect or build the component tree. It does not have any part in the JSF rendering phase.

For example, the c:if tag is used to apply children handlers when its expression evaluates to true. This means that the expression MUST be able to be evaluated before the restoring or rendering phases. Typically this is not an issue as expressions are available at all lifecycles as they are bound to managed beans and backing beans. The case where this breaks down is when you have components creating variable expressions during the rendering phase. An example of this is the commonly used “var” expression within the h:dataTable. The “var” expression is only valid when rows are being rendered by the data table. Because the if tag is processed before the rendering phase, its expressions would never properly evaluate. This example would always cause unexpected results:

<h:dataTable var="row" value="#{MyBean.data}">
<c:if test="#{row.valid}">
Hello: #{row.name}

You can see how this can be confusing, especially for new developers. If you do not understand the two lifecycles between JSTL and JSF/Facelets, you could spend hours trying to figure out why your data table is not generating content accordingly.

Let’s look at some other examples. One of my favorites is the c:forEach tag. This tag processes a given set of children tags for each iteration. This would seem to mimic the behavior of the dataTable tag. However, the two are very much different. The forEach tag is processed during the compile time phase while the dataTable is processed within the JSF lifecycle. So, why does that matter? Apart from the expressions having to be available as I already discussed, it matters severely to the component tree and memory/performance utilization. When the forEach tag is processed/applied, it applies each of its children for every iteration provided. This means that if the children tag handlers created component instances, then they would create X instances where X is the number of iterations. Let’s look at an example:

<c:forEach var="row" items="#{MyBean.data}">
&lth:outputText value="#{row.name}" />

If the MyBean.data expression resulted in 100 values, then you would have 100 HtmlOutputText components in the tree. However, if you used an h:dataTable tag, then you would only have 2 components in the tree. That does not seem all that bad. However, imagine the case where you have a large data set (ie: thousands of rows) and you had several children components. That would result in tens of thousands of components. After serializing all of those components to the state, your state will be thousands of times larger (megabytes rather than kilobytes). This can cause long access times, long download times, etc. Again, not understanding how JSTL interacts with its different phases, can quickly kill an application with unexpected results.

Now that I have showed how JSTL can be bad, let me provide one case where JSTL is very powerful (however, confusing at the same time to newbies). Let’s look at the h:panelGrid component. This component has a “columns” attribute that defines how many columns (ie: td) to create. Each child will represent one of those columns. However, imagine you want to use a panel grid but the column components you want are based on a data list that you would want to iterate over or are based on the same data that you want to avoid typing over and over (for maintenance reasons). Using panel grid with data table will not work as you would only have a single component child (the data table). Howver, let’s look at what forEach did to our component tree: it created a component instance for every child. Thus, we get a proper handling in the panel grid. For example:

<h:panelGrid columns="3">
<c:forEach var="index" start="1" end="6">
Index: #{index}

In looking at this you would think that it would fail because you only have a single child when you expect 3 columns. When you understand JSTL within Facelets, you realize the power and how it actually creates a 3×2 grid numbered from Index 1 to Index 6. This is because the end component tree has 6 panel group components as immediate children of the panel grid. Therefore, the panel grid is able to iterate over those panel group components accordingly.

The big point to remember to all of this is understand, understand, and understand the different lifecycles and their impact on each other. Without the understanding, you can quickly become confused on why things are not working as expected. First and foremost, understand how the JSF component tree is impacted and second how EL expressions are evaluated (especially with respect to c:set). A good suggestions is to use the ui:debug tag in Facelets and examine the output component tree to see how it is impacted after compiling/evaluating a Facelet.