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

 Articles & Books 
Speed up your Data-Driven JSF/Seam Application by Two Orders of Magnitude – Part 2
Speed up your Data-Driven JSF/Seam Application by Two Orders of Magnitude – Part 2
by Dan Allen
27 Mar 2009 01:30 EDT

In the second installment of this two-part article, Dan Allen continues his discussion of 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.

Components and control (or lack thereof)

If you have components in the data table that generate embedded JavaScript, there isn't much you can do except plead with the author of the component library to keep the encoded output terse. This is unfortunately one of the areas of control that you relinquish by choosing to use a component-based approach. My advice here is to submit patches to correct waste or use your own components.

I did discover one area in Ajax4jsf where you can cheat. Previously, I recommended using <a:commandLink> for defining Ajax interactions. But in Ajax4jsf, it's possible to turn any JavaScript event on the page into an Ajax request using the <a:support> tag, making it a drop-in replacement for <a:commandLink>. And it just so happens that the JavaScript that the <a:support> tag produces is smaller than the JavaScript generated by the <a:commandLink>. Here's how the edit link would look using <a:support> rather than <a:commandLink>:

<h:graphicImage id="e" value="/i/e.png" alt="Edit" title="Edit" rendered="#{_item != conversationScope.get('itemInEditMode')}"> <a:support id="ec" event="onclick" action="#{benthicMsmntEditor.editItem}" reRender="dataTable" ajaxSingle="true"/> </h:graphicImage>

Not only does switching to <a:support> reduce the size of the response, but it also requires less processing on the server, according to my tests.

Curbing the expressiveness of CSS

RichFaces uses a well-designed approach to styling components by relying heavily on CSS. This design allows for styles to be extracted into external stylesheets and shared by all instances of a component. However, the creators of RichFaces went a bit overboard by using two style classes per component, an internal name and a public name, and making the name of these classes excessively long. As an example, here is a table row with one cell shown in a RichFaces data table.

<tr class="dr-table-firstrow rich-table-firstrow "> <td id="dc:dt:1:tx" <class="dr-table-cell rich-table-cell">Diptera </td> </tr>

You need some style classes or else your page is going to look pretty plain, but this is a tad overkill. The solution here is to disable the public class name and just reference the internal class name in your custom stylesheet. After all, the first word in CSS is cascading, which means you can override the styles that RichFaces associates with a class. There is no need for two style classes. To apply this optimization, you have to alter the RichFaces UI source files to remove the appending of the public class name, and recompile the library. If you are patient, you can wait for a future release of RichFaces which will likely implement this optimization (my guess is that the public class will remain and the internal class removed).

Overall, our changes have significantly reduced the size of the response. But there is one major outstanding issue remaining. If you crack open FireBug and inspect the response of one of the Ajax requests, you will see the following markup in the <head> of the response:

<head><link class="component" rel="stylesheet" type="text/css" href="/edas2-perflab/a4j/s/3_2_2.GAcss/toolBar.xcss/DATB/eAGbfKFwr7PCCWUAE8YD4A__" /><script type="text/javascript" src="/edas2-perflab/a4j/g/3_2_2.GAorg.ajax4jsf.javascript.PrototypeScript"></script><script type="text/javascript" src="/edas2-perflab/a4j/g/3_2_2.GAorg.ajax4jsf.javascript.AjaxScript"></script><script type="text/javascript" src="/edas2-perflab/a4j/g/3_2_2.GAscripts/menu.js"></script><link class="component" rel="stylesheet" type="text/css" href="/edas2-perflab/a4j/s/3_2_2.GAcss/dropdownmenu.xcss/DATB/eAGbfKFwr7PCCWUAE8YD4A__" /><script type="text/javascript" src="/edas2-perflab/a4j/g/3_2_2.GAorg/richfaces/renderkit/html/scripts/utils.js"></script><script type="text/javascript" src="/edas2-perflab/a4j/g/3_2_2.GAorg/ajax4jsf/javascript/scripts/form.js"></script><script type="text/javascript" src="/edas2-perflab/a4j/g/3_2_2.GAorg/richfaces/renderkit/html/scripts/form.js"></script><link class="component" rel="stylesheet" type="text/css" href="/edas2-perflab/a4j/s/3_2_2.GAcss/menucomponents.xcss/DATB/eAGbfKFwr7PCCWUAE8YD4A__" /><link class="component" rel="stylesheet" type="text/css" href="/edas2-perflab/a4j/g/3_2_2.GAorg/richfaces/renderkit/html/css/msg.css" /><link class="component" rel="stylesheet" type="text/css" href="/edas2-perflab/a4j/g/3_2_2.GAorg/richfaces/renderkit/html/css/msgs.css" /><script type="text/javascript" src="/edas2-perflab/a4j/g/3_2_2.GAorg.ajax4jsf.javascript.ImageCacheScript"></script><script type="text/javascript" src="/edas2-perflab/a4j/g/3_2_2.GAorg/richfaces/renderkit/html/scripts/browser_info.js"></script><script type="text/javascript" src="/edas2-perflab/a4j/g/3_2_2.GAscripts/simpleTogglePanel.js"></script><link class="component" rel="stylesheet" type="text/css" href="/edas2-perflab/a4j/s/3_2_2.GAcss/simpleTogglePanel.xcss/DATB/eAGbfKFwr7PCCWUAE8YD4A__" /><script type="text/javascript" src="/edas2-perflab/a4j/g/3_2_2.GAorg/richfaces/renderkit/html/scripts/jquery/jquery.js"></script><script type="text/javascript" src="/edas2-perflab/a4j/g/3_2_2.GAorg/richfaces/renderkit/html/scripts/available.js"></script><script type="text/javascript" src="/edas2-perflab/a4j/g/3_2_2.GAorg.ajax4jsf.javascript.SmartPositionScript"></script><script type="text/javascript" src="/edas2-perflab/a4j/g/3_2_2.GAorg/richfaces/renderkit/html/scripts/scriptaculous/effects.js"></script><script type="text/javascript" src="/edas2-perflab/a4j/g/3_2_2.GAscripts/suggestionbox.js"></script><link class="component" rel="stylesheet" type="text/css" href="/edas2-perflab/a4j/s/3_2_2.GAorg/richfaces/renderkit/html/css/suggestionbox.xcss/DATB/eAGbfKFwr7PCCWUAE8YD4A__" /><script type="text/javascript" src="/edas2-perflab/a4j/g/3_2_2.GAscripts/comboboxUtils.js"></script><script type="text/javascript" src="/edas2-perflab/a4j/g/3_2_2.GAscripts/combolist.js"></script><script type="text/javascript" src="/edas2-perflab/a4j/g/3_2_2.GAscripts/inplaceinput.js"></script><script type="text/javascript" src="/edas2-perflab/a4j/g/3_2_2.GAscripts/inplaceselectlist.js"></script><script type="text/javascript" src="/edas2-perflab/a4j/g/3_2_2.GAscripts/inplaceselect.js"></script><link class="component" rel="stylesheet" type="text/css" href="/edas2-perflab/a4j/s/3_2_2.GAcss/inplaceselect.xcss/DATB/eAGbfKFwr7PCCWUAE8YD4A__" /><script type="text/javascript" src="/edas2-perflab/a4j/g/3_2_2.GAorg/richfaces/renderkit/html/scripts/data-table.js"></script><link class="component" rel="stylesheet" type="text/css" href="/edas2-perflab/a4j/s/3_2_2.GAcss/table.xcss/DATB/eAGbfKFwr7PCCWUAE8YD4A__" /><link class="user" rel="stylesheet" type="text/css" href="/edas2-perflab/a4j/s/3_2_2.GAextended-skin-styles.xcss/DATB/eAGbfKFwr7PCCWUAE8YD4A__" /><link class="user" rel="stylesheet" type="text/css" href="/edas2-perflab/stylesheet/theme.css" /></head>

Whoa! What's with all of those JavaScript and CSS includes?? Again, this delves deeply into how RichFaces is implemented. RichFaces bundles CSS and JavaScript with each component and includes them in any page that uses that component. Since JSF allows the component tree to be modified dynamically in Java, it's possible for the components that appear on the page to be changed during a postback. That means new resources may be needed. To accommodate this scenario, RichFaces sends the stack of CSS and JavaScript includes with each response so that the page can be updated as needed. (Truthfully, RichFaces could be optimized to send just the new resources).

As mentioned earlier, HTTP connections are costly and this piecemeal approach is not in tune with performance concerns. To get around this problem, RichFaces can pack all the JavaScript and CSS in its component set and deliver them in two requests on the very first page in the application and ensure that the browser caches them on all future requests. To enable this feature, add the following two context parameters to the /WEB-INF/web.xml file:

<context-param> <param-name>org.richfaces.LoadStyleStrategy</param-name> <param-value>ALL</param-value> </context-param> <context-param> <param-name>org.richfaces.LoadScriptStrategy</param-name> <param-value>ALL</param-value> </context-param>

Frankly, you would be crazy not to use these settings in a production environment. Here's the <head> tag that RichFaces rewards you with on an Ajax request.

<head> <link class="user" rel="stylesheet" type="text/css" href="/edas2-perflab/a4j/s/3_2_2.GAextended-skin-styles.xcss/DATB/eAGbfKFwr7PCCWUAE8YD4A__" /> <link class="user" rel="stylesheet" type="text/css" href="/edas2-perflab/stylesheet/theme.css" /> </head>

The only reason the <head> tag contains anything at all now is because we are using two custom stylesheets in our application. RichFaces doesn't roll up custom resources into its pack and thus doesn't exclude them. (It's possible that there's a way to get RichFaces to stop including custom stylesheets in an Ajax response, but I haven't figured it out yet).


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.