问题域::1、同帐号登录:若此帐号已登录,不可再次登录(与QQ模式相反)。

         2、登录人数限制,超过、已达人数限制则提示:系统繁忙,稍后再试。

解决思路::使用HttpSessionAttributeListener监听器(虽然我同时使用了HttpSessionListener不过感觉不好操作)

知识储备::HttpSessionAttributeListener中有attributeAdd、attributeRemove、attributeReplace3个方法。

对session的setAttribute、removeAttribute将触发attributeAdd、attributeRemove方法,对同一个session的同一个attribute进行重复设置将触发attributeReplace方法。


HttpSessionListener不好操作的原因:只要访问jsp页面便会创建session(访问html并不会创建session,在server端,如servlet中调用HttpServletRequest.getSession(true)才会创建),jsp是动态页,本质就是个servlet。我的login.jsp显然是个jsp,当我在监听器中invalidate一个session,返回登录页,马上就又创建了一个session。这是我感觉不清楚的地方,功夫没到家。


具体实现:

监听器代码

public class OnlineListener implements HttpSessionListener,
        HttpSessionAttributeListener {
    private static List<SessionAndUser> sessions;
    static int delS = -1;
    static boolean flag = false;
    static {
        if (sessions == null) {
            sessions = Collections
                    .synchronizedList(new ArrayList<SessionAndUser>());
        }
    }
    public void sessionCreated(HttpSessionEvent hse) {
        System.out.println(hse.getSession() + "-" + new Date());
        System.out.println(hse.getSession() + "-" + new Date());
    }
    public void sessionDestroyed(HttpSessionEvent hse) {
        System.out.println("-------------sessionDestroyed()-----------");
        System.out.println(hse.getSession() + " "
                + new Date(hse.getSession().getLastAccessedTime()));
        System.out.println(hse.getSession() + " " + new Date());
    }
    public void attributeAdded(HttpSessionBindingEvent e) {
        System.out.println("-------------*start added*-----------------------"
                + sessions.size());
        HttpSession session = e.getSession();
        ActionContext ctx = ActionContext.getContext();
        boolean newOne = true;
        String attrName = e.getName();
        // 登录
        if (attrName.equals(Constant.USER_NAME)) {
            // 检查登录人数
            if (sessions.size() >= Constant.USER_LIMIT) {
                newOne = false;
                ctx.put("timeoutMSG", "serverBusy");
            }
            String nowUser = (String) e.getValue();
            // 遍历所有session,检查是否已经登录,若是则提示已经登录
            for (int i = sessions.size() - 1; i >= 0; i--) {
                SessionAndUser tem = sessions.get(i);
                if (tem.getUserName().equals(nowUser)) {
                    newOne = false;
                    ctx.put("timeoutMSG", "beenLoged");// tem.getSession().invalidate();//
                                                        // 同账号顶替登录,自动调用remove
                    break;
                }
            }
            // 新登录帐号添加进账户维护列表
            if (newOne) {
                SessionAndUser sau = new SessionAndUser();
                sau.setUserName(nowUser);
                sau.setSession(session);
                sau.setSid(session.getId());
                sessions.add(sau);
            }
        }
    }
    public void attributeRemoved(HttpSessionBindingEvent e)
            throws IllegalStateException {
        HttpSession session = e.getSession();
        System.out
                .println("-------------*start Removed*-----------------------"
                        + sessions.size());
        if (delS > -1) {
            if (flag) {
                sessions.remove(delS);
                flag = false;
            }
        } else {
            // 登录
            String attrName = e.getName();
            if (attrName.equals(Constant.USER_NAME)) {
                String nowUser = (String) e.getValue();
                // 遍历所有session
                for (int i = sessions.size() - 1; i >= 0; i--) {
                    SessionAndUser tem = sessions.get(i);
                    if (tem.getUserName().equals(nowUser)) {
                        sessions.remove(i);
                        break;
                    }
                }
            }
        }
    }
    public void attributeReplaced(HttpSessionBindingEvent e) {
        HttpSession session = e.getSession();
        System.out
                .println("-------------*start replace*-----------------------"
                        + sessions.size());
        String attrName = e.getName();
        delS = -1;
        // 登录
        if (attrName.equals(Constant.USER_NAME)) {
            // User nowUser = (User) e.getValue();//old value
            String nowUser = (String) session.getAttribute(Constant.USER_NAME);// 当前session中的user
            // 遍历所有session
            for (int i = sessions.size() - 1; i >= 0; i--) {
                SessionAndUser tem = sessions.get(i);
                if (tem.getUserName().equals(nowUser)
                        && !tem.getSid().equals(session.getId())) {
                    System.out.println("Remove:invalidate 1!");
                    delS = i;
                    flag = true;
                } else if (tem.getSid().equals(session.getId())) {
                    tem.setUserName(nowUser);
                }
            }
            if (delS != -1) {
                sessions.get(delS).getSession().invalidate();// 失效时自动调用了remove方法。也就会把它从sessions中移除了
            }
        }
    }
}

代码主要思路是定义一个静态List<SessionAndUser>存放session和帐号名称。


登录的Action中获得监听器返回值并处理的代码

session.setAttribute(Constant.USER_NAME, operator.getUsername());
        ActionContext ctx = ActionContext.getContext();
        if("serverBusy".equals(ctx.get("timeoutMSG"))){
            ctx.put("timeoutMSG", "服务器繁忙,请稍后再试");
            return "jump";
        }
        if("beenLoged".equals(ctx.get("timeoutMSG"))){
            ctx.put("timeoutMSG", "此账户在别处登录");
            return "jump";
        }


页面捕获提示信息代码

<%@taglib prefix="s" uri="/struts-tags"%>
<s:property value="#attr.timeoutMSG" />


以上。