| C114首页 | 新闻 | 技术 | 市场 | 会展 | 黄页 | 人物 | 3G | WiMax | LTE | FTTH | IPTV | IP通信 | NGN 通信人家园 | 通信人博客 | 通信商情网 | 通信人才网 | English |
|
|
关于JSP中基于Session的在线用户统计分析
( 2008/6/20 09:14 )
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方法。 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; }
{ Vector activeSessions = (Vector) application.getAttribute("activeSessions"); if (activeSessions == null) { activeSessions = new Vector(); }
if (sessionUser != null) { activeSessions.add(e.getSession()); } application.setAttribute("activeSessions",activeSessions); }
{ 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); } } } } 假设其中的JDBCUser类是一个任意User类。在执行用户登录时,把User类和HttpSessionBinding类都加入到Session中去。 这样,每次用户登录后,在application中的attribute "activeSessions"这个vector中都会增加一条记录。每当session超时,valueUnbound被触发,在这个vector中删去将要被超时的session。 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.
{ throw new ACLException("Please input a valid userName and password."); }
this.request.getParameter("userID"), this.request.getParameter("password") ); user.touchLoginTime(); this.session.setAttribute("user",user); this.session.setAttribute("BindingNotify",new HttpSessionBinding(application)); } }
Login的时候,把User和这个BindingNotofy目的的类都加入到session中去。logout的时候,就要主动在activeSessions这个vector中删去这个session。
public void logout() throws SQLException,ACLException { if (this.user == null && this.session.getAttribute("user")==null) { return; }
if (activeSessions != null) { activeSessions.remove(this.session); application.setAttribute("activeSessions",activeSessions); }
{ String s = (String)e.nextElement(); this.session.removeAttribute(s); } this.user.touchLogoutTime(); this.user = null; } 这两个函数位于一个HttpSessionManager类中.这个类引用了jsp里面的application全局对象。这个类的其他代码和本文无关且相当长,我就不贴出来了。 下面来看看JSP里面怎么用。 假设一个登录用的表单被提交到doLogin.jsp, 表单中包含UserName和password域。节选部分片段: <% 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"); %> 再来看看现在我们怎么得到一个当前在线的用户列表。
<body bgcolor="#FFFFFF"> <table cellspacing="0" cellpadding="0" width="100%">
<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); }
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())). <td class="<%= stl %>3" nowrap=''> <%= BeaconDate.getInstance( new java.util.Date(sess.getLastAccessedTime())). </tr> <% } %> </table> </body> 以上的代码从application中取出activeSessions,并且显示出具体的时间。其中BeaconDate类假设为格式化时间的类。 这样,我们得到了一个察看在线用户的列表的框架。至于在线用户列表分页等功能,与本文无关,不予讨论。 这是一个非刷新模型的例子,依赖于session的超时机制。我的同事sonymusic指出很多时候由于各个厂商思想的不同,这有可能是不可信赖的。考虑到这种需求,需要在每个叶面刷新的时候都判断当前用户距离上次使用的时间是否超过某一个预定时间值。这实质上就是自己实现session超时。如果需要实现刷新模型,就必须使用这种每个叶面进行刷新判断的方法。
|
·解决JSP开发中Web程序中文显示三种方法 (11-11) ·利用缓冲技术提高JSP程序的性能和稳定性 (11-6) ·介绍JSP程序动态网站环境搭建的详细步骤 (9-11) ·新手看招:JSP中表单数据存储的通用方法 (9-11) 热议文章·中国电信“三不新政” 不改号不换卡无需换终端 (12-2) 评:终端问题与技术问题同在,但是只有产业链和应用上去了,中国电信经营CDMA及未来之... ·诺基亚不是TD的救世主 (12-2) ·中国联通确定各省分公司领导名单 (12-1) ·张成良:基于EPON的FTTB最适合中国 (11-30) ·传新联通鼓励员工内退,变相减人 (11-30) ·重组后的中国联通,何以对抗即将到来的混战 (11-29) ·中电信狂挤CDMA用户水分 一夜少了1333万户 (11-26) ·不能上网下载网络电视的原因与解决方法 (12-2) ·HSDPA对网络规划的影响 (12-2) ·基于IP架构的cdma2000系统A接口协议的设计 (12-2) ·CDMA 1x EV-DO无线网络规划和建设分析 (12-2) ·数字无线网络技术在实时无损检测系统中应用 (12-2) ·六大局域网优化秘技提升网络效率 (12-2) ·次贷危机下,LTE是个新谎言 (10-29) ·全球通信业面临金融危机冲击 对中国企业或利大于弊 (10-27) ·宽带市场不宜“非对称管制” (9-17) ·从校园竞争看重组后电信运营商竞争力对比 (9-8) |
|||||||||||||||
|