Make your Primefaces app load faster with lazy loading

2014-08-14

Every web developer has experienced this before: the client wants to show "almost everything" on the same page, but is very unhappy when he opens the site and has to look at a blank page for more than a couple of seconds.

Nowadays, developers have the means to optimize a lot (and fix everything that broke in the process), but we still cannot cast a magic “load superfast” spell and get rid of all loading time. Can we do more?

  • Make your Primefaces app load faster with lazy loading

Originally posted at Webdev community

Every web developer has experienced this before: the client wants to show almost EVERYTHING1 on the same page, but is very unhappy when he opens the site and has to look at a blank page for more than a couple of seconds.

Nowadays, developers have the means to optimize a lot (and fix everything that broke in the process afterwards), but we still cannot cast a magic “load superfast” spell and get rid of all loading time. Can we do more?

See the difference!

Download the example app

The problem

This page takes forever to load, because we need to do very heavy work on the backend-side. Until that work has been finished, the page is not rendered, not even partially!

Loading a heavy application without remote command

What would we do if we did not use JSF and Primefaces?

If we would build the application with pure JavaScript, we could initially load our template only, and then use Ajax to load the heavy components when the page is already visible.

The application is not faster, but seems faster, because

  1. The user gets a immediate response. The annoying blank page disappears (almost) instantly.
  2. If the user just wants to click through to another page, he does not have to wait until the -in that case unnecessary- heavy components are loaded.

But too bad: we do not use pure JavaScript; we use JSF and Primefaces. We need to execute all our Java methods before the page can be loaded, due to the JSF lifecycle. Or do we?

Entering: Primefaces Remote Command

There is a way! Somewhere hidden in the depts of the Primefaces Showcase, you find “Remote Command”. This is a great but relatively unknown feature of Primefaces, which allows us to execute backing bean methods by calling them with JavaScript. Yes, you read that right: we can call Java methods with JavaScript. And it is really easy.

Download the example app

1) Add the rendered attribute to the parts you want to load with Ajax

How do we start? We do not want to render the heavy parts while opening the page. So, let us start with that.

First, we add a property shouldRender to our backing bean, which evaluates to false initially.

Do not forget to add a getter and setter!

Note that this bean should be viewscoped (or have a longer life), because we will update this property with Ajax later on.

@ManagedBean
@ViewScoped
public class ExampleWithRemoteCommandController {
    private boolean shouldRender = false;

    public boolean isShouldRender() {
        return shouldRender;
    }

    public void setShouldRender(boolean shouldRender) {
        this.shouldRender = shouldRender;
    }

    public String getHeavyLifting1() throws InterruptedException {
        Thread.sleep(2000);
        return "Pjoew, I need to work 2 seconds to calculate this part!";
    }
    public String getHeavyLifting2() throws InterruptedException {
        Thread.sleep(5000);
        return "Pjoew, I need to work 5 seconds to calculate this part!";
    }
}

In the xhtml-page, we add an attribute rendered to the tag surrounding the heavy component. The value is a reference to our shouldRender property. This means that the part will not be rendered at page load time, because shouldRender evaluates to false.

<h:outputText rendered="#{exampleWithRemoteCommandController.shouldRender}"
              value="#{exampleWithRemoteCommandController.heavyLifting1}" />

When we try this out, the page loads a lot faster than before. Ofcourse, we ripped out all the heavy lifting! But let’s make a deal: the initial page load time should not increase from now on, ok?

2) Add a remoteCommand tag and surround our lazy loaded page part with another component

When the page is fully loaded, we want to change our rendered attribute to true. But the rendered attribute is part of JSF, not of HTML. So, when the page is loaded, we are already past the JSF-rendering phase.

We cannot use Java either. We can only use Javascript, so… we should call Java methods with JavaScript? JavaScript can only, naturally, call other JavaScript methods. How could we possibly call a Java method in JavaScript?

With the tag p:remoteCommand, you can expose serverside logic to a JavaScript function. You define inside the <p:remoteCommand> tag which Java code you want to execute, and Primefaces will generate a JavaScript method and the necessary Java web services which can communicate with each other!

Notice the following:

  • You need to give the remote command a name. This will generate a JavaScript function with that same name. So, if you set name="xyz", you can call the Java code inside the tag with JavaScript-function xyz().
  • We want to set the shouldRender attribute to true, so that when we update the page part, the heavy module is rendered.
  • We wrap the container with the rendered attribute in a new component. This is the component we will update. It is not possible to update the component with the rendered attribute itself: at the time we try to update the component (=after the page loaded), it is not rendered (shouldRender is false)! You cannot update something that is not rendered!
  • We also added a spinner, which will be shown when the component starts to load, and will be hidden when the loading of the component is done.
<p:remoteCommand name="doHeavyLifting1"
                 update="heavyLifting1LazyLoadingContainer"
                 onstart="showLoadingSpinner('heavyLifting1LazyLoadingContainer')"
                 oncomplete="hideLoadingSpinner('heavyLifting1LazyLoadingContainer')">
    <f:setPropertyActionListener target="#{exampleWithRemoteCommandController.shouldRender}"
                              value="#{true}" />
</p:remoteCommand>

<h:panelGroup id="heavyLifting1LazyLoadingContainer">
    <h:outputText rendered="#{exampleWithRemoteCommandController.shouldRender}"
              value="#{exampleWithRemoteCommandController.heavyLifting1}" />
</h:panelGroup>

3) Add the JavaScript that calls our backing bean

We can execute the code in the p:remoteCommand tag by calling the name of the remote command as a function. This can be done anytime we like: on document ready, on clicking on an element, when a fancy introduction movie has been played, when you have reached the bottom of the page when you scoll down in a list2,…

$('document').ready(function() {
   doHeavyLifting1();
   doHeavyLifting2();
});

var showLoadingSpinner = function(id) {
  $('#' + id).addClass('loading');
};
var hideLoadingSpinner = function(id) {
  $(id).removeClass('loading');  
};

When you want to do your ajax calls on the document ready event (like in this example), you don’t need to code this in JavaScript. You can add attribute autoRun="true" to the remote command tag. Primefaces will generate the JavaScript for you.

<p:remoteCommand name="doHeavyLifting1"
                 update="heavyLifting1LazyLoadingContainer"
                 onstart="showLoadingSpinner('heavyLifting1LazyLoadingContainer')"
                 oncomplete="hideLoadingSpinner('heavyLifting1LazyLoadingContainer')"
                 autoRun="true">

Comparing the results

I paste the url in the address bar and press enter.

A comparison between loading a heavy application with and without remote command

Limits

I have encountered only one big limit using the Remote Command component: JSF does not handle parallel Ajax requests well. The serial order model is part of the JSF specification.3. As you see in the example gif, the left part needs to be loaded before the request for the right part has even been made. The silver lining: Primefaces handles this automatically.

Common mistakes

  • The <p:remoteCommand> tag should be included in the form. If you put it outside a form, it is not possible to process or anything inside that form.
  • Do not put the <p:remoteCommand> tag in the container that you will update. Doing that could cause an infinite loop.
  • We wrap the container with the rendered attribute in a new component. This is the component we will update. It is not possible to update the component with the rendered attribute itself: at the time we try to update the component (=after the page loaded), it is not rendered (shouldRender is false)! You cannot update something that is not rendered!
  • It is common practice to do initialization logic in a method with a @PostConstruct annotation. This delays the page load!
  • Since you are doing Ajax-requests, the backing bean should be ViewScoped (or have an even longer lifespan).

Further reading

  1. If you want to block some parts of your page until a component has loaded, you can use Primefaces BlockUI.

  2. You can pass parameters to your Java methods. This feature is out of scope for this article. Find more about this feature at the Primefaces documentation.

    The way you do this has changed since PrimeFaces 3.3!

Notes

1 I like to exaggerate, especially when it comes to frustrations.

2 Primefaces 5.0 introduces a new component for this: DataScroller.

3 As described in this thread.

Me and my son

Stijn Hooft

Software engineer, specialized in Java and web technologies

Mail LinkedIn Github