Dienstag, 22. Oktober 2013

Fixing the JSF error "ViewRoot is null" occuring exactly after 60 minutes when using MyFaces CODI although session timeout in web.xml is higher

Dear Readers!

I will do this post in English since this problem bugged me for a long long time and I couldn't find the solution on the web by searching for "ViewRoot is null".

Given:

You have JSF 2 web application using MyFaces CODI and you have a session timeout longer than 60 minutes defined in your web.xml (Why? Because the customer wants it!).

<session-config>
    <session-timeout>120</session-timeout> 
</session-config>

The problem:

After more than 60 minutes of inactivity - when you klick somewhere on your page - you get an ugly error stating "ViewRoot is null".

The solution:

It turns out that MyFaces CODI is the source of the problem - it defines a HARDCODED session timeout of 60 minutes.

How did I find out?

I started to download the source code of each library used in my web application and searched for the string "60" - strange, isn't it? But I was successful.

So how to fix this?

We need two classes to circumvent the problem using adavanced features of CDI. The first class uses the CDI Alternative and Specialization feature. It defines a customized WindowContextConfiguration returning a customized session timeout.

package at.coopxarch.bluprint.presentation.controller.cdi;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Alternative;
import javax.enterprise.inject.Specializes;
import org.apache.myfaces.extensions.cdi.core.api.config.ConfigEntry;
import org.apache.myfaces.extensions.cdi.core.api.scope.conversation.config.WindowContextConfig;
import at.coopxarch.bluprint.common.util.BluPrintConstants;

@SuppressWarnings("serial")
@ApplicationScoped
@Alternative
@Specializes

public class BluPrintWindowContextConfig extends WindowContextConfig {

    /**
     * Specifies the time for the timeout for a window. After a timeout is detected all beans 
     * which are only linked to the window will be destroyed.
     *
     * @return the time for the timeout for a window
     */
    @ConfigEntry
    public int getWindowContextTimeoutInMinutes()
    {
        return BluPrintConstants.SESSION_TIMEOUT_IN_MINUTES;
    }
}

The second class is a CDI producer to instantiate and inject our special WindowContextConfig.

package at.coopxarch.bluprint.presentation.controller.cdi;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;

public class BluPrintWindowContextProducer {

    @ApplicationScoped
    @Produces
    public BluPrintWindowContextConfig produceBluPrintWindowContextConfig() {

       return new BluPrintWindowContextConfig();
    }
}

The downside of the solution:

The downside of this solution is the fact that you have to define the session timeout in two places: in the web.xml and as a constant somewhere in the code. It is advisable to comment the web.xml "session-timout" section so in case of a change of the session timeout a coder can also change the second session timeout definition. But that - of course - is optional ;-)

Let's see if anybody replies back to me who has had the same problem...

Yours
JWR@coopXarch