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.

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.

Resources

Dan Allen is a Senior Software Engineer at Red Hat and author of Seam in Action. He has over eight years of development experience using technologies that include Java frameworks (Seam, JSF, EJB3, Hibernate, Spring, Struts), testing frameworks (JUnit, TestNG), JavaScript and DOM scripting, CSS and page layouts, Maven 2, Ant, Groovy, and many others.

After graduating from Cornell University with a degree in Materials Science and Engineering in 2000, Dan became captivated by the world of free and open source software, which gave him his debut in software development. He soon discovered the combination of Linux and the Java EE platform to be the ideal blend on which to build his professional career, with his interests equally divided between the two platforms.

Dan is a member of the Seam project, a dedicated open source advocate, and a Java blogger. He lives with his extremely supportive wife in Laurel, MD. You can keep up with Dan's development experiences by subscribing to his blog at http://mojavelinux.com.

Copyright (C) 2003-2014 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.