JSF Central - Speed up your Data-Driven JSF/Seam Application by Two Orders of Magnitude - Part 1
JSF Central

 Articles & Books 
Speed up your Data-Driven JSF/Seam Application by Two Orders of Magnitude
Speed up your Data-Driven JSF/Seam Application by Two Orders of Magnitude - Part 1
by Dan Allen
05 Feb 2009 03:00 EST

In the first of this two-part article, Dan Allen discusses some common performance problems you may encounter when using JSF components, Seam components, and the EL. You'll learn about the set of best practices for eliminating them that led to an improvement of two orders of magnitude in the performance of his application.

When you call an intercepted method once, you would never notice the impact of the interceptors. However, when you call the method 5200 times, the time spent in the interceptors adds up. How much of a difference does it make and what other options do we have?

To determine the impact, I timed both the rendering of the entire page and the rendering of the data table region, as described in the introduction. The data table region consists of the data table and the pagination controls and summary information for the table. A test of 4 requests on 50 rows (2600 calls) produced these timing results:

Stage 1 timing results (50 rows)
Request Elapsed time of request (ms) Time to render table (ms)
1 6330 6090
2 6340 6096
3 6400 5883
4 6100 5850
avg 6292.4 5979.8

Not many people are going to stick around for a page that takes 6 seconds to render (in the best case scenario), and that doubles for 100 rows. The trick is to outject the selected row so that the comparison can be done without having to invoke a Seam component. Outjection is a mechanism in Seam that takes the value of a JavaBean property on a component and assigns it directly to the name of a variable in the specified scope (such as the conversion scope). You outject a property by annotating it with the @Out annotation, as shown here:

@Out(required = false) private T itemInEditMode;

(For readers with Seam experience, there is a reason why you cannot simply add @BypassInterceptors to the editing() method, which I will provide in a moment.)

Now we can check if the row is in edit mode by comparing the iteration variable in the data table to the outjected property using the following EL expression in the view:

"#{item == itemInEditMode}"

Here's how things improve after making this change:

Stage 2 timing results (50 rows)
Request Elapsed time of request (ms) Time to render table (ms)
1 904 663
2 807 608
3 813 569
4 823 592
avg 836.8 608

Less than one second is certainly a nice place to be. We can do better, but let's first focus on the 5 second discrepancy because it is a cause of concern regarding Seam's performance.

The truth is, interceptors come with a cost. Again, this cost only adds up when you are pounding the component, like the rendered attribute does. Unfortunately, that is more of a limitation (and a fact of life) in the way that the data table component in JSF was designed. On the other hand, that is why Seam provides the @BypassInterceptors annotation. This annotation is intended to be used on methods that read the state of a component, as opposed to a method with behavior. After all, Seam is a stateful framework and espouses using objects as they were intended, to have behavior and state.

So why not just add @BypassInterceptors to the editing() method to reduce the overhead of invoking it? Theoretically that would work. The only problem is that Seam relies on interceptors to restore the state of conversation-scoped components, at least in Seam 2.0. In Seam 2.1, this behavior is disabled by default, so you could just add @BypassInterceptors to the method. However, if you plan to use stateful session beans (SFSBs) in your application or run the application in a cluster, you will need to enable the behavior I am about to describe, so it's important to understand why interceptors on conversation-scoped components are important.

Seam's managed entity interceptor

Seam 2.0 uses an interceptor that aids with maintaining the object identity of persistent objects (managed entities) across passivation of a SFSB, or when components jump nodes in a cluster. This interceptor has good intentions, but can have bad consequences. At the end of a method call on a conversation-scoped component, the interceptor transfers the values of all fields holding references to entity instances directly into the conversation context, and then nullifies the values of those fields. It reverses the process at the beginning of the next call. The consequence is that without interceptors, your conversation-scoped object is missing state. Eeek! What's worse is that the performance of the interceptor is naturally challenged if your component happens to be storing large data sets, because it takes time for the interceptor to perform its work. It's an unfortunate sacrifice in order to achieve transparent replication.

Rather than worrying about whether to use this interceptor, or locking the design of your application into its absence, it's best just to avoid the need to disable interceptors. Besides, there are still other interceptors that may need to be enabled on a component's methods (its all or nothing when you disable them) and working with outjected data is the fastest approach anyway. You can feel comfortable calling methods on Seam components in other areas of your page, but you should avoid doing so inside of a data table, which brings us to our second lesson.

Lesson #2: Don't call intercepted methods inside a data table (or in excess)

The question is, have we done all that we can do to optimize? Not even close. There is another important lesson to learn, and this one has to do with the EL, or more specifically, the EL resolver mechanism.

Resolving variables efficiently

In JSF, the view is bound to server-side components using a syntax known as the Unified Expression Language (EL). The root of an EL string (e.g., #{itemInEditMode}) is presumed to be a variable in one of the available web application scopes, or the name of a component that must be created (such as a JSF managed bean or Seam component). The name resolution is handled by the EL resolver chain, which is a collection of objects that know how to locate or create objects that map to a name. All resolvers in the chain are consulted until the end of the chain is reached or a value is found. This lookup happens a tremendous number of times while rendering a JSF view, especially while rendering a data table. Thus, it's a potential source of performance problems.


RSS feed(all feeds)

The Editor's Desk
Inside Facelets
In the Trenches

Site version 1.83  Report web site problems

Copyright (C) 2003-2015 Virtua, Inc. All Rights Reserved. Java, JavaServer Faces, and all Java-based marks are trademarks or registered trademarks of Oracle Corporation. in the United States and other countries. Virtua, Inc. is independent of Oracle Corporation. All other trademarks are the sole property of their respective owners.