在DWR中,我们可以通过WebContextFactory.get()来取得一个WebContext对象,进而通过WebContext的getScriptSession()取得ScriptSession对象。
但是要注意,在我们自定义的Servlet中,我们也可以通过WebContextFactory.get()来取得一个WebContext,但是这种方法却不能取得ScriptSession对象。因为,此WebContext对象其实不是通过DWR的上下文环境得到的,所以,就根本没有创建ScriptSession对象。
假设这种方式也能得到ScriptSession的话,那么我们实现“推”也就可以不局限在DWR的上下文环境中了,那么其灵活性就会大很多了。所以,这就是我们不能在Servlet中实现推的原因。
在我们需要推送的页面中,如果你刷新一下,那么就提交一个Http的request,此时,如果是第一次,那么就会创建一个httpSession对象,同时,请求由DwrServlet来处理后,就会创建一个ScriptSession.这个ScriptSession会和你的request请求的URI绑定放在一个由ScriptSessionManager维护的Map里面(这里面其实是一个URI对应的Set,在Set里面放置的是URI绑定的所有ScriptSession)。
当你刷新的时候,同样的一个HttpSession,却会创建一个新的ScriptSession,然后绑定到对应的URI上。
向所有的页面访问者推送
当我们想向所有的页面访问者推送的时候,我们只需要,取得所有的页面访问者,就可以“推”了。如何取得所有的页面访问者?
DWR3.0可以通过
//得到所有ScriptSession
Collection<ScriptSession> sessions = Browser.getTargetSessions();
DWR2.x可以通过
Collection pages = webContext.getScriptSessionsByPage("/yourPage.jsp");
在上面的推送中产生的问题
上面的方法已经可以实现向所有的访问者推送。但是问题是,在客户端,如果用户刷新一次或多次,那么,Collection里面可能就保存了很多的无用的ScriptSession,所以不仅仅会影响性能问题,更重要的是,可能就不能实现你想要的功能。如何管理有效的ScriptSession
由于上面的问题,我们就需要自己管理ScriptSession。其实,有效地HttpSession,就是那个和当前的HttpSession匹配的ScriptSession。
所以,我们就可以自己维护一个Map,在这个Map里面,我们定义key就是HttpSession的Id,其值就是ScriptSession对象。
在每一次页面载入的时候,都去注册此ScriptSession,那么就会把新的ScriptSession绑定到httpSession上面了。
在DWR3.0中推出了 ScriptSessionListener用来监听ScriptSession的创建及销毁事件。我们可以使用该监听器来维护我们自己的Map。
具体实现:
1.新建一个类实现ScriptSessionListener接口
public class PushListener implements ScriptSessionListener { //维护一个Map key为session的Id, value为ScriptSession对象 public static final Map<String,ScriptSession> sessionMap = new HashMap<String,ScriptSession>(); private static PushListener listener; private PushListener() { } public static PushListener getInstance(){ if(listener==null){ listener = new PushListener(); } return listener; } /** * 会话/长连接创建时调用的方法 */ public void sessionCreated(ScriptSessionEvent ev) { HttpSession httpSession = WebContextFactory.get().getSession(); //本系统登录时在HttpSession中放置系统用户 if(httpSession.getAttribute("users")==null){ return; } String userId = ((Users) httpSession.getAttribute("users")).getUserId() + ""; // 每一个scriptsession对应唯一客户端 ev.getSession().setAttribute("userId", userId); // 保证每一个httpSession在一个时间点只存在一个scriptsession sessionMap.put(httpSession.getId(), ev.getSession()); } /** * 会话(长连接)关闭时调用的方法 */ public void sessionDestroyed(ScriptSessionEvent ev) { WebContext webContext = WebContextFactory. get(); HttpSession httpSession = webContext.getSession(); sessionMap.remove(httpSession.getId()); //移除scriptSession } }
2.使用ScriptSessionFilter过滤
如果我们不想要给所有的客户端推送消息,只想给特定的客户端推送,那么我们可以使用
ScriptSessionFilter来实现。在filter中去判定session中的Attribute值是不是我们给定的。
我们需要一个ScriptSessionFilter对象
public class PushFilter implements ScriptSessionFilter { private String userId; public PushFilter() { } public PushFilter(String id){ this.userId = id; } /** * 匹配指定的客户端 */ @Override public boolean match(ScriptSession session) { if (session.getAttribute("userId") == null){ return false; }else{ return (session.getAttribute("userId")).equals(userId); } }}
3.实现run方法
public class PushRunable implements Runnable { private String message; private ScriptBuffer script = new ScriptBuffer(); public PushRunable(){ } public PushRunable(String msg){ this.message = msg; } /** * 新启的线程,执行的业务 */ @Override public void run() { script.appendCall("showMessage", message); Collection<ScriptSession> sessions = Browser.getTargetSessions(); for (ScriptSession scriptSession : sessions) { //添加待执行的脚本到dwr excution池中 scriptSession.addScript(script); } } }
4.客户端注册
public class PushUtil { /** * 脚本接口(客户端注册接口),在页面调用 * * @param userId */ public void onPageLoad(String userId) { ScriptSession scriptSession = WebContextFactory.get() .getScriptSession(); scriptSession.setAttribute("userId", userId); initInfo(); } // 管理scriptsession private void initInfo() { Container container = ServerContextFactory.get().getContainer(); ScriptSessionManager manager = container .getBean(ScriptSessionManager.class); Collection<ScriptSession> sessions = manager.getAllScriptSessions(); Collection<ScriptSession> myManagerSessions = PushListener.sessionMap .values(); // 未过滤dwr容器中的scriptsession System.out.println("before..."); for (ScriptSession scriptSession : sessions) { if (!scriptSession.isInvalidated()) { System.out.print(scriptSession); } } // 根据自定义的session管理器过滤掉dwr容器中无效的scriptsession for (ScriptSession scriptSession : sessions) { if (!myManagerSessions.contains(scriptSession)) { scriptSession.invalidate(); } } // 过滤后dwr容器中的scriptsession System.out.println(); System.out.println("after..."); for (ScriptSession scriptSession : sessions) { if (!scriptSession.isInvalidated()) { System.out.print(scriptSession); } } // 给dwr的scriptsession管理器植入监听器,监听器中有自己的业务逻辑和对scriptsession的管理逻辑 ScriptSessionListener listener = PushListener.getInstance(); manager.addScriptSessionListener(listener); } /** * 发送方法,供外部模块调用 * * @param userid * @param message */ public static void sendMessageAuto(String userid, String message) { Browser.withAllSessionsFiltered(new PushFilter(userid), new PushRunable(message)); } }