JSP环境基于Session的在线用户统计深入分析

问题:JSP环境基于Session的在线用户统计深入分析   (   积分:1,   回复:23,   阅读:365   )  
  分类:JSP/Servlet   (   版主:eguy,   delphiasp   )      
  来自:曹晓钢,   时间:2001-12-4   18:30:00,   ID:763625   [显示:小字体   |   大字体]      
  JSP作为后起之秀能够在服务器编程环境中占据一定地位,是和它良好支持一系列业界标准  
  密切相关的。Session就是它提供的基础设施之一。作为一个程序员,你可以不介意具体在  
  客户端是如何实现,就方便的实现简单的基于session的用户管理。  
   
  现在对于处理在线用户,有几种不同的处理方法。  
   
  一种是叶面刷新由用户控制,服务器端控制一个超时时间比如30分钟,到了时间之后用户  
  没有动作就被踢出。这种方法的优点是,如果用户忘了退出,可以防止别人恶意操作。  
  缺点是,如果你在做一件很耗时间的事情,超过了这个时间限制,submit的时候可能要  
  再次面临登陆。如果原来的叶面又是强制失效的话,就有可能丢失你做的工作。在实现  
  的角度来看,这是最简单的,Server端默认实现的就是这样的模式。  
   
  另一种方式是,站点采用框架结构,有一个Frame或者隐藏的iframe在不断刷新,这样你  
  永远不会被踢出,但是服务器端为了判断你是否在线,需要定一个发呆时间,如果超过  
  这个发呆时间你除了这个自动刷新的叶面外没有刷新其他叶面的话,就认为你已经不在  
  线了。采取这种方式的典型是xici.net。   他的优点是可以可以利用不断的刷新实现一些  
  类似server-push的功能,比如网友之间发送消息。  
   
  不管哪一种模式,为了实现浏览当前所有的在线用户,还需要做一些额外的工作。  
  servlet   API中没有得到Session列表的API。  
   
  可以利用的是Listener.   Servlet   2.2和2.3规范在这里略微有一些不一样。  
  2.2中HttpSessionBindingListener可以实现当一个HTTPSession中的Attribute变化的  
  时候通知你的类。而2.3中还引入了HttpSessionAttributeListener.鉴于我使用的环境  
  是Visual   age   for   java   4和JRun   server   3.1,他们还不直接支持Servlet   2.3的编程,  
  这里我用的是HttpSessionBindingListener.  
   
  需要做的事情包括做一个新的类来实现HttpSessionBindingListener接口。这个接口有  
  两个方法:  
  public   void   valueBound(HttpSessionBindingEvent   event),和  
  public   void   valueUnbound(HttpSessionBindingEvent   event)。  
  当你执行Session.addAttribute(String,Object)的时候,如果你已经把一个实现了  
  HttpSessionBindingListener接口的类加入为Attribute,Session会通知你的类,调用  
  你的valueBound方法。相反,Session.removeAttribute方法对应的是valueUndound方法。  
   
  [code]  
  public   class   HttpSessionBinding   implements   javax.servlet.http.HttpSessionBindingListener    
  {  
  ServletContext   application   =   null;  
   
  public   HttpSessionBinding(ServletContext   application)  
  {  
  super();  
  if   (application   ==null)  
  throw   new   IllegalArgumentException("Null   application   is   not   accept.");  
   
  this.application   =   application;  
   
  }  
   
  public   void   valueBound(javax.servlet.http.HttpSessionBindingEvent   e)    
  {    
  Vector   activeSessions   =   (Vector)   application.getAttribute("activeSessions");  
  if   (activeSessions   ==   null)  
  {  
  activeSessions   =   new   Vector();  
  }  
   
  JDBCUser   sessionUser   =   (JDBCUser)e.getSession().getAttribute("user");  
  if   (sessionUser   !=   null)  
  {  
  activeSessions.add(e.getSession());  
  }  
  application.setAttribute("activeSessions",activeSessions);  
  }  
   
  public   void   valueUnbound(javax.servlet.http.HttpSessionBindingEvent   e)    
  {  
  JDBCUser   sessionUser   =   (JDBCUser)e.getSession().getAttribute("user");  
  if   (sessionUser   ==   null)  
  {  
  Vector   activeSessions   =   (Vector)   application.getAttribute("activeSessions");  
  if   (activeSessions   !=   null)  
  {  
  activeSessions.remove(e.getSession().getId());  
  application.setAttribute("activeSessions",activeSessions);  
  }  
  }  
  }  
  }  
  [/code]  
   
  假设其中的JDBCUser类是一个任意User类。  
  在执行用户登录时,把User类和HttpSessionBinding类都加入到Session中去。  
  这样,每次用户登录后,在application中的attribute   "activeSessions"这个vector中  
  都会增加一条记录。  
  每当session超时,valueUnbound被触发,在这个vector中删去将要被超时的session.  
   
  [code]  
  public   void   login()  
  throws   ACLException,SQLException,IOException  
  {  
  /*   get   JDBC   User   Class   */  
  if   (user   !=   null)  
  {  
  logout();  
  }  
  {  
  //   if   session   time   out,   or   user   didn't   login,   save   the   target   url   temporary.  
   
  JDBCUserFactory   uf   =   new   JDBCUserFactory();  
   
  if   (   (this.request.getParameter("userID")==null)    
  ||   (this.request.getParameter("password")==null)     )  
  {  
  throw   new   ACLException("Please   input   a   valid   userName   and   password.");  
  }  
   
  JDBCUser   user   =    
  (JDBCUser)   uf.UserLogin(  
  this.request.getParameter("userID"),  
  this.request.getParameter("password")   );  
  user.touchLoginTime();  
  this.session.setAttribute("user",user);  
  this.session.setAttribute("BindingNotify",new   HttpSessionBinding(application));  
  }  
  }  
   
  [/code]  
  Login的时候,把User和这个BindingNotofy目的的类都加入到session中去。  
   
  logout的时候,就要主动在activeSessions这个vector中删去这个session.  
  [code]  
  public   void   logout()  
  throws   SQLException,ACLException  
  {  
  if   (this.user   ==   null    
  &&   this.session.getAttribute("user")==null)  
  {  
  return;  
  }  
   
  Vector   activeSessions   =   (Vector)   this.application.getAttribute("activeSessions");  
  if   (activeSessions   !=   null)  
  {  
  activeSessions.remove(this.session);  
  application.setAttribute("activeSessions",activeSessions);  
  }  
   
  java.util.Enumeration   e   =   this.session.getAttributeNames();  
   
  while   (e.hasMoreElements())  
  {  
  String   s   =   (String)e.nextElement();  
  this.session.removeAttribute(s);  
  }  
  this.user.touchLogoutTime();  
  this.user   =   null;  
  }  
  [/code]  
   
  这两个函数位于一个HttpSessionManager类中.这个类引用了jsp里面的application全局  
  对象。  
  这个类的其他代码和本文无关且相当长,我就不贴出来了。  
   
  下面来看看jsp里面怎么用。  
  假设一个登录用的表单被提交到doLogin.jsp,   表单中包含UserName和password域。  
   
  节选部分片段:  
  [code]  
  <%  
  HttpSessionManager   hsm   =   new   HttpSessionManager(application,request,response);  
  try  
  {  
  hsm.login();  
  }  
  catch   (   UserNotFoundException   e)  
  {  
  response.sendRedirect("InsufficientPrivilege.jsp?detail=User%20does%20not%20exist.");  
  return;  
  }  
  catch   ( InvalidPasswordException   e2)  
  {  
  response.sendRedirect("InsufficientPrivilege.jsp?detail=Invalid%20Password");  
  return;  
  }  
  catch   (   Exception   e3)  
  {  
  %> Error:<%=e3.toString()   %><br>  
  Press   <a   href="login.jsp">Here</a>   to   relogin.  
  <% return;  
  }  
  response.sendRedirect("index.jsp");  
  %>  
  [/code]  
   
  再来看看现在我们怎么得到一个当前在线的用户列表。  
  [code]  
  <body   bgcolor="#FFFFFF">  
  <table   cellspacing="0"   cellpadding="0"   width="100%">  
   
  <tr   >  
      <td   style="width:24px">SessionId  
      </td>  
      <td   style="width:80px"   >User  
      </td>  
      <td   style="width:80px"   >Login   Time  
      </td>  
      <td   style="width:80px"   >Last   access   Time  
      </td>  
  </tr>  
  <%  
  Vector   activeSessions   =   (Vector)   application.getAttribute("activeSessions");  
  if   (activeSessions   ==   null)  
  {  
  activeSessions   =   new   Vector();  
  application.setAttribute("activeSessions",activeSessions);  
  }  
   
  Iterator   it   =   activeSessions.iterator();  
  while   (it.hasNext())  
  {  
  HttpSession   sess   =   (HttpSession)it.next();  
  JDBCUser   sessionUser   =   (JDBCUser)sess.getAttribute("user");  
  String   userId   =   (sessionUser!=null)?sessionUser.getUserID():"None";  
  %>  
  <tr>  
      <td   nowrap=''><%=   sess.getId()   %></td>  
      <td   nowrap=''><%=   userId   %></td>  
      <td   nowrap=''>  
  <%=     BeaconDate.getInstance(   new   java.util.Date(sess.getCreationTime())).getDateTimeString()%></td>  
      <td   class="<%=   stl   %>3"   nowrap=''>  
  <%=     BeaconDate.getInstance(   new   java.util.Date(sess.getLastAccessedTime())).getDateTimeString()%></td>  
  </tr>  
  <%  
  }  
  %>  
  </table>  
  </body>  
  [/code]  
   
  以上的代码从application中取出activeSessions,并且显示出具体的时间。其中  
  BeaconDate类假设为格式化时间的类。  
   
  这样,我们得到了一个察看在线用户的列表的框架。至于在线用户列表分页等功能,  
  与本文无关,不予讨论。  
  这是一个非刷新模型的例子,依赖于session的超时机制。我的同事sonymusic指出很  
  多时候由于各个厂商思想的不同,这有可能是不可信赖的。考虑到这种需求,需要在  
  每个叶面刷新的时候都判断当前用户距离上次使用的时间是否超过某一个预定时间值。  
  这实质上就是自己实现session超时。  
  如果需要实现刷新模型,就必须使用这种每个叶面进行刷新判断的方法。  
   
  曹晓钢  
  2001年12月3日  
  本文版权属于南京丰柏信息技术有限公司。保留一切权力。  
  (若不保留此版权信息,请勿转载)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值