OmniFaces CombinedResourceHandler Gives Your Application a Boost

Posted on Posted in OmniFaces, Performance, web design


OmniFaces 2.1 is soon to be released. Time to present a useful OmniFaces component which has been improved in version 2.1: the CombinedResourceHandler.

How does the CombinedResourceHandler boost your application?

These days, the average web page has a size of 800 kilobytes, and figures are still rising. Unfortunately, such a web page isn’t a single file, but a collection of HTML files, CSS files, Javascript files and images. Thing is, the browser can’t display anything until every necessary resource file has been loaded. Adding insult to injury, the files aren’t loaded simultaneously, but sequentially. Most modern browsers load up to eight files in parallel. If your web page consists of nine or more files, page load times increase significantly.

Modern JSF pages tend to consist of much more than 9 files. Typically 30 resource files are loaded. I’ve also seen a Liferay application loading 130 files on each request, give or take a few. Granted, most resource files are cached by the browser, but even so that’s a lot of network traffic. Remember, these 130 files are requested in chunks of eight files at a time, so that’s 17 turnaround cycles.1 You won’t want to use such an application on a cellphone, especially if you’re suffering from a slow network connection abroad.

OmniFaces to the rescue

HTTP/2, the next-generation internet protocol, is going to deal much more efficiently with scattered resource files. However, HTTP/2 still is in an early stage, so for the next couple of years we have to find another solution. The most common approach is to combine CSS files and Javascript files into one big file each. These big files take more time to load than each individual small file, but they are faster than loading the whole collection of small files. The CombinedResourceHandler of OmniFaces cares about that. In most cases, all you have to do is to add OmniFaces to your project and add three lines to the faces-config.xml file of your JSF project:

<dependency>
  <groupId>org.omnifaces</groupId>
  <artifactId>omnifaces</artifactId>
  <version>2.1</version>
  <scope>compile</scope>
</dependency>
 <application>
     <resource-handler>org.omnifaces.resourcehandler.CombinedResourceHandler</resource-handler>
 </application>
compile 'org.omnifaces:omnifaces:2.1'

Note that at the time of writing the final version hasn’t been released yet. Currently, you have to use version 2.1-RC1 instead of 2.1.

Now OmniFaces replaces every CSS file of your JSF page by a single, combined CSS files. It also combined the Javascript files with target head. That’s not the default, so you have to move the Javascript files to the page header if you haven’t already done so:

<h:head>
     <h:outputScript name="script.js" target="head" />
 </h:head>

You can exclude certain Javascript files from being combined by omitting the head target. That’s important because many Javascript libraries are very squeamish with respect to load order and timing.

Developer mode

Don’t be surprised if you don’t see an effect on your development machine: by default, the CombinedResourceHandler is disabled in development mode. Make sure to set the context parameter to Production in your application’s web.xml to see the effect:

<context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>    
    <param-value>Production</param-value>
</context-param>

Alternatively, you can activate or deactivate the feature by adding the context parameter org.omnifaces.COMBINED_RESOURCE_HANDLER_DISABLED to the web.xml. The parameter allows for EL expressions, so you can control the resource handler even programmatically, if you wish so:

<context-param>
     <param-name>org.omnifaces.COMBINED_RESOURCE_HANDLER_DISABLED</param-name>
     <param-value>#{someApplicationScopedBean.someBooleanProperty}</param-value>
 </context-param>

Tweaking the CombinedResourceHandler

There’s an exhaustive documentation at the OmniFaces showcase, so I’ll cover the configuration only briefly here:

You can exclude arbitrary Javascript and CSS files from being combined by adding a context parameter. This way, they can remain in the head target, and you can exclude resource files added by third-party libraries, such as PrimeFaces. You can even define if the excluded files are to be added before or after the combined file.

Starting with OmniFaces 2.1, the combined resource file can by cached on the server side. That’s an application-scoped in-memory cache unless you configure it otherwise. Hence, server side caching of the combined resource is deactivated by default. That’s a sensible setting because the cache might become huge. The server side cache trades memory for CPU cycles. This may or may not be a good decision, depending on the circumstances, so it’s up to you. You activate the server side cache by setting the context parameter org.omnifaces.COMBINED_RESOURCE_HANDLER_CACHE_TTL to a numeric value greater than zero. The number is the number of seconds the files are held in the cache. In most cases, resource files are static, so it may be a good idea to set it to a high number, such as 86400 (i.e. one day).

CombinedResourceHandler uses a global cache of OmniFaces. Among other things, the configuration of the global cache influences the maximum number of files to be combined. Read the full documentation at the OmniFaces showcase. At the time of writing, the documentation of the 2.1 snapshot was much more exhaustive, so you may want to read the snapshot documentation until OmniFaces 2.1 has been released.

BootsFaces and AngularFaces support

We went into great lengths to optimize BootsFaces for the CombinedResourceHandler. Most resource files required by BootsFaces and AngularFaces are automatically moved to the head target. As a rule of thumb, I generally recommend to use BootsFaces with the CombinedResourceHandler. Among other things, this fixes a weird problem concerning the GLYPHICON and Font Awesome icon sets.

Unfortunately, the caching version of the CombinedResourceHandler didn’t make it into the 1.x branch of OmniFaces. Using OmniFaces 2.1, in turn, means you have to add CDI to your tool chain. Alternatively, you can backport the feature to OmniFaces 1.x by adding a couple of classes to your project. There’s a brief description how to do it on the BootsFaces showcase.

Kudos

I’d like to thank the OmniFaces team for accepting my pull request which added server side caching to OmniFaces. By the way, it was issue #100 – sort of a magic number :).


Dig deeper

Documentation of the OmniFaces CombinedResourceHandler (version 2.0 at the time of writing)
Documentation of the OmniFaces cache (version 2.0 at the time of writing)
Documentation of the OmniFaces CombinedResourceHandler (Developer preview)
Documentation of the OmniFaces cache (Developer preview)

Andy Davies on how to adapt to HTTP/2


  1. That’s a simplified explanation to bring the point home. In reality, browsers are more efficient than my text implies.

Leave a Reply

Your email address will not be published.