JSP中基于Session的在线用户统计剖析
JSP中基于Session的在线用户统计剖析
JSP作为后起之秀可以在服务器编程环境中占据肯定位置,是和它良好支持一系列业界标准密切相干的。Session就是它提供的根底设备之一。作为一个程序员,你可能不介意详细在客户端是如何完成,就方便的完成简略的基于session的用户治理。如今对于解决在线用户,有几种不同的解决方法。
一种是页面刷新由用户控制,服务器端控制一个超时工夫比如30分钟,到了工夫之后用户没有动作就被踢出。这种方法的优点是,假设用户忘了参加,可能防止他人恶意操作。缺陷是,假设你在做一件很耗工夫的事件,超过了这个工夫限度,submit的时分能够要再次面临登陆。假设原来的叶面又是强迫失效的话,就有能够失落你做的工作。在完成的角度来看,这是最简略的,Server端默许完成的就是这样的形式。
另一种模式是,站点采用框架构造,有一个Frame或许隐藏的iframe在始终刷新,这样你永远不会被踢出,然而服务器端为了判别你能否在线,需求定一个发呆工夫,假设超过这个发呆工夫你除了这个主动刷新的叶面外没有刷新其余叶面的话,就以为你已经不在线了。采取这种模式的典型是xici.net。他的优点是可能可能应用始终的刷新完成一些类似server-push的性能,比如网友之间发送消息。
不管哪一种形式,为了完成阅读以后一切的在线用户,还需求做一些额外的工作。servletAPI中没有失去Session列表的API。
可能应用的是Listener.Servlet2.2和2.3规范在这里略微有一些不一样。2.2中HttpSessionBindingListener可能完成当一个HTTPSession中的Attribute变化的时分告诉你的类。而2.3中还引入了HttpSessionAttributeListener.鉴于我利用的环境是Visualageforjava4和JRunserver3.1,他们还不间接支持Servlet2.3的编程,这里我用的是HttpSessionBindingListener.
需求做的事件包括做一个新的类来完成HttpSessionBindingListener接口。这个接口有两个方法:
publicvoidvalueBound(HttpSessionBindingEventevent)
publicvoidvalueUnbound(HttpSessionBindingEventevent)
当你执行Session.addAttribute(String,Object)的时分,假设你已经把一个完成了HttpSessionBindingListener接口的类退出为Attribute,Session会告诉你的类,调用你的valueBound方法。雷同,Session.removeAttribute方法对应的是valueUndound方法。
publicclassHttpSessionBindingimplementsjavax.servlet.http.HttpSessionBindingListener
{
ServletContextapplication=null;
publicHttpSessionBinding(ServletContextapplication)
{
super();
if(application==null)
thrownewIllegalArgumentException("Nullapplicationisnotaccept.");
this.application=application;
}
publicvoidvalueBound(javax.servlet.http.HttpSessionBindingEvente)
{
VectoractiveSessions=(Vector)application.getAttribute("activeSessions");
if(activeSessions==null)
{
activeSessions=newVector();
}
JDBCUsersessionUser=(JDBCUser)e.getSession().getAttribute("user");
if(sessionUser!=null)
{
activeSessions.add(e.getSession());
}
application.setAttribute("activeSessions",activeSessions);
}
publicvoidvalueUnbound(javax.servlet.http.HttpSessionBindingEvente)
{
JDBCUsersessionUser=(JDBCUser)e.getSession().getAttribute("user");
if(sessionUser==null)
{
VectoractiveSessions=(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.
publicvoidlogin()
throwsACLException,SQLException,IOException
{
/*getJDBCUserClass*/
if(user!=null)
{
logout();
}
{
//ifsessiontimeout,oruserdidn'tlogin,savethetargeturltemporary.
JDBCUserFactoryuf=newJDBCUserFactory();
if((this.request.getParameter("userID")==null)
||(this.request.getParameter("password")==null))
{
thrownewACLException("PleaseinputavaliduserNameandpassword.");
}
JDBCUseruser=(JDBCUser)uf.UserLogin(
this.request.getParameter("userID"),
this.request.getParameter("password"));
user.touchLoginTime();
this.session.setAttribute("user",user);
this.session.setAttribute("BindingNotify",newHttpSessionBinding(application));
}
}
Login的时分,把User和这个BindingNotofy目标的类都退出到session中去。logout的时分,就要自动在activeSessions这个vector中删去这个session.
publicvoidlogout()
throwsSQLException,ACLException
{
if(this.user==null&&this.session.getAttribute("user")==null)
{
return;
}
VectoractiveSessions=(Vector)this.application.getAttribute("activeSessions");
if(activeSessions!=null)
{
activeSessions.remove(this.session);
application.setAttribute("activeSessions",activeSessions);
}
java.util.Enumeratione=this.session.getAttributeNames();
while(e.hasMoreElements())
{
Strings=(String)e.nextElement();
this.session.removeAttribute(s);
}
this.user.touchLogoutTime();
this.user=null;
}
这两个函数位于一个HttpSessionManager类中.这个类引用了jsp里面的application全局对象。这个类的其余代码和本文有关且相当长,我就不贴进去了。
下面来看看jsp里面怎样用。
假定一个登录用的表单被提交到doLogin.jsp,表单中蕴含UserName和password域。节选局部片段:
<%
HttpSessionManagerhsm=newHttpSessionManager(application,request,response);
try
{
hsm.login();
}
catch(UserNotFoundExceptione)
{
response.sendRedirect("InsufficientPrivilege.jsp?detail=User%20does%20not%20exist.");
return;
}
catch(InvalidPasswordExceptione2)
{
response.sendRedirect("InsufficientPrivilege.jsp?detail=Invalid%20Password");
return;
}
catch(Exceptione3)
{
%>Error:<%=e3.toString()%><br>
Press<ahref="login.jsp">Here</a>torelogin.
<%return;
}
response.sendRedirect("index.jsp");
%>
再来看看如今咱们怎样失去一个以后在线的用户列表。
<bodybgcolor="#FFFFFF">
<tablecellspacing="0"cellpadding="0"width="100%">
<tr>
<tdstyle="width:24px">SessionId
</td>
<tdstyle="width:80px">User
</td>
<tdstyle="width:80px">LoginTime
</td>
<tdstyle="width:80px">LastaccessTime
</td>
</tr>
<%
VectoractiveSessions=(Vector)application.getAttribute("activeSessions");
if(activeSessions==null)
{
activeSessions=newVector();
application.setAttribute("activeSessions",activeSessions);
}
Iteratorit=activeSessions.iterator();
while(it.hasNext())
{
HttpSessionsess=(HttpSession)it.next();
JDBCUsersessionUser=(JDBCUser)sess.getAttribute("user");
StringuserId=(sessionUser!=null)?sessionUser.getUserID():"None";
%>
<tr>
<tdnowrap=''><%=sess.getId()%></td>
<tdnowrap=''><%=userId%></td>
<tdnowrap=''>
<%=BeaconDate.getInstance(newjava.util.Date
(sess.getCreationTime())).getDateTimeString()%></td>
<tdclass="<%=stl%>3"nowrap=''>
<%=BeaconDate.getInstance(newjava.util.Date
(sess.getLastAccessedTime())).getDateTimeString()%></td>
</tr>
<%
}
%>
</table>
</body>
以上的代码从application中取出activeSessions,并且显示出详细的工夫。其中BeaconDate类假定为格式化工夫的类。
这样,咱们失去了一个察看在线用户的列表的框架。至于在线用户列表分页等性能,与本文有关,不予探讨。
这是一个非刷新模型的例子,依赖于session的超机遇制。我的同事sonymusic指出很多时分因为各个厂商思维的不同,这有能够是不可信任的。思考到这种须要,需求在每个叶面刷新的时分都判别以后用户距离上次利用的工夫能否超过某一个预约工夫值。这本质上就是本人完成session超时。假设需求完成刷新模型,就必须利用这种每个叶面停止刷新判别的方法。