javax.faces.application.ViewExpiredException的解决方案

来源:互联网 发布:linux port range 编辑:程序博客网 时间:2024/05/23 02:02
摘抄一[没有试过,看起来是处理异常(捕获异常再重定向),而不是避开异常]

javax.faces.application.ViewExpiredException: viewId:/pages/gardeninfo/envRiskEditForm.faces - View /pages/gardeninfo/envRiskEditForm.faces could not be restored.对于用户来说是不懂的,那只要在重写生命周期便可处理

第一种【没有试过】:

1. 重写一个类,继承Lifecycle类,重写父类的execute方法,捕捉该异常,然后进行处理(如设置跳转到登录页面)。
2. 重写一个类,继承LifecycleFactoryImpl类,添加SessionLifecycleImpl的实例到工厂中,假设重写的子类为project.jsf.test.SessionLifecycleFactoryImpl。
3.在faces-config.xml中注册JSFLifecycleFactory类。
4.在web.xml中指定JSFLifecycleFactory工厂中创建的JSFLifecycle实例的键(key)。

public class JSFLifecycleFactory extends LifecycleFactoryImpl {
 private static Logger LOGGER = FacesLogger.LIFECYCLE.getLogger();

 public static final String JSF_LIFECYCLE = "JSF";

 public JSFLifecycleFactory() {
  super();
 
  lifecycleMap.put(JSF_LIFECYCLE, new JSFLifecycle());
  if (LOGGER.isLoggable(Level.FINE)) {
   LOGGER.fine("Created EJS JSF Lifecycle");
  }
 }
}

public class JSFLifecycle extends Lifecycle {

 private static Logger LOGGER = FacesLogger.LIFECYCLE.getLogger();

 private Phase response = new RenderResponsePhase();

 private Phase[] phases = {
   null, // ANY_PHASE placeholder, not a real Phase
   new RestoreViewPhase(), new ApplyRequestValuesPhase(),
   new ProcessValidationsPhase(), new UpdateModelValuesPhase(),
   new InvokeApplicationPhase(), response };

 private List<PhaseListener> listeners = new CopyOnWriteArrayList<PhaseListener>();

 public void execute(FacesContext context) throws FacesException {
  try {
   if (context == null) {
    throw new NullPointerException(MessageUtils
      .getExceptionMessageString(
        MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID,
        "context"));
   }

   if (LOGGER.isLoggable(Level.FINE)) {
    LOGGER.fine("execute(" + context + ")");
   }

   for (int i = 1, len = phases.length - 1; i < len; i++) {
    if (context.getRenderResponse()
      || context.getResponseComplete()) {
     break;
    }
    phases[i].doPhase(context, this, listeners.listIterator());
   }
  }
  catch (ViewExpiredException e) {
   JSFMessageUtils.removeMessage();
   JSFMessageUtils.showAndLogException("信息","操作错误", e
       .getMessage(), null, JSFMessageUtils.WARN,
     JSFMessageUtils.OK);
  }
  catch (Exception e) {
   JSFMessageUtils.removeMessage();
   JSFMessageUtils.showAndLogException("信息","操作错误", e
     .getMessage(), null, JSFMessageUtils.WARN,
   JSFMessageUtils.OK);
  }
 }

 public void render(FacesContext context) throws FacesException {
  try {
   if (context == null) {
    throw new NullPointerException(MessageUtils
      .getExceptionMessageString(
        MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID,
        "context"));
   }

   if (LOGGER.isLoggable(Level.FINE)) {
    LOGGER.fine("render(" + context + ")");
   }
   if (!context.getResponseComplete()) {
    response.doPhase(context, this, listeners.listIterator());
   }
  }
  catch (FacesException e) {
   JSFMessageUtils.removeMessage();
   JSFMessageUtils.showAndLogException("信息","操作错误", e
     .getMessage(), null, JSFMessageUtils.WARN,
   JSFMessageUtils.OK);
  }
  catch (Exception e) {
   JSFMessageUtils.showAndLogException("信息","操作错误", e
     .getMessage(), null, JSFMessageUtils.WARN,
   JSFMessageUtils.OK);
  }
 }

 public void addPhaseListener(PhaseListener listener) {
  if (listener == null) {
   throw new NullPointerException(MessageUtils
     .getExceptionMessageString(
       MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID,
       "listener"));
  }

  if (listeners == null) {
   listeners = new CopyOnWriteArrayList<PhaseListener>();
  }

  if (listeners.contains(listener)) {
   if (LOGGER.isLoggable(Level.FINE)) {
    LOGGER.log(Level.FINE,
      "jsf.lifecycle.duplicate_phase_listener_detected",
      listener.getClass().getName());
   }
  }
  else {
   if (LOGGER.isLoggable(Level.FINE)) {
    LOGGER.log(Level.FINE, "addPhaseListener({0},{1})",
      new Object[] { listener.getPhaseId().toString(),
        listener.getClass().getName() });
   }
   listeners.add(listener);
  }
 }

 public PhaseListener[] getPhaseListeners() {
  return listeners.toArray(new PhaseListener[listeners.size()]);

 }

 public void removePhaseListener(PhaseListener listener) {
  if (listener == null) {
   throw new NullPointerException(MessageUtils
     .getExceptionMessageString(
       MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID,
       "listener"));
  }

  if (listeners.remove(listener) && LOGGER.isLoggable(Level.FINE)) {
   LOGGER.log(Level.FINE, "removePhaseListener({0})",
     new Object[] { listener.getClass().getName() });
  }
 }
}

 

faces-config.xml:

<factory>
  <lifecycle-factory>org.ejs.jsf.JSFLifecycleFactory</lifecycle-factory>
</factory>

web.xml

<context-param>
  <param-name>javax.faces.LIFECYCLE_ID</param-name>
  <param-value>JSF</param-value>
</context-param>

 

第二种网上搜的【没有试过】:

1. 重写一个类,继承LifecycleImpl类,重写父类的execute方法,捕捉该异常,然后进行处理(如设置跳转到登录页面),假设重写的子类名为project.jsf.test.SessionLifecycleImpl。
2. 重写一个类,继承LifecycleFactoryImpl类,添加SessionLifecycleImpl的实例到工厂中,假设重写的子类为project.jsf.test.SessionLifecycleFactoryImpl。
3.在faces-config.xml中注册SessionLifecycleFactoryImpl类。
4.在web.xml中指定SessionLifecycleFactoryImpl工厂中创建的SessionLifecycleImpl实例的键(key)。

具体如下:
1.project.jsf.test.SessionLifecycleImpl类:

import javax.faces.FacesException;
import javax.faces.application.ViewExpiredException;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletResponse;

import com.sun.faces.lifecycle.LifecycleImpl;

public class SessionLifecycleImpl extends LifecycleImpl {
public final static String SESSION_TIMEOUT_PAGES = "/pages/sessionTimeOut.faces";
public SessionLifecycleImpl() {
super();
}

public void execute(FacesContext context) {
try {
super.execute(context);
}catch(ViewExpiredException vee) {
redirect(context);
}catch(FacesException fe) {
throw fe;
}
}

private void redirect(FacesContext context) {
try {
context.responseComplete();
context.renderResponse();
HttpServletResponse response = (HttpServletResponse)context.getExternalContext().getResponse();
String url = context.getExternalContext().getRequestContextPath()+ SESSION_TIMEOUT_PAGES;
response.sendRedirect(url);
}catch(Exception e) {
System.out.println(" Error: session timeout url redirect ");
}
}
}

2. project.jsf.test.SessionLifecycleFactoryImpl类:

import com.sun.faces.lifecycle.LifecycleFactoryImpl;

public class SessionLifecycleFactoryImpl extends LifecycleFactoryImpl {
public static final String SESSION_LIFECYCLE = "SessionLifecycle";
public SessionLifecycleFactoryImpl(){
super();
addLifecycle(SESSION_LIFECYCLE, new SessionLifecycleImpl());
}
}

3. faces-config.xml中注册SessionLifecycleFactoryImpl类
<faces-config>
<factory>
<lifecycle-factory>project.jsf.test.SessionLifecycleFactoryImpl</lifecycle-factory>
</factory>
.........
</faces-config>

4. web.xml中指定SessionLifecycleImpl实例的键(key):
<context-param>
<param-name>javax.faces.LIFECYCLE_ID</param-name>
<param-value>SessionLifecycle</param-value>
</context-param>

 

第三种:网上搜的【没有试过】

现这个错误是因为session 超时。当然关掉页面重新打开就不会有这个问题,但是在可用性方面就很差。作为开发人员看见这个错误会知道为什么,普通浏览者肯定会觉得出了什么问题。所以还是解决一下好。

如果是 sun appplication server

解决办法是在web.xml中添加
<error-page>
     <exception-type>javax.faces.application.ViewExpiredException</exception-type>
     <location>/sessionExpired.jsp</location>
</error-page>

sessionExpired.jsp:
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c"%>
<c:redirect url="/login.jsf" />

如果web容器是tomcat,解决办法如下:

package com.jsf.util;

import javax.faces.FacesException;
import javax.faces.application.ViewExpiredException;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletResponse;

import com.sun.faces.lifecycle.LifecycleImpl;

public class TeleLifecycleImpl extends LifecycleImpl
{
 public TeleLifecycleImpl()
 {
  super();
 }
 
 public void execute(FacesContext context)
 {
  try
  {
   super.execute(context);
  }
  catch (ViewExpiredException  vee)
  {
   redirect(context);
  }
  catch (FacesException fe)
  {
   throw fe;
  }
 }
 
 private void redirect(FacesContext context)
 {
  try
  {
   context.responseComplete();
   context.renderResponse();
   HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();
   String url = context.getExternalContext().getRequestContextPath() + "/faces/error.jsp";
   response.sendRedirect(url);
  }
  catch (Exception e)
  {
   System.out.println("url redirect wrong ");
  }

 }
}

 在jsf配置文件 faces-config.xml 中添加如下内容

<factory>
     <lifecycle-factory>trackingmap.TeleLifecycleFactoryImpl</lifecycle-factory>
</factory>

在web.xml 中添加如下内容

<context-param>
     <param-name>javax.faces.LIFECYCLE_ID</param-name>
     <param-value>TELEEPOCH</param-value>
</context-param>


摘抄二[实践证明好像无效,看起来需要多种方案组合]

后台页面以JSF编写,当客户端的session失效后,再去点击JSF页面中的操作时,会要求用户再次输入用户名、密码,然后重定向到之前点击操作的页面,在此重定向时产生了异常:

  1. javax.faces.application.ViewExpiredException: viewId:/jsf/profile/editProfileRedirect.xhtml - View /jsf/profile/editProfileRedirect.xhtml could not be restored.  
  2.     at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:186)  
  3.     at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:100)  
  4.     at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:104)  
  5.     at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)  
  6.     at org.jboss.portletbridge.AjaxPortletBridge.execute(AjaxPortletBridge.java:836)  
  7.     at org.jboss.portletbridge.AjaxPortletBridge.doFacesRequest(AjaxPortletBridge.java:343)  
  8.     at javax.portlet.faces.GenericFacesPortlet.processAction(GenericFacesPortlet.java:323)  
  9.     at org.jboss.portal.portlet.impl.jsr168.PortletContainerImpl$Invoker.doFilter(PortletContainerImpl.java:557)  
  10.     at org.jboss.portal.portlet.impl.jsr168.api.FilterChainImpl.doFilter(FilterChainImpl.java:109)  
  11.     at org.jboss.portlet.filter.JBossPortletFilter.doFilter(JBossPortletFilter.java:59
  12. .。。。。。。。。。。。

原因是JSF页面会保存一个状态,默认是在服务端,当客户端的session失效后通过<h:commandLink><h:commandButton>访问JSF页面时,就会出现这个异常。

The ViewExpiredException will be thrown whenever the javax.faces.STATE_SAVING_METHOD is set toserver (default) and the enduser sends a HTTP POST request using<h:commandLink> or<h:commandButton> on a view, while the associated view state isn't available in the session anymore.

修改方法之一是在项目的web.xml配置文件中将javax.faces.STATE_SAVING_METHOD的值配置为“client”,即将页面的状态保存在客户端中。这样客户端的页面就会更复杂了一些。可能会稍微影响到页面的性能。


摘抄三【这种情况好像不是很通用】

javax.faces.application.ViewExpiredException: viewId xxx- View xxx could not be restored.

项目中老出现这样的异常。都是在session过期之后的自动跳转时出现,检查代码,有一块跳转 在sessioni == null情况下仍然调用FilterChain.doFilter..。加上判断。

另外将RequestDispatcher 修改为response.sendRedirect....

问题解决~~


摘抄四【实践证明好像无效,不过session-config倒是可以借鉴】

运行jsf页面出现javax.faces.application.ViewExpiredException - /*.jsp No saved view state could be found for the view identifier: /*.jsp问题的解决方法
 
出现此问题的原因我也不是太清楚,看了相关资料说是因为session的保存时间造成了。所以下面的两个方法都是针对时间来修正。
在web.xml中新增下列节点

方法1.   

1  <web-app>2            <context-param> 3              <param-name>facelets.BUILD_BEFORE_RESTORE</param-name> 4            <param-value>true</param-value> 5          </context-param> 6  </web-app>
方法2.
  增加保存时间为600分钟
1  <web-app>       2        <session-config>3            <session-timeout>600</session-timeout>4        </session-config>5  </web-app>

摘抄五[stackoverflow上的大杂烩,值得借鉴]

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)
share|improve this question

56% accept rate
 
feedback

2 Answers

activeoldestvotes
up vote34down vote accepted

The ViewExpiredException will be thrown whenever the javax.faces.STATE_SAVING_METHOD is set toserver (default) and the enduser sends a HTTP POST request using<h:commandLink> or<h:commandButton> on a view, while the associated view state isn't available in the session anymore. The view state is identified by a hidden input fieldjavax.faces.ViewState of the<h:form>. With the state saving method set toserver, this contains only the view state ID which references a serialized view state in the session. With the state saving method set toclient, this is instead the whole serialized view state, so the enduser won't get aViewExpiredException when the session expires.

In order to prevent ViewExpiredException when the state saving is set toserver, only redirecting the POST request after logout is not sufficient. You also need to instruct the browser tonot cache the dynamic JSF pages, otherwise the browser may show them from the cache instead of requesting a fresh one from the server when you send a GET request on it (e.g. by back button). If you're using POST for page-to-page navigation, this will in turn fail.

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 theoutcome value.

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

or

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

To instruct the browser to not cache the dynamic JSF pages, create a Filter which is mapped on the servlet name of theFacesServlet and adds the needed response headers to disable the browser cache. E.g.

@WebFilter(servletNames={"Faces Servlet"}) // Must match <servlet-name> of your FacesServlet.public class NoCacheFilter implements Filter {    @Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {        HttpServletRequest req = (HttpServletRequest) request;        HttpServletResponse res = (HttpServletResponse) response;        if (!req.getRequestURI().startsWith(req.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER)) { // Skip JSF resources (CSS/JS/Images/etc)            res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.            res.setHeader("Pragma", "no-cache"); // HTTP 1.0.            res.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 anerror-page for that inweb.xml which goes to index/login page or maybe a "Your session is timed out" page. E.g.

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

See also:

  • Issue with JSF ViewExpiredException and multiple error-page tag definitions in web.xml
  • Check if session exists JSF

That said and unrelated to the concrete 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 vanilla page-to-page navigation.

See also

  • When should I use h:outputLink instead of h:commandLink?
share|improve this answer
 
How can I do it with implicit navigation in java ee 6? I don't use faces-config. – l245c4lSep 4 '10 at 16:01
 
Oh, you're using JSF 2.0? You should have mentioned that in your question! Add?faces-redirect=true to theoutcome. I've updated the answer accordingly. – BalusCSep 4 '10 at 16:02
 
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 :( – l245c4lSep 4 '10 at 16:13
 
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. – BalusCSep 4 '10 at 16:22
1 
@LS: The filter is however still mandatory for the case whenever one presses the back button after an expired POST and tries to invoke another POST request on it. This would otherwise unintuitively result in this exception. – BalusCJun 12 at 18:15
show9 more comments
feedback
up vote3down vote

Have you tried adding lines below to your web.xml?<context-param> <param-name>com.sun.faces.enableRestoreView11Compatibility</param-name> <param-value>true</param-value></context-param>I found this to be very effective when I encountered this issue. 【这个证明有效,但是貌似只适用于SUN RI】

share|improve this answer
 
it worked for me too. Thanks for the answer. What is the purpose of this? – MartKMay 17 at 10:56
1 
Don't recall exactly, but I found this solution on ICEFaces website. – Mike GHMay 22 at 12:18


摘抄六[还没有试过]

重写ViewHandler

Preventing ViewExpiredException in JSF

http://www.gregbugaj.com/?p=164



PFYU总结

[正如BalusC说的那样,解决这个问题可能需要解决三方面:服务端浏览器端处理异常(重定位)]

导致ViewExpiredException的原因有多种,常见的有:

这里有一段stackoverflow上的解释:

This exception will be thrown whenever the enduser fires a POST request on a view which does not exist anymore in the server side, because the session has been expired. The view states are by default stored in the session, so they are lost whenever the session expires. A POST request will be fired when the enduser clicks a command button or command link or fires a JSF ajax request.

This can happen when you keep a page open in browser and untouch it for too long so that the session expires (usually, that is 30 minutes, depending on server configuration). Firing a POST request on such a view after session expiration will then result in ViewExpiredException. This can also happen when the browser loads the page from its cache instead of from the server.

They can be handled by <error-page> inweb.xml or a customExceptionHandler. They can if necessary be avoided by setting the context parameterjavax.faces.STATE_SAVING_METHOD toclient instead of (default)server which would then save the serialized view state as a hidden input field value of the POST form. With server side state saving, the browser also needs to be instructed to not cache the dynamic JSF pages.


1. session expiration (invalidate/timeout)。session timeout可以在web.xml中显示指定。

重现:可以在web.xml中设置session-timeout到1分钟来重现

SUNRI: [避开]貌似只能设置STATE_SAVING_METHOD, 设置numberOfViewsInSession/numberOfLogicalViewsenableRestoreView11Compatibilty都无效

               [处理]设置error-page,重写LifeCycle,重写ExceptionHandler (推荐处理,而不是避开)

MyFaces: [避开]貌似只能设置STATE_SAVING_METHOD, 设置NUMBER_OF_VIEWS_IN_SESSION无效

                  [处理]设置error-page,重写LifeCycle,重写ExceptionHandler(推荐处理,而不是避开)

2. The enduser has requested/created too much views within a session and is submitting to an old view. The default max views per session is 15. In other words, if the enduser opens 16 browser windows/tabs on a page with a form within the same session and submits to the first one, then the user can get ViewExpiredException.Thereal ViewExpiredException is usually only thrown when you send a HTTP POST request to the server while the HTTP session is expired. 可以在web.xml中显示指定session中的最大view数目。MyFaces和SUN RI的配置参数不一样。

重现:可以在web.xml中设置NUMBER_OF_VIEWS_IN_SESSION来重现[MyFaces],设置numberOfViewsInSession/numberOfLogicalViews[SUNRI]貌似没法重现

SUNRI: [避开]没法重现,一旦重现了解决方法应该是同MyFaces

MyFaces: [避开]貌似设置STATE_SAVING_METHOD或设置NUMBER_OF_VIEWS_IN_SESSION都有效

3. browser loads the page from its cache instead of from the server.

这种情况,貌似必须要采取NoCacheFileter+清空浏览器。即使设置STATE_SAVING_METHOD都无效

SUNRI: [避开]貌似只能设置NoCacheFilter或页面中添加头,同时清空浏览器

MyFaces: [避开]目前好像还没遇到浏览器Cache的问题,一旦遇到了解决方法应该是同SUNRI


1 针对MyFaces

1.1 从web.xml中的引擎参数入手(避免异常)

1.1.1 javax.faces.STATE_SAVING_METHOD

You can solve it by setting the state saving method toclient instead ofserver so that views are stored (in serialized form, of course) in a hidden input field of the POST form, instead of in the session in the server side (which is in turn to be referenced byJSESSIONID cookie; so all views will basically get lost when you delete the session cookie or when the session expires).

将参数从默认的server改为client,实际测试过,有效?

1.1.1 org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION

将这个参数设大一点,比如设成500,实际测试过,有效?


1.2 创建Filter设置HTTP Response的Header,通知浏览器不要Cache。同时如果异常已经发生了,则需要清空浏览器缓存。(避免异常)

当然也可以把这些内容挨个加到每个页面中

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

 HttpServletRequest req = (HttpServletRequest) request;
 HttpServletResponse res = (HttpServletResponse) response;

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

 chain.doFilter(request, response);

}

    <filter>
        <display-name>NoCacheFilter</display-name>
        <filter-name>NoCacheFilter</filter-name>
        <filter-class>filters.NoCacheFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>NoCacheFilter</filter-name>
        <url-pattern>
        *.faces</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>NoCacheFilter</filter-name>
        <url-pattern>
        /faces/*</url-pattern>
    </filter-mapping>

实际测试过,有效?


1.3 重写LifeCycle(处理异常)

代码见上面,看起来要复杂一点。没有试过。


1.4 在web.xml中写error-page (处理异常)

<error-page>
     <exception-type>javax.faces.application.ViewExpiredException</exception-type>
     <location>/sessionExpired.jsp</location>
</error-page>


2 针对SUN RI

2.1 从web.xml中的引擎参数入手(避免异常)

2.1.1 javax.faces.STATE_SAVING_METHOD

将参数从默认的server改为client,实际测试过,好像无效?(不稳定,似乎需要多种方案组合)

2.1.2 com.sun.faces.numberOfViewsInSession和com.sun.faces.numberOfLogicalViews

将这个参数设大一点,比如设成500,实际测试过,好像无效?(不稳定,似乎需要多种方案组合)

2.1.3 com.sun.faces.enableRestoreView11Compatibilty

将参数设置成true,实际测试过,好像无效??(不稳定,似乎需要多种方案组合)


2.2 创建Filter设置HTTP Response的Header,通知浏览器不要Cache。同时如果异常已经发生了,则需要清空浏览器缓存。(避免异常)

当然也可以把这些内容挨个加到每个页面中

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

 HttpServletRequest req = (HttpServletRequest) request;
 HttpServletResponse res = (HttpServletResponse) response;

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

 chain.doFilter(request, response);

}

    <filter>
        <display-name>NoCacheFilter</display-name>
        <filter-name>NoCacheFilter</filter-name>
        <filter-class>filters.NoCacheFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>NoCacheFilter</filter-name>
        <url-pattern>
        *.faces</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>NoCacheFilter</filter-name>
        <url-pattern>
        /faces/*</url-pattern>
    </filter-mapping>

实际测试过,有效?(不稳定,似乎需要多种方案组合)


2.3 重写LifeCycle(处理异常)

代码见上面,看起来要复杂一点。没有试过。


2.4 在web.xml中写error-page (处理异常)

<error-page>
     <exception-type>javax.faces.application.ViewExpiredException</exception-type>
     <location>/sessionExpired.jsp</location>
</error-page>