5

I leave some tabs open with forms and when I press the command buttons (after some time when the session expires) I receive a java script alert saying:

serverError: class javax.faces.application.ViewExpiredException viewId:/register.xhtml - View /register.xhtml could not be restored.

On firefox (glassfish 4 locally)

I have added :

<error-page>
    <exception-type>javax.faces.application.ViewExpiredException</exception-type>
    <location>/index.xhtml?faces-redirect=true</location>
</error-page>

in my web.xml but I am not redirected to my index. Why is that ?

Edit: sample button

<h:commandButton value="Register" action="#{userController.register}">
    <f:ajax execute="@form" render="@form" />
</h:commandButton>

UserController is ViewScoped

Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361

2 Answers2

4

That's expected behaviour, if you look at sections 13.3.6.3 and 13.3.6.4 of the JSF-2.2 spec, at least if you have

<context-param>
  <param-name>javax.faces.PROJECT_STAGE</param-name>
  <param-value>Development</param-value>
</context-param>

in your web.xml (it silently ignores the error in production mode). The JSF client-side code sends an AJAX request when you click that button and receives

<error>
    <error-name>...</error-name>
    <error-message>...</error-message>
</error>

with your ViewExpiredException in the response. The client-side code is then expected to react to that error information. Register a function with onerror (on the f:ajax tag) or addOnError (on the jsf.ajax object) to handle error yourself.

Since a lot of folks expect the server to direct the client on such an issue, omnifaces includes the FullAjaxExceptionHandler that basically resets your response and sends the error page you specified.

See also:

Session timeout and ViewExpiredException handling on JSF/PrimeFaces ajax request

Community
  • 1
  • 1
mabi
  • 5,279
  • 2
  • 43
  • 78
  • Aha - yes I have `javax.faces.PROJECT_STAGE` - will look into your answer when in my dev machine. Thanks ! What is the `jsf.ajax` object ? – Mr_and_Mrs_D Feb 18 '14 at 13:44
  • 1
    @Mr_and_Mrs_D it's a javascript object that contains the ajax related functionality of your JSF implementation. So you add an error handler by calling `jsf.ajax.addOnError(function() { /* your stuff */ });` – mabi Feb 18 '14 at 13:53
  • I implemented and registered an exception handler – Mr_and_Mrs_D Feb 21 '14 at 03:32
0

Try this approach to handle view expiration: Create the following 2 classes in your project

import javax.faces.context.ExceptionHandler;
import javax.faces.context.ExceptionHandlerFactory;

public class ViewExpiredExceptionExceptionHandlerFactory extends
        ExceptionHandlerFactory {

    private ExceptionHandlerFactory parent;

    public ViewExpiredExceptionExceptionHandlerFactory(
            ExceptionHandlerFactory parent) {
        this.parent = parent;
    }

    @Override
    public ExceptionHandler getExceptionHandler() {
        return new ViewExpiredExceptionExceptionHandler(
            parent.getExceptionHandler());
    }
}

and

public class ViewExpiredExceptionExceptionHandler extends
        ExceptionHandlerWrapper {

    private ExceptionHandler wrapped;

    public ViewExpiredExceptionExceptionHandler(ExceptionHandler wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public ExceptionHandler getWrapped() {
        return this.wrapped;
    }

    @Override
    public void handle() throws FacesException {
        for (Iterator<ExceptionQueuedEvent> i = 
                getUnhandledExceptionQueuedEvents().iterator(); i.hasNext();) {
            ExceptionQueuedEvent event = i.next();
            ExceptionQueuedEventContext context = (ExceptionQueuedEventContext)
                event.getSource();
            Throwable t = context.getException();
            if (t instanceof ViewExpiredException) {
                ViewExpiredException vee = (ViewExpiredException) t;
                FacesContext facesContext = FacesContext.getCurrentInstance();
                Map<String, Object> requestMap = facesContext
                    .getExternalContext().getRequestMap();
                NavigationHandler navigationHandler = facesContext
                    .getApplication().getNavigationHandler();
                try {
                    // Push some useful stuff to the request scope for use in
                    // the page
                    requestMap.put("currentViewId", vee.getViewId());
                    navigationHandler.handleNavigation(facesContext, null,
                        "/index");
                    facesContext.renderResponse();
                } finally {
                    i.remove();
                }
            }
        }
        // At this point, the queue will not contain any ViewExpiredEvents.
        // Therefore, let the parent handle them.
        getWrapped().handle();
    }
}

In faces-config.xml add the following lines:

<factory>
    <exception-handler-factory>path.to.class.ViewExpiredExceptionExceptionHandlerFactory</exception-handler-factory>

And that's it, you're good to go.

EDIT

Ok, i forgot about the Omnifaces way of doing this, i don't use this because once you set the page for error it warns you to make pages for 404 and another one that i don't remember right now. You should add to your faces-config.xml the following:

<factory>
<exception-handler-factory>org.omnifaces.exceptionhandler.FullAjaxExceptionHandlerFactory</exception-handler-factory>

and your code will work. For more informations see the showcase for FullAjaxExceptionHandlerFactory from the omnifaces website http://showcase.omnifaces.org/exceptionhandlers/FullAjaxExceptionHandler;jsessionid=sOwX0FVqcY-InUhvDWEMksfQ .

Don't forget to add the required jar to your project.

Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361
  • [Plagiarism](http://meta.stackexchange.com/questions/160077/users-are-calling-me-a-plagiarist-what-do-i-do) is not appreciated here. Please attribute the source where you ripped off this code from. It would also be respectable if you can explain in layman's terms how exactly this all solves OP's concrete problem. Right now it's really nothing more than a clueless "BAM, here's some code to copypaste. Try it, perhaps it fixes the problem!". – BalusC Feb 17 '14 at 10:38
  • The code is from one of my existing projects. If i recall well, when i first started learning jsf, i tried some spring integration at one point with spring roo. The generated project had this kind of approach for resolving expired views and i've been using it since then with no problems. – Alex Dutescu Feb 17 '14 at 11:51
  • @AlexDutescu: excuse me, omnifaces ? – Mr_and_Mrs_D Feb 17 '14 at 20:58
  • @Mr_and_Mrs_D I'm not going to tell you now what omnifaces is, i gave you a link for that. I don't know why your code doesn't work either just from the info you have provided, so i gave you 2 alternatives to help you get over the problem. That's all i can do, maybe someone else will have a better answer. – Alex Dutescu Feb 18 '14 at 07:48
  • 1
    @AlexDutescu: when I said "excuse me, omnifaces ?" I meant _when did I mention omnifaces in the question_ not what omnifaces is. Reread the first comment in this answer. I don't want to "make it work" - _I want an answer to the question I posted_. I can "make it work" - programming is not about "making it work", it is about making it work _the right way_. Programmers who "made it work" have filled the world with half broken programs. I hope you are not one of them – Mr_and_Mrs_D Feb 18 '14 at 09:35