Handling ViewExpiredException on login page using a custom ViewHandler

Distortum picture Distortum · Aug 21, 2012 · Viewed 8.8k times · Source

In my web app, when the session expires and a user initiates a request (full page or AJAX), the user is redirected to the login page with a message that the session expired. The problem is, when the login page is left open long enough for the session to expire and the user attempts to login, the user is redirected to the login page with a message that the session expired.

I came across this solution, but it is for JSF 1.x and won't work with JSF 2. So I started building my own:

public class LoginViewHandler extends ViewHandler
{
    public LoginViewHandler(ViewHandler parent)
    {
        this.parent = parent;
    }

    @Override
    public Locale calculateLocale(FacesContext context)
    {
        return parent.calculateLocale(context);
    }

    @Override
    public String calculateRenderKitId(FacesContext context)
    {
        return parent.calculateRenderKitId(context);
    }

    @Override
    public UIViewRoot createView(FacesContext context, String viewId)
    {
        return parent.createView(context, viewId);
    }

    @Override
    public String getActionURL(FacesContext context, String viewId)
    {
        return parent.getActionURL(context, viewId);
    }

    @Override
    public String getResourceURL(FacesContext context, String path)
    {
        return parent.getResourceURL(context, path);
    }

    @Override
    public void renderView(FacesContext context, UIViewRoot viewToRender)
            throws IOException, FacesException
    {
        parent.renderView(context, viewToRender);
    }

    @Override
    public UIViewRoot restoreView(FacesContext context, String viewId)
    {
        UIViewRoot viewRoot = parent.restoreView(context, viewId);

        if (viewRoot == null && viewId.equals("/login.xhtml"))
        {
            parent.initView(context);

            viewRoot = parent.createView(context, viewId);
            context.setViewRoot(viewRoot);

            try
            {
                buildView(context, viewRoot); // Compile error!
            }
            catch (IOException e)
            {
                log.log(Level.SEVERE, "Error building view", e); 
            }
        }

        return viewRoot;
    }

    @Override
    public void writeState(FacesContext context) throws IOException
    {
        parent.writeState(context);
    }

    private ViewHandler parent;
}

However, there is no method called buildView. I'm not sure what it does, why it's needed and what I should replace it with. Any ideas?

Answer

BalusC picture BalusC · Aug 22, 2012

It has been moved to ViewDeclarationLanguage#buildView() as this step is decoupled from the view technology used (Facelets, JSP, JavaVDL, whatever).

context.getApplication().getViewHandler()
    .getViewDeclarationLanguage(context, viewId).buildView(context, viewRoot);

It's by the way simpler to extend ViewHandlerWrapper instead. This way you don't need that bunch of delegate methods.