Imagine a bacon-wrapped Ferrari. Still not better than our free technical reports.
See all our reports

Detecting JSF Session Bloat Early with XRebel

The development of complex interactive web applications inherently requires you to store state in one location or another (on the server, on the client, in a database, …). Using JSF, the web session preserves the state of the container managed beans by taking your scope rules into account. Since this is conveniently tied to regular Java classes and their fields, it’s very easy to accidentally grow the session without really realizing just how large the memory footprint has become.

For example, let’s take a look at this simple file upload form that I was experimenting with:

Upload an XML file:

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html">
    <h:head>
        <title>File Upload</title>
    </h:head>
    <h:body>
        <h:form id="form" enctype="multipart/form-data" prependId="false">
            <p>Upload an XML file:</p>
            <h:inputFile value="#{uploadBean.file}"></h:inputFile><br />
            <h:commandButton value="Go" action="#{uploadBean.upload}"></h:commandButton>
            <p id="textOutput">#{uploadBean.msg}</p>
        </h:form>
    </h:body>
</html>

With the HTML view done, I moved onwards and coded the processing of the uploaded file. When I tested it out however, XRebel alerted me with the following warning:

xrebel-jsf-session-bloat1

Wooow, what’s this! A massive session increase of 8MB in a single request!

Using XRebel’s session explorer, I drilled down to identify where that size increase came from. It shows very clearly that the session stored the contents of the file through a field from the UploadBean instance.

xrebel-jsf-session-bloat2

Armed with the information from XRebel, I took a look at my code and it immediately became apparent what I did wrong.

Here are the sources with suggested fixes inlined as comments:

@ManagedBean
@ViewScoped  // hmmm, this would better be @RequestScoped
public class UploadBean {

    private Part file;
    private String msg;
    private String content;  // damn, using a field was not a good idea

    public void upload() throws IOException {
        if (null != file && file.getSize() > 0) {
            msg = "Uploaded " + file.getSubmittedFileName();
            
            StringWriter writer = new StringWriter();
            IOUtils.copy(file.getInputStream(), writer, "UTF-8");
            content = writer.toString();
            if (content.contains("<?xml")) {
                msg += ", thanks.";
            } else {
                msg += ", but it is not an XML file.";
            }
        }
    }

    public UploadBean() {}
    public String getMsg() { return msg; }
    public void setMsg(String msg) { this.msg = msg; }
    public Part getFile() { return file; }
    public void setFile(Part file) { this.file = file; }
}

Since XRebel was running as an always-on interactive profiler, I immediately saw that my code exploded the session size. A very embarrassing production scalability problem was luckily averted early on, right when the root cause was created and, I learned a valuable lesson for the next time!

If you’re curious to try this out yourself, don’t hesitate to download XRebel and use it with the packaged NetBeans example project.


TRY XREBEL FREE