There was a thread on the JSF LinkedIn group about JSF performance and a number of people complained about the fact that as part of the restore view phase, JSF reconstructs the component tree including binding data to data tables causing unnecessary fetches of data from the database. This article looks at one way of avoiding the additional work to fetch the data.

When you first do a postback, the restore view phase rebuilds the tree, including fetching the data to bind it to the data table. Why you ask, well a good question. Typically this wouldn’t be an issue since we want the server state to match the client view state. However, as the developer, we know that for some cases these results are useless since we don’t anticipate using them for anything so fetching them is really a waste of time.

Let’s take a look at this problem in action. You can use the maven archetype to generate the project and code along or download the source from here (fastapp.zip).

  1. Create a new maven application using the jee6-servlet-basic knappsack archetype. This gives you an empty Java EE 6 application that can be run using Jetty or Tomcat from the command line.
  2. Add a new class that will be our backing bean that returns a list of paginated numbers but takes a long time (2 seconds) to do it.
    @Named
    @RequestScoped
    public class DataBean {
    
    	//indicates the page we are on
    	private int pageNumber = 0;	
    	
    	//lazy loaded reference to the results
    	private List<Integer> data;
    	
    	//returns the list of data by lazy loading it
    	public List<Integer> getData() {
    		if (data == null) {
    			data = generateData();
    		}
    		return data;
    	}
    	
    	//generates the list of data, think of this as an expensive DB operation
    	private List<Integer> generateData()  {
    		System.out.println("Generating Data starting from "+pageNumber);
    		
    		//Sleep for 2 seconds while I access the database and do stuff
    		try {
    			Thread.sleep(2000);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		
    		//now we actually generate the data
    		List<Integer> results = new ArrayList<Integer>();
    		for (int i = 0;i < 10;i++) {
    			results.add(pageNumber+i);
    		} 
    		return results;
    		
    	}
    	
    	//method to move to the next page in the list
    	public void nextPage() {
    		pageNumber = pageNumber+10;
    		//invalidate the lazy loaded data
    		data = null;
    	}
    	
    	//method to move to the previous page in the list	
    	public void previousPage() {
    		pageNumber = pageNumber-10;
    		//invalidate the lazy loaded data
    		data = null;
    	}
    
    	//getter/setter for page number 
    	public int getPageNumber() {
    		return pageNumber;
    	}
    	public void setPageNumber(int pageNumber) {
    		this.pageNumber = pageNumber;
    	}
    }
    

    We lazy load the results so for each request, we start with nothing and then load the results when the restore view phase looks for them. In the render response phase, the same results are used unless the next/previous page methods are called and the data value is set to null and invalidated. In this case when the data is requested in the render response phase, we see it is null and so it will be loaded fresh for the new page number.

  3. Now we modify the existing home.xhtml page to show a table and add two buttons to move to the previous and next pages.
    <ui:define name="content">
    
    	<h:dataTable value="#{dataBean.data}" var="v_value">
    		<h:column>
    			<f:facet name="header">Value</f:facet>
    			#{v_value}
    		</h:column>
    	</h:dataTable>
    	<h:form>
    		<h:commandButton action="#{dataBean.previousPage}" value="Previous" />
    		Page #{dataBean.pageNumber} 
    		<h:commandButton action="#{dataBean.nextPage}" value="Next" />
    		<h:inputHidden value="#{dataBean.pageNumber}" />
    	</h:form>
    </ui:define>
    

    Fairly straightforward, we just show the list of numbers and have buttons for navigating. We store the page number in a hidden field and post it back to our backing bean so we have a stateless setup. I also added a phase listener that I’ve used before to log the timing of the JSF requests that is documented in this post (Timing JSF Requests using a Phase Listener). Since this was for Seam originally I modified it by removing the logger code and just pushing the log text to System.out. You can turn it on and off by removing it from the faces-config.xml file in the lifecycle section.

  4. In a command console enter mvn clean jetty:run to start the server and go to the home page at http://localhost:8080/fastapp/home.jsf.

When you first go to the page, you trigger a GET request which will cause the data to be loaded one time and displayed. In our output log we see :

Generating Data starting from 0

Fantastic. At this point, we know the page took 2 seconds to render. If we click the next or previous buttons, we cause a postback which will restore the view (including rebuilding the data) and then change the page number which will invalidate the data before again rebuilding the data for the new page. We see this in the log as :

Generating Data starting from 0
Generating Data starting from 10

If you are using the phase listener to time it, you will see the two requests as :

//first request initiated
Executed Phase RESTORE_VIEW 1
Generating Data starting from 0
Execution Time = 2228ms
Executed Phase RENDER_RESPONSE 6

//click next page to trigger the postback and response
Generating Data starting from 0
Executed Phase RESTORE_VIEW 1
Executed Phase APPLY_REQUEST_VALUES 2
Executed Phase PROCESS_VALIDATIONS 3
Executed Phase UPDATE_MODEL_VALUES 4
Executed Phase INVOKE_APPLICATION 5
Generating Data starting from 10
Execution Time = 4116ms
Executed Phase RENDER_RESPONSE 6

You can see the first request took 2 seconds while the second request took over 4 seconds. The problem here is that the data is being fetched twice, once for the restore view and again for the rendering of the response with the new data as you can see in the phase listener log entries.

Solution

The solution to the problem is to skip fetching the data the first time altogether. It isn’t needed, it isn’t even used and it isn’t even the correct data (don’t worry, I’ll explain that later). In the FacesContext object there is a method called getRenderResponse() which returns true if the render response method has been called and we are in the rendering phase. Until we are rendering the page, we don’t really need our data so we only load it if this method returns true.

	//generates the list of data, think of this as an expensive DB operation
	private List<Integer> generateData()  {
		if (!FacesContext.getCurrentInstance().getRenderResponse()) {
			return null;
		}
		System.out.println("Generating Data starting from "+pageNumber);
		...
		...
		...
	}

It’s that simple, now redeploy the application load the page and click next, this time, we only get one data fetch per page request even on postbacks because we don’t bother loading the data until we really need it in the render response and if you look at the timing, our pages are loading twice as fast since we only fetch half the data.

//initial request
Executed Phase RESTORE_VIEW 1
Generating Data starting from 0
Execution Time = 2278ms
Executed Phase RENDER_RESPONSE 6

//click the next button to trigger a postback - look, no data loaded until the response is rendered
Executed Phase RESTORE_VIEW 1
Executed Phase APPLY_REQUEST_VALUES 2
Executed Phase PROCESS_VALIDATIONS 3
Executed Phase UPDATE_MODEL_VALUES 4
Executed Phase INVOKE_APPLICATION 5
Generating Data starting from 10
Execution Time = 2302ms
Executed Phase RENDER_RESPONSE 6

So there you go, you can increase the speed of your JSF pages by only loading data when you really need it, I guess kind of lazy lazy loading data.

What about that bug we are dodging?

Ah yes, that thing, not sure if it can be called a bug but to see the problem, remove the code to check for the render response so we do the double data request on postbacks. Now load up the application and click the next button twice, and you should see the following log :

//initial GET request, we only load the data once
Generating Data starting from 0

//Click next, trigger a postback with two data fetches
Generating Data starting from 0
Generating Data starting from 10

//Click next again, trigger a postback with two more fetches.
Generating Data starting from 0 --err, this isn't right it should be 10
Generating Data starting from 20

When we do the second postback, the first set of data fetched is starting from position 0. We are loading our data using the default pageNumber value of 0. So not only are we loading data we don’t need, we are loading the wrong data. This could have consequences if say you are in a search page and the postback triggers an unconstrained search which may take longer than one with search parameters set.

The reason for this is that the restore view process takes place before the request values are applied so the pageNumber is set to the default value of zero which makes the fetching of the data even more useless. I’m not sure it can be called a bug since the value hasn’t been set at the restore view phase The question is whether it is correct for the restore view phase to fetch data based on values that haven’t yet been initialized and if so, what’s the point?

A Feature?

You know, thinking about it, this might really be a feature, not a bug. It’s feasible to utilize the state of the pageNumber value to determine whether we can return the results if we change its type to Integer. We know that we don’t need to fetch the data if the page number is null because we haven’t hit the read request values stage so we must still be in the restore view stage and therefore don’t need any data.
The only problem comes with the initial setting of the value when you first enter the page, what sets it from null to zero initially. You’d have to fudge around with the getters and settings, plus it would be hard to display that initial page of results since it would always be empty.

The source code for this project is available from here (fastapp.zip). To run, simply unzip, and in the console, type mvn clean jetty:run or mvn clean tomcat:run and go to http://localhost:8080/fastapp/home.jsf.