JSP作为后起之秀能够在服务器编程环境中占据一定地位是和它良好支持一系列业界标准密切相关的Session就是它提供的基础设施之一作为一个程序员你可以不介意具体在客户端是如何实现就方便的实现简单的基于session的用户管理现在对于处理在线用户有几种不同的处理方法
一种是页面刷新由用户控制服务器端控制一个超时时间比如分钟到了时间之后用户没有动作就被踢出这种方法的优点是如果用户忘了退出可以防止别人恶意操作缺点是如果你在做一件很耗时间的事情超过了这个时间限制submit的时候可能要再次面临登陆如果原来的叶面又是强制失效的话就有可能丢失你做的工作在实现的角度来看这是最简单的Server端默认实现的就是这样的模式
另一种方式是站点采用框架结构有一个Frame或者隐藏的iframe在不断刷新这样你永远不会被踢出但是服务器端为了判断你是否在线需要定一个发呆时间如果超过这个发呆时间你除了这个自动刷新的页面外没有刷新其他页面的话就认为你已经不在线了采取这种方式的典型是xicinet 他的优点是可以可以利用不断的刷新实现一些类似serverpush的功能比如网友之间发送消息
不管哪一种模式为了实现浏览当前所有的在线用户还需要做一些额外的工作Servlet API中没有得到Session列表的API
可以利用的是Listener Servlet 和规范在这里略微有一些不一样中HttpSessionBindingListener可以实现当一个HTTPSession中的Attribute变化的时候通知你的类而中还引入了HttpSessionAttributeListener鑒于我使用的环境是Visual age for Java 和JRun server 他们还不直接支持Servlet 的编程这里我用的是HttpSessionBindingListener
需要做的事情包括做一个新的类来实现HttpSessionBindingListener接口这个接口有两个方法
public void valueBound(HttpSessionBindingEvent event)
public void valueUnbound(HttpSessionBindingEvent event)
当你执行SessionaddAttribute(StringObject)的时候如果你已经把一个实现了HttpSessionBindingListener接口的类加入为AttributeSession会通知你的类调用你的valueBound方法相反SessionremoveAttribute方法对应的是valueUndound方法
public class HttpSessionBinding implements javaxservlet
{
ServletContext application = null;
public HttpSessionBinding(ServletContext application)
{
super();
if (application ==null)
throw new IllegalArgumentException("Null application is not accept");
thisapplication = application;
public void valueBound(javaxservlet
{
Vector activeSessions = (Vector) applicationgetAttribute("activeSessions");
if (activeSessions == null)
{
activeSessions = new Vector();
JDBCUser sessionUser = (JDBCUser)egetSession()getAttribute("user");
if (sessionUser != null)
{
activeSessionsadd(egetSession());
}
applicationsetAttribute("activeSessions"activeSessions);
public void valueUnbound(javaxservlet
{
JDBCUser sessionUser = (JDBCUser)egetSession()getAttribute("user");
if (sessionUser == null)
{
Vector activeSessions = (Vector) applicationgetAttribute("activeSessions");
if (activeSessions != null)
{
activeSessionsremove(egetSession()getId());
applicationsetAttribute("activeSessions"activeSessions);
}
}
}
假设其中的JDBCUser类是一个任意User类在执行用户登录时把User类和HttpSessionBinding类都加入到Session中去
这样每次用户登录后在application中的attribute "activeSessions"这个vector中都会增加一条记录每当session超时valueUnbound被触发在这个vector中删去将要被超时的session
public void login()
throws ACLExceptionSQLExceptionIOException
{
/* get JDBC User Class */
if (user != null)
{
logout();
}
{
// if session time out or user didnt login save the target url temporary
JDBCUserFactory uf = new JDBCUserFactory();
if ( (thisrequestgetParameter("userID")==null) || (thisrequestgetParameter("password")==null) )
{
throw new ACLException("Please input a valid userName and password");
JDBCUser user = (JDBCUser) ufUserLogin(
thisrequestgetParameter("userID")
thisrequestgetParameter("password") );
usertouchLoginTime();
thissessionsetAttribute("user"user);
thissessionsetAttribute("BindingNotify"new HttpSessionBinding(application));
}
Login的时候把User和这个BindingNotofy目的的类都加入到session中去logout的时候就要主动在activeSessions这个vector中删去这个session
public void logout()
throws SQLExceptionACLException
{
if (thisuser == null && thissessiongetAttribute("user")==null)
{
return;
Vector activeSessions = (Vector) thisapplicationgetAttribute("activeSessions");
if (activeSessions != null)
{
activeSessionsremove(thissession);
applicationsetAttribute("activeSessions"activeSessions);
javautilEnumeration e = thissessiongetAttributeNames();
while (ehasMoreElements())
{
String s = (String)enextElement();
thissessionremoveAttribute(s);
}
thisusertouchLogoutTime();
thisuser = null;
这两个函数位于一个HttpSessionManager类中这个类引用了jsp里面的application全局对象这个类的其他代码和本文无关且相当长我就不贴出来了
下面来看看JSP里面怎么用
假设一个登录用的表单被提交到doLoginjsp 表单中包含UserName和password域节选部分片段
<%
HttpSessionManager hsm = new HttpSessionManager(applicationrequestresponse);
try
{
hsmlogin();
}
catch ( UserNotFoundException e)
{
responsesendRedirect("InsufficientPrivilegejsp?detail=User%does%not%exist");
return;
}
catch ( InvalidPasswordException e)
{
responsesendRedirect("InsufficientPrivilegejsp?detail=Invalid%Password");
return;
}
catch ( Exception e)
{
%> Error:<%=etoString() %><br>
Press <a href="loginjsp">Here</a> to relogin
<% return;
}
responsesendRedirect("indexjsp");%>
再来看看现在我们怎么得到一个当前在线的用户列表
<body bgcolor="#FFFFFF">
<table cellspacing="" cellpadding="" width="%">
<tr >
<td style="width:px">SessionId
</td>
<td style="width:px" >User
</td>
<td style="width:px" >Login Time
</td>
<td style="width:px" >Last Access Time
</td>
</tr>
<%
Vector activeSessions = (Vector) applicationgetAttribute("activeSessions");
if (activeSessions == null)
{
activeSessions = new Vector();
applicationsetAttribute("activeSessions"activeSessions);
Iterator it = activeSessionsiterator();
while (ithasNext())
{
HttpSession sess = (HttpSession)itnext();
JDBCUser sessionUser = (JDBCUser)sessgetAttribute("user");
String userId = (sessionUser!=null)?sessionUsergetUserID():"None";
%>
<tr>
<td nowrap=><%= sessgetId() %></td>
<td nowrap=><%= userId %></td>
<td nowrap=>
<%= BeaconDategetInstance( new JavautilDate(sessgetCreationTime()))getDateTimeString()%></td>
<td class="<%= stl %>" nowrap=>
<%= BeaconDategetInstance( new javautilDate(sessgetLastAccessedTime()))getDateTimeString()%></td>
</tr>
<%
}
%>
</table>
</body>
以上的代码从application中取出activeSessions并且显示出具体的时间其中BeaconDate类假设为格式化时间的类
这样我们得到了一个察看在线用户的列表的框架至于在线用户列表分页等功能与本文无关不予讨论
这是一个非刷新模型的例子依赖于session的超时机制我的同事sonymusic指出很多时候由于各个厂商思想的不同这有可能是不可信赖的考虑到这种需求需要在每个叶面刷新的时候都判断当前用户距离上次使用的时间是否超过某一个预定时间值这实质上就是自己实现session超时如果需要实现刷新模型就必须使用这种每个叶面进行刷新判断的方法