使用DWR实现消息推送

dwr支持如下3种模式的消息推送:

  • Polling,浏览器每隔一段时间向服务器发出请求,查看是否有更新的内容;
  • Comet,
  • Piggyback,服务器等待浏览器下一次发出请求时,将更新的内容合并到响应一起返回。

默认使用Piggyback模式,使用Polling/Comet模式需要额外的配置。

DWR简单入门介绍了基于DWR构建项目。

使用Maven构建项目,在pom.xml中添加如下配置

<repositories>
    <repository>
        <!-- Please consider setting up your own on-site repository proxy such 
            as with Nexus and pointing the url element below at that instead -->
        <id>oss-sonatype-snapshots</id>
        <name>OSS Sonatype Snapshots Repository</name>
        <url>http://oss.sonatype.org/content/repositories/snapshots</url>
        <releases>
            <enabled>false</enabled>
        </releases>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
</repositories>

<dependencies>
...
    <dependency>
        <groupId>org.directwebremoting</groupId>
        <artifactId>dwr</artifactId>
        <version>3.0.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
    <!-- 使用Tomcat需要额外添加的依赖包 -->
    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>catalina</artifactId>
        <version>6.0.44</version>
    </dependency>
</dependencies>

使用的容器是Tomcat(与使用Jetty的配置不同),在web.xml中添加如下配置

<servlet>
    <servlet-name>dwr-invoker</servlet-name>
    <servlet-class>org.directwebremoting.server.tomcat.DwrCometProcessor</servlet-class>
    <init-param>
        <param-name>debug</param-name>
        <param-value>true</param-value>
    </init-param>
    <init-param>
        <!-- 2.0 RC3之前的参数名为pollAndCometEnabled -->
        <param-name>activeReverseAjaxEnabled</param-name>
        <param-value>true</param-value>
    </init-param>
    <init-param>
        <param-name>maxWaitAfterWrite</param-name>
        <param-value>1000</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>dwr-invoker</servlet-name>
    <url-pattern>/dwr/*</url-pattern>
</servlet-mapping>

dwr.xml中的配置

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dwr PUBLIC
    "-//GetAhead Limited//DTD Direct Web Remoting 3.0//EN"
    "http://getahead.org/dwr/dwr30.dtd">

<dwr>
    <allow>
        <create creator="new" javascript="messagePush">
            <param name="class" value="me.util.MessagePushUtil" />
        </create>
    </allow>
</dwr>

采用的调试方式是,打开接收页面(receiver.jsp)和发送页面(sender.jsp),在发送页面将消息发送至服务端,由服务端将消息推送至接受页面。JSP文件中使用JQuery替代了dwr提供的<script type='text/javascript' src='/dwr/util.js'></script>

接收页面(receiver.jsp)

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page isELIgnored="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="/css/easyui.css">
<link rel="stylesheet" type="text/css" href="/css/demo.css">
<script type="text/javascript" src="/js/jquery.js"></script>
<script type="text/javascript" src="/js/jquery.easyui.min.js"></script>
<script type='text/javascript' src='/dwr/engine.js'></script>
<script type='text/javascript' src='/dwr/interface/messagePush.js'></script>
<title>消息推送</title>
</head>
<body>
    <h2>接受消息推送</h2>
</body>
<script>
    $(function() {
        dwr.engine.setActiveReverseAjax(true);
        dwr.engine.setNotifyServerOnPageUnload(true);
        messagePush.onPageLoad('<%=session.getAttribute("uid")%>');
    });

    function showMessage(autoMessage) {
        $.messager.show({
            title : "消息推送",
            msg : autoMessage,
            showType : 'slide',
            timeout : 5000
        });
    }
</script>
</html>

发送页面(sender.jsp)

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page isELIgnored="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="./css/easyui.css">
<link rel="stylesheet" type="text/css" href="./css/demo.css">
<script type="text/javascript" src="./js/jquery.js"></script>
<script type="text/javascript" src="./js/jquery.easyui.min.js"></script>
<script type='text/javascript' src='/dwr/engine.js'></script>
<script type='text/javascript' src='/dwr/interface/messagePush.js'></script>
<title>消息推送</title>
</head>
<body>
    <h2>发送消息推送</h2>
    <div id="p" class="easyui-panel"
        style="width: 400px; height: 100x; padding: 10px;">
        <div>
            ID:<input id="uid" style="width: 40%; height: 25px">消息:<input
                id="msg" style="width: 40%; height: 25px"></br> </br> <a
                href="javascript:void(0)" style="width: 30%; height: 25px"
                onclick="sendMsg()">发送</a>
        </div>
    </div>
</body>
<script>
    function sendMsg() {
        messagePush.sendMessageAuto($('#uid').val(), $('#msg').val());
        $('#uid').val('');
        $('#msg').val('');
    }
</script>
</html>

me/util/MessagePushUtil.java中的内容

package me.util;

import java.util.Collection;

import javax.servlet.ServletException;

import org.apache.log4j.Logger;
import org.directwebremoting.Browser;
import org.directwebremoting.ScriptBuffer;
import org.directwebremoting.ScriptSession;
import org.directwebremoting.ScriptSessionFilter;
import org.directwebremoting.WebContextFactory;

import me.message.MessagePushServlet;

public class MessagePushUtil {

    private static final Logger LOGGER = Logger.getLogger(MessagePushUtil.class);

    public void onPageLoad(String userID) {

        ScriptSession scriptSession = WebContextFactory.get().getScriptSession();
        // 工厂方法get()返回WebContext实例,通过WebContext获取servlet参数
        // ScriptSession与HttpSession类似
        scriptSession.setAttribute("uid", userID);

        MessagePushServlet mpServlet = new MessagePushServlet();
        try {
            mpServlet.init();
            LOGGER.info(String.format("消息推送初始化成功,uid:%s", userID));
        } catch (ServletException e) {
            LOGGER.error(String.format("消息推送初始化错误,uid:%s", userID));
        }

    }

    /**
     * 根据userID向指定用户推送消息
     * 
     * @param userID
     * @param autoMessage
     */
    public void sendMessageAuto(String userID, String autoMessage) {

        final String uid = userID;
        final String message = autoMessage;

        Browser.withAllSessionsFiltered(new ScriptSessionFilter() {
            // 实现过滤器中的match()方法
            public boolean match(ScriptSession session) {
                if (session.getAttribute("uid") == null)
                    return false;
                else
                    return (session.getAttribute("uid")).equals(uid);
            }
        }, new Runnable() {

            private ScriptBuffer script = new ScriptBuffer();

            public void run() {
                // 调用JSP中定义的showMessage()方法,实现消息的前端显示
                script.appendCall("showMessage", message);
                Collection<ScriptSession> sessions = Browser.getTargetSessions();
                for (ScriptSession scriptSession : sessions) {
                    scriptSession.addScript(script);
                }
                LOGGER.info(String.format("向用户推送消息,uid:%s,message:%s", uid, message));
            }
        });
    }
}

me/message/MessagePushServlet.java中的内容,覆盖DwrServlet中的init()方法实现ScriptSession监听器,在页面中加入engine.js时,ScriptSession创建,默认由org.directwebremoting.impl.DefaultScriptSessionManager管理,当发生unload事件时,DefaultScriptSessionManager会被通知销毁ScriptSession。

package me.message;

import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;
import org.directwebremoting.Container;
import org.directwebremoting.ServerContextFactory;
import org.directwebremoting.WebContextFactory;
import org.directwebremoting.event.ScriptSessionEvent;
import org.directwebremoting.event.ScriptSessionListener;
import org.directwebremoting.extend.ScriptSessionManager;
import org.directwebremoting.servlet.DwrServlet;

public class MessagePushServlet extends DwrServlet {

    private static final long serialVersionUID = 4298890285665323894L;
    private static final Logger LOGGER = Logger.getLogger(MessagePushServlet.class);

    @Override
    public void init() throws ServletException {

        Container container = ServerContextFactory.get().getContainer();
        // 工厂方法get()返回ServerContext实例
        ScriptSessionManager manager = container.getBean(ScriptSessionManager.class);
        ScriptSessionListener listener = new ScriptSessionListener() {
            public void sessionCreated(ScriptSessionEvent ev) {
                HttpSession session = WebContextFactory.get().getSession();

                String userID = (String) session.getAttribute("uid");
                LOGGER.info("a ScriptSession is created");
                ev.getSession().setAttribute("uid", userID);
            }

            public void sessionDestroyed(ScriptSessionEvent ev) {
                LOGGER.info("a ScriptSession is distroyed");
            }
        };
        manager.addScriptSessionListener(listener);
    }
}

运行前还要修改Tomcat下的server.xml中的配置

<!--
<Connector connectionTimeout="20000" port="8080" 
protocol="HTTP/1.1" redirectPort="8443"/>
-->
<Connector URIEncoding="UTF-8" connectionTimeout="20000" port="8080"
protocol="org.apache.coyote.http11.Http11NioProtocol" redirectPort="8443"/>

运行项目,打开http://localhost:8080/receiver.jsp,页面加载时,调用

messagePush.onPageLoad('<%=session.getAttribute("uid")%>');

调用服务端的me.util.MessagePushUtil.onPageLoad(String userID)方法,初始化成功(可以使用字符串,如"123456789",替换'<%=session.getAttribute("uid")%>')。打开http://localhost:8080/sender.jsp,填写刚才的uid(123456789)和消息内容,点击发送,调用

messagePush.sendMessageAuto($('#uid').val(), $('#msg').val());

调用服务端的me.util.MessagePushUtil.sendMessageAuto(String userID, String autoMessage)方法,调用receiver.jsp的showMessage(autoMessage)方法,可以在接收页面看到来自服务端推送的消息。

页面预览如下,

发送页面
发送页面

接收页面
接收页面

除此之外,在log4j.properties中添加类似的配置

log4j.rootCategory=INFO, console
log4j.category.org.directwebremoting.log=INFO, dwr, console

log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%5p] %d{yyyy-MM-dd HH:mm:ss} %l - %m%n

log4j.appender.dwr=org.apache.log4j.RollingFileAppender
log4j.appender.dwr.File=../logs/dwr.log
log4j.appender.dwr.MaxFileSize=100KB
log4j.appender.dwr.MaxBackupIndex=1
log4j.appender.dwr.layout=org.apache.log4j.PatternLayout
log4j.appender.dwr.layout.ConversionPattern=[%5p] %d{yyyy-MM-dd HH:mm:ss} %l - %m%n

参考资料

DWR3实现服务器端向客户端精确推送消息
DWR简单入门
Reverse Ajax
Logging

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,497评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,678评论 6 342
  • 推送技术产生场景: --服务器端主动性: 客户端与服务器交互都是客户端主动的, 服务器一般不能主动与客户端进行数据...
    原军锋阅读 34,371评论 4 60
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,497评论 18 399
  • 一. Java基础部分.................................................
    wy_sure阅读 3,774评论 0 11