当前位置: 代码迷 >> JavaScript >> JSF中session过期后视图恢复的有关问题
  详细解决方案

JSF中session过期后视图恢复的有关问题

热度:301   发布时间:2012-12-27 10:17:10.0
JSF中session过期后视图恢复的问题

在jsf中,一旦session过期,生命周期为session的Controller bean对象将会被销毁,因此除了一些静态页面,大多数情况的动态页面视图也就无法恢复。如果没有在web.xml设置error page,那么系统会直接在页面中抛出异常。当然关掉页面重新打开就不会有这个问题,但是普通浏览者这个时候往往会不知所措,以为网站挂掉了,这样可用性会很差。

??? 一般网上提供的解决方法是在web.xml中设置error page来捕获错误,重定向到一个指定的页面,例如:
<error-page>??? ?
<exception-type>javax.faces.application.ViewExpiredException</exception-type>??? ?
<location>/login.jsf</location>?
</error-page>


??? 但悲惨的是,在调试中发现javax.faces.application.ViewExpiredException这个异常并不会被捕获到,com.sun.faces.lifecycle.LifecycleImpl的execute方法抛出的实际上是FacesException。我怀疑可能是jsf的2.0版本修改了这一块,而网上的解决方法是针对老版本的。

??? 如此,我们只能通过从com.sun.faces.lifecycle.LifecycleImpl派生一个类来处理这个异常,代码如下:

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

import com.ppedu.commons.SystemProperty;
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) {
?? ??? ??? ?//似乎永远不会捕获,应该是异常在父类的同名方法中被包装成了FacesException
?? ??? ??? ?redirect(context);
?? ??? ?}
?? ??? ?catch (FacesException fe) {
?? ??? ??? ?//取得该异常的root cause
?? ??? ??? ?Object obj= fe.getCause();
?? ??? ??? ?//判断是否ViewExpiredException
?? ??? ??? ?if (obj instanceof? ViewExpiredException){?? ??? ??? ??? ?
?? ??? ??? ??? ?redirect(context);
?? ??? ??? ?}
?? ??? ??? ?else{
?? ??? ??? ??? ?throw fe;
?? ??? ??? ?}
?? ??? ?}
?? ?}
?? ??
?? ?private void redirect(FacesContext context) {
?? ??? ?try {
?? ??? ??? ?context.responseComplete();
?? ??? ??? ?context.renderResponse();
?? ??? ??? ?HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();
?? ??? ??? ?
?? ??? ??? ?//根据原来的请求路径确定要重定向的路径
?? ??? ??? ?HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
?? ??? ??? ?String path = request.getRequestURI();
?? ??? ??? ?String [] ss = path.split("/");?? ??? ??? ?
?? ??? ??? ?String redirectKey = "session.timeout.redirect.url.general";
?? ??? ??? ?for(int i=0;i<ss.length;i++){
?? ??? ??? ??? ?if( ss[i].equals("security") || ss[i].equals("organManagementCenter") || ss[i].equals("webManageCenter")){
?? ??? ??? ??? ??? ?//管理模块
?? ??? ??? ??? ??? ?redirectKey = "session.timeout.redirect.url.management";
?? ??? ??? ??? ??? ?break;
?? ??? ??? ??? ?}
?? ??? ??? ??? ?if( ss[i].equals("usercenter")){
?? ??? ??? ??? ??? ?//个人中心
?? ??? ??? ??? ??? ?redirectKey = "session.timeout.redirect.url.usercenter";
?? ??? ??? ??? ??? ?break;
?? ??? ??? ??? ?}
?? ??? ??? ?}
?? ??? ??? ?//目标页面
?? ??? ??? ?String url = context.getExternalContext().getRequestContextPath() + SystemProperty.getKeyValue(redirectKey);?? ?
?? ??? ??? ?response.sendRedirect(url);
?? ??? ?} catch (Exception e) {
?? ??? ??? ?System.out.println("url redirect wrong ");
?? ??? ?}
?? ?}
}


??? 这个类可以捕获ViewExpiredException引起的异常,将页面重定向到实现设定的url。com.ppedu.commons.SystemProperty是一个读配置文件的类,读者可以自己实现一个,配置文件设置了三个url,分别指向网站首页、用户中心登陆页和管理中心登录页,设置如下:

#============================================================================
# session过期后的默认页 ?
#============================================================================
session.timeout.redirect.url.general = /home.jsf
session.timeout.redirect.url.management = /security/managementLogin.jsf
session.timeout.redirect.url.usercenter = /usercenter/login.jsf


??? 然后,我们创建一个工厂类:

import com.sun.faces.lifecycle.LifecycleFactoryImpl;

public class TeleLifecycleFactoryImpl extends LifecycleFactoryImpl {
?? ?public final static String CUSTOM_LIFECYCLE = "TELEEPOCH";?? ?
??? /** Creates a new instance of TeleLifecycleFactoryImpl */
??? public TeleLifecycleFactoryImpl() {
??????? addLifecycle(CUSTOM_LIFECYCLE,new TeleLifecycleImpl());
??? }
}

??? 接着,在faces-config.xml中注册这个工厂类来监查生命周期:

<factory>
?? ??? ?<lifecycle-factory>com.ppedu.view.TeleLifecycleFactoryImpl</lifecycle-factory>
</factory>


??? OK,搞定收工,测试一下看看是否能获得预期效果。

  相关解决方案