views:

278

answers:

1

I have written simple application with container-managed security. The problem is when I log in and open another page on which I logout, then I come back to first page and I click on any link etc or refresh page I get this exception. I guess it's normal (or maybe not:)) because I logged out and session is destroyed. What should I do to redirect user to for example index.xhtml or login.xhtml and save him from seeing that error page/message?

In other words how can I automatically redirect other pages to index/login page after I log out?

Here it is:

javax.faces.application.ViewExpiredException: viewId:/index.xhtml - View /index.xhtml could not be restored.
    at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:212)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
    at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:110)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:312)
    at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1523)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:343)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:215)
    at filter.HttpHttpsFilter.doFilter(HttpHttpsFilter.java:66)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:215)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:277)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:188)
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:641)
    at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:97)
    at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:85)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:185)
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:325)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:226)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:165)
    at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:791)
    at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:693)
    at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:954)
    at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:170)
    at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:135)
    at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:102)
    at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:88)
    at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76)
    at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53)
    at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57)
    at com.sun.grizzly.ContextTask.run(ContextTask.java:69)
    at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:330)
    at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:309)
    at java.lang.Thread.run(Thread.java:619)
+1  A: 

The ViewExpiredException will be thrown whenever you submit a HTTP POST request using h:commandLink or h:commandButton while the associated view isn't available in the session anymore. Redirecting the request after logout is not the only solution, you also need to disable the browser cache. Otherwise browser is displaying page from cache. If you're using POST for navigation, this will in turn fail (the view ID is namely stored as hidden input field of POST form).

To fire a redirect after logout in JSF 2.0, either add <redirect /> to the <navigation-case> in question (if any), or add ?faces-redirect=true to the outcome value.

<h:commandButton value="Logout" action="logout?faces-redirect=true" />

or

public String logout() {
    // ...
    return "index?faces-redirect=true";
}

To disable the browser cache, create a Filter which is mapped on the url-pattern of interest (e.g. *.jsf) and contains the following in the doFilter() method:

response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
response.setDateHeader("Expires", 0); // Proxies.
chain.doFilter(request, response);

When you want to handle the ViewExpiredException on an arbitrary page which was already opened in some browser tab/window while you're logged out in another tab/window, then you'd like to specify an error-page for that in web.xml which goes to index/login page. E.g.

<error-page>
    <exception-type>javax.faces.application.ViewExpiredException</exception-type>
    <location>/login.jsf</location>
</error-page>

That said and unrelated to the problem, using HTTP POST for pure page-to-page navigation isn't very user/SEO friendly. In JSF 2.0 you should really prefer h:link or h:button over the h:commandXXX ones for plain navigation.

BalusC
How can I do it with implicit navigation in java ee 6? I don't use faces-config.
l245c4l
Oh, you're using JSF 2.0? You should have mentioned that in your question! Add `?faces-redirect=true` to the `outcome`. I've updated the answer accordingly.
BalusC
Yes I just started with java ee:) and I'm using faces-redirect=true in all my navigations. I use h:commandLink only when I have actions assosciated with it. For example Logout link... I have action String logout() where I invalidate session and redirect to login, but it doesn't work on page where I was logged in and being at the moment logged out and throws that exception :(
l245c4l
As said, only redirecting is not enough. You also need to disable browser cache. Btw: in Java EE 6 you can use `@WebFilter` to register the filter. Update: I think I understand your new problem, you want to handle it for an arbitrary already-opened page as well? See the answer update.
BalusC
Okay I created filter DisableBrowserCacheFilter did all setXXXXX but it still gives me this error. On page where I'm actually logging out it redirects me to login but on other pages still that error. Actually I'm binding this filter to "/*" url pattern. Is it good?:) I have already another filter with same pattern (to switching between https and http) so maybe they are conflicting?
l245c4l
Honestly said, I find it hard to understand your *actual* problem about *"on other pages still that error"*, since you didn't describe the use case in detail. But I already made a guess about that, have you checked the updated answer about the error page? By the way, mapping filter on `/*` is a bit too wide. You would of course like to allow cache for static files (css, js, img, etc) to save network bandwidth.
BalusC
Yeah exactly what I was thinking. I've already made it with error-page but I thought that exception showing that something bad is happening:) Thanks for your answers. I hope you can help me next time too:)
l245c4l
You're welcome.
BalusC
BTW I've created Session Expired error page bound to that exception. Now it would be good idea to redirect from there to index or login. How do I do that in JSF 2.0 ? Another filter with some 'sleep' would be good idea?
l245c4l
A `<meta http-equiv="refresh" content="3;url=login.jsf">` in `<head>` goes to `login.jsf` after `3` seconds. For future new question please press `Ask Question` button :)
BalusC
Thanks again and sorry for that:) but at least I got quick and professional answer in no time :p
l245c4l