pushlet 学习

标签: pushlet 学习 | 发表时间:2015-04-30 15:37 | 作者:z724130632
出处:http://www.iteye.com

转自: http://blog.csdn.net/houpengfei111/article/details/7498481

 

网络资源:

一、简述:

    pushlet是一种comet实现,在servlet机制下,数据从server端的java对象直接推送(push)到(动态)HTML页面,而无需任何java applet或者插件的帮助。

二、使用pushlet需要做哪些准备工作

1.下载pushlet

http://sourceforge.net/projects/pushlets/files/pushlets/2.0.4/pushlet-2.0.4.zip/download

2.eclipse 或者myeclipse搭建简单项目

(1)创建工程

a. File à new à projectàweb project   项目名称为:  pushlet-demo

(2)加依赖文件

a.在下载文件的webapps 演示文件夹中找寻文件

b.在src目录中添加  log4j.properties  pushlet.properties   sources.properties ,添加后修改sources.properties  清空演示配置,准备添加自己的配置

source1=nl.justobjects.pushlet.test.TestEventPullSources$TemperatureEventPullSource

source2=nl.justobjects.pushlet.test.TestEventPullSources$SystemStatusEventPullSource

source3=nl.justobjects.pushlet.test.TestEventPullSources$PushletStatusEventPullSource

source4=nl.justobjects.pushlet.test.TestEventPullSources$AEXStocksEventPullSource

source5=nl.justobjects.pushlet.test.TestEventPullSources$WebPresentationEventPullSource

source6=nl.justobjects.pushlet.test.TestEventPullSources$PingEventPullSource

注意:可以不清空,以上斜体部分在sources.properties注释掉即可。

c.添加依赖jar包

在下载文件的lib目录下复制 pushlet.jar  pushletclient.jar导入到自己工程的/WEB-INF/lib 

d.配置web.xml

<servlet>

      <servlet-name>pushlet</servlet-name>                  <servlet-class>nl.justobjects.pushlet.servlet.Pushlet</servlet-class>

注意:此处名字太长如果记不住可以利用eclipse等工具随便下个类中用快捷键提示得到。

      <load-on-startup>1</load-on-startup>

</servlet>

 

<servlet-mapping>

       <servlet-name>pushlet</servlet-name>

        <url-pattern>/pushlet.srv</url-pattern>

 注意:此处默认为/pushlet.srv,一般无需改动,要想改动需要在对应的js文件。

</servlet-mapping>

    

(3)添加实例

a. 创建数据源

package com.source;

 

import java.io.Serializable;

import nl.justobjects.pushlet.core.Event;

import nl.justobjects.pushlet.core.EventPullSource;

 

publicclass HelloWorldEventPullSource  implements Serializable{

   privatestaticfinallong serialVersionUID = 1L;

   staticpublicclass HelloWorldEvent  extends EventPullSource{

      @Override

       protectedlong getSleepTime() {

           return 1000; //刷新时间

      }

      @Override

       protected Event pullEvent() {

          Event event =Event. createDataEvent("/source/event");//事件标识

          注意:此处”/source/event”将对应页面js代码中的PjoinListen中的参数

          event.setField("msg", "hello,world");//封装参数

           return event;

      }

  }

}

    

b. 配置数据源 

sources.properties  进行数据源配置

添加source1=com.source.HelloWorldEventPullSource$HelloWorldEvent

  注意: HelloWorldEventPullSourc这是类名,HelloWorldEvent是内部类名。

c. 页面调用 

Index.html

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

  <head>

    <title>index.html</title>

    <meta http-equiv="content-type" content="text/html; charset=UTF-8">

<script type="text/javascript" src="js/ajax-pushlet-client.js"></script> 

<!—js 从演示项目的  lib 下copy -->

  <script type="text/javascript">

      PL._init(); 

      PL.joinListen('/source/event');  //事件标识 在数据源中引用

      function onData(event) { 

          alert(event.get("msg"));

        }

    </script>

  </head>

  <body>

  </body>

</html>

4.   测试  http://localhost:8080/pushlet-demo/index.html

 

三、对pushlet的一点看法:

    个人认为pushlet后台代码核心主要在事件源,所以我们自己创建一个事件源类,这个事件源类必须实现EventSource或者继承EventPullSource类。如果事件源类实现了EventSourc。接口,那么它需要实现把事件”推”到pushlet框架的方法;如果事件源类扩展了EventPullSource类,那么Pushlet框架会每隔一段时间自动地拉取事件源产生的事件。然后,把事件源类捆绑到TestEventPullSources类中,最后将这个事件源的类名存放到类路径下的资源文件sources.properties中。将事件源捆绑到TestEventPullSources类中,并存放于资源文件中,目的是便于对事件源的创建和管理。当Pushlet.服务器被载人时,事件源管理器就会在类路径中寻找资源文件,并将文件中的事件源载人激活。

     一个简单的做法,当我们继承了EventPullSource类(只能继承的原因是该类是个抽象类),主要实现两个方法,一个就是getSleepTime()和pullEvent(),其中getSleepTime是为了控制刷新数据的时间,而在pullEvent是写自己业务代码的地方。写完这个类后,除了要在jsp页面完成一些代码外,更主要的是要在sources.properties文件进行相关的配置。(详见第二点)显而易见,这种做法虽然理解起来简单但是不便于扩展,配置起来也有一定的麻烦。

经过研究源码,查找API,发现EventPullSource这个类除了上面提到的方法外,还有activate()、isAlive()、passivate()、run()、start()、stop()仔细研究发现其实EventPullSource是实现了EventSource及runnable这两个接口的。既然这样,可以想到自己是否也可以写一个类实现这两个接口,将自己的业务方法封装在pullEvent方法中,并在Jsp页面中的javaScript中编写一些必须的方法,发现可以达到推送的目的。然而这样不需要在sources.properties中进行相关配置,省去了很多麻烦。

说的再多不如给一段代码来的直观点:

package com.unistrong.dial.pushlet;

 

import java.util.HashMap;

import java.util.Map;

 

import com.unistrong.dial.vo.Dial;

 

import nl.justobjects.pushlet.core.Dispatcher;

import nl.justobjects.pushlet.core.Event;

import nl.justobjects.pushlet.core.EventSource;

import nl.justobjects.pushlet.util.Log;

 

public class ServerMain implements EventSource,Runnable {

    private volatile boolean alive = false;

    private volatile boolean active = false;

    private static int threadNum = 0;

    private Thread thread;

    private long sleepTime=0;

    private String eventName;

    private Map<String, String> map=new HashMap<String, String>();//同一个id的事件只能在集合中出现一次

    public ServerMain() {

    }

    public ServerMain(long sleepTime,String eventName,Map<String, String> map) {

        this.sleepTime=sleepTime;

        this.eventName=eventName;

        this.map=map;

    }

    public Event pullEvent(){

        Dial dial=new Dial();

        dial.setDid("123");

        dial.setData("10000");

        dial.setDname("dial");

        Event event = Event.createDataEvent(eventName);

        event.setField("did", dial.getDid());

        //event.setField("data", dial.getData());

        //event.setField("dname", dial.getDname());

        return event;

    }

 

    public void start() {

        thread = new Thread(this, "EventPullSource-" + (++threadNum));

        thread.setDaemon(true);

        thread.start();

    }

 

    public boolean isAlive() {

        return alive;

    }

    /**

     * Stop the event generator thread.----->停止事件构造器线程

     */

    public void stop() {

        alive = false;

 

        if (thread != null) {

            thread.interrupt();

            thread = null;

        }

 

    }

 

    /**

     * Activate the event generator thread.------->激活事件构造器线程。

     */

    synchronized public void activate() {

        if (active) {

            return;

        }

        active = true;

        if (!alive) {

            start();

            return;

        }

        Log.debug(getClass().getName() + ": notifying...");

        notifyAll();

    }

 

    /**

     * Deactivate the event generator thread.--------->停用事件构造其线程。

     */

    public void passivate() {

        if (!active) {

            return;

        }

        active = false;

    }

 

    /**

     * Main loop: sleep, generate event and publish.----->主循环:设置睡眠,事件构造器和进行发布

     */

    public void run() {

        Log.debug(getClass().getName() + ": starting...");

        alive = true;

        while (alive) {

            try {

 

                Thread.sleep(sleepTime);

 

                // Stopped during sleep: end loop.睡眠时间停止:结束循环。

                if (!alive) {

                    break;

                }

 

                // If passivated wait until we get

                // get notify()-ied. If there are no subscribers

                // it wasts CPU to remain producing events...

                /*直到我们得到通知才能不在等待,如果没有用户订阅我们要消耗cpu来维持活动*/

                synchronized (this) {

                    while (!active) {

                        Log.debug(getClass().getName() + ": waiting...");

                        wait();

                    }

                }

 

            } catch (InterruptedException e) {

                break;

            }

 

            try {

                // Derived class should produce an event.

                //实现类产生一个事件。

                Event event = pullEvent();

 

                // Let the publisher push it to subscribers.

                //将订阅信息推到用户那里

                Dispatcher.getInstance().multicast(event);

            } catch (Throwable t) {

                Log.warn("EventPullSource exception while multicasting ", t);

                t.printStackTrace();

            }

        }

        Log.debug(getClass().getName() + ": stopped");

    }

}

在这段代码中我们主要注意以下几个方面即可:

1.个人认为根据自己的业务的实际情况可以增加一些属性,并且要生成一无参和有参数的构造方法。有参数的构造方法的参数根据实际情况而定,当然上面代码中的参数我认为是几个必要的参数,其他视情况而定。

2.主要的业务代码仍然在pullEvent方法中实现。

3.除了pullEvent这个方法自己写之外,其他方法完全可以复制EventPullSource里面的代码,省力又准确。

4.写好这个类,我们就可以在struts框架的Action中,或者servlet中直接调用其中的activate()方法。当然如果再考虑严谨点我们多写一个类当做业务层来调用里面的方法,不仅可以推送数据,也可以停用单个或者所有的event.

再贴段代码看看就会明白:

package com.unistrong.dial.pushlet;

 

import java.util.HashMap;

import java.util.Map;

 

public class EventManager {

    private static EventManager em=new EventManager();

 

    public EventManager() {

    }

 

    public static EventManager getInstance(){

        return em;

    }

    //eventList集合主要用来放所有的事件,为了防止重复所以使用map集合

    private Map<String, ServerMain> eventList=new HashMap<String, ServerMain>();

    public Map<String, ServerMain> getEventList() {

        return eventList;

    }

    

    /**

     * 创建订阅

     */

    public void createEvent(long sleepTime,String eventName,Map<String, String> map){

        System.out.println("开启订阅:"+eventName);

        ServerMain sm=new ServerMain(sleepTime,eventName,map);

        eventList.put(eventName, sm);

        sm.activate();

    }

    /**

     * 停止订阅

     */

    public void removeEvent(String eventName){

        if(eventList.containsKey(eventName)){

            System.out.println("停用:"+eventName);

            ServerMain sm=new ServerMain();

            sm.stop();

            eventList.remove(eventName);

        }

    }

}

这样看起来就专业多,也严谨的多。

最后,在jsp页面的javaScript中应该注意的几个方面:

首先,不论你在后台是继承EventPullSource方法还是实现EventSource接口,都要在

Jsp页面的<head>标签里引入ajax-pushlet-client.js文件。

【关于继承EventPullSource:】

在javaScript中写入以下代码并部署工程运行即可。

    <script type="text/javascript">

        PL._init(); 

        PL.joinListen('/source/event');  //事件标识 在数据源中引用

        function onData(event) { 

            alert(event.get("msg"));

        }

    </script>

【关于实现EventSource接口:】

在javaScript中写入以下代码:

<script type="text/javascript">

        PL.setDebug(false);

        function onEvent(event){

            var did=event.get("did");

            alert(did);

            var data=event.get("data");

            var dname=event.get("dname");

            document.getElementById("chartdiv").innerHTML=did;

        }

        function listen(){

            PL.joinListen("/${id}");<!—这里监听的id是要唯一标识的,可以由后台传过来-->

        }

        function leave(){

            PL.leave();

        }

        

    </script>

查看js源码就可以看到这几个方法的作用,不过要注意的是我们要在页面的onload事件中调用其中的方法。如下:

<body onload="listen();return false;" onunload="leave();return false;">

………

</body>

如此运行就可以看到自己想要的效果。

以上是我自己在学习pushlet中的一些总结,当然里面有很大一部分是来自网络的支持,也有自己的看法,倘若有不正确之处,可以随时指正,这样我们可以共同进步。

 

pushlet笔记 3种发布方式

1.2.1. Direct Publishing (local)

1.2.1. Direct Publishing (local)

Your application may directly publish Events by using/src/nl/justobjects/pushlet/core/Dispatcher.getInstance().java . Since Dispatcher is (currently) a Singleton, sending the Event is a matter of callingDispatcher.getInstance().multicast()/unicast()/broadcast() .

The other two methods (EventSource and Pushlet protocol) will eventually callDispatcher.getInstance().multicast()/unicast()/broadcast() .

Note that in order to call Dispatcher.getInstance().multicast()/unicast()/broadcast() , your class needs to be in the same classloader as the Dispatcher.getInstance(). This may be an issue when for example your sender is in another web application. You may use the Pushlet protocol in that case or put pushlet.jar on the system CLASSPATH. In Tomcat you can also make pushlet.jar a shared library.

 

直接用Dispatcher的方法发布,可以向所有人广播,发布给订阅者,指定sessionid发布

1.2.2. Using EventSource

An EventSource is an object that is managed (activated/passivated) by the Pushlet framework. Developing your own EventSource involves creating a class that implements nl.justobjects.pushlet.core.EventSource(when your EventSource pushes Events to the framework) or that extendsnl.justobjects.pushlet.core.EventPullSource (when the framework should pull your EventSource at dedicated intervals) and adding your EventSource to a properties file aptly namedsources.properties .

See /webapps/pushlet/WEB-INF/classes/sources.properties for an example. This file specifies which EventSource objects need to be created and managed. Note: since v2.0.3 sources.properties can also be placed under WEB-INF. See /src/nl/justobjects/pushlet/core/EventSourceManager.java how this is done. See examples in /src/nl/justobjects/pushlet/test/TestEventPullSources where several example sources are bundled.

During initialization the EventSourceManager will look for the file sources.properties in the CLASSPATH. Usually this file will be put under WEB-INF/classes.

一个自己写的事件源类,需要配置在sources.properties ,要继承EventPullSource 或实现EventSource

1.2.3. Using the Pushlet protocol

The Chat and WebPresentation examples use the remote publication of events through the Pushlet (control) protocol. In a webapp the Pushlet JS API provides a p_publish() method through which your app may send events.

The /src/nl/justobjects/pushlet/test/PushletPingApplication.java provides a complete example illustrating sending and receiving Events and using/src/nl/justobjects/pushlet/client/PushletClient.java . DHTML clients may use the JavaScript pushlet library.

 

使用pushlet 协议 收发交互   ,  可以跨服务器

 

Ajax-pushlet-client是pushlet的ajax客户端方式,对该JS进行分析后发现,它是采用了面向对象的javascript技术和充分利用XMLHttpRequest对象来实现的HTTP长连接,达到了服务器“推”技术。

 

1、属性
NV_P_FORMAT: 'p_format=xml-strict',//数据格式,默认是严格严格XML

    NV_P_MODE: 'p_mode=pull', //pushlet采用的模式,默认为pull模式

    pushletURL: null,//请求URL地址

    webRoot: null,//项目根路径

    sessionId: null,//sessionId

    STATE_ERROR: -2,//一些状态常量

    STATE_ABORT: -1,

    STATE_NULL: 1,

    STATE_READY: 2,

    STATE_JOINED: 3,

    STATE_LISTENING: 3,

    state: 1,//状态

 

       statusMsg: 'null', //状态消息

    statusChanged: false,//状态是否发生了改变

    statusChar: '|',//状态分隔符

 

2、方法
       _init:获取XMLHttpRequest对象

设定pushlet的请求URL

将状态置为STATE_READY.

 

    _doRequest (anEvent, aQuery):首先判断是不是出现错误,然后再判断是否需要等待状态,若不需要等待则构建查询字符串。最后调用_getXML(url, PL._onResponse)方法向服务器发出请求,后一个参数为回调方法。

 

    _getWebRoot:获取项目根目录,可以根据实际项目修改

 

    _getXML:以get方式请求URL,用同步或者异步方式

 

_onResponse(xml):先将xml转变为pushlet事件对象(_rsp2Events),得到多个PushletEvent对象,再调用_onEvent()处理每个事件。

 

_rsp2Events(xml):取得事件标签(event),然后调用PushletEvent(),可能得到多个PushletEvent对象。

 

_onEvent(event):处理由服务器端传来的事件,首先判断事件类型,由不同的类型调用不同的处理方法:

Data:调用_doCallback(event, window.onData),方法处理。onData是自定义;

Refresh:从event中取得刷新时间,然后调用_doRequest('refresh')刷新;

Error:将state置为STATE_ERROR,获取错误原因,调用_doCallback(event, window.onError);

join-ack:将状态置为” STATE_JOINED”,取得sessionId,调用_doCallback(event, window.onJoinAck);

join-listen-ack:将state设为STATE_LISTENING,取得sessionId,调用_doCallback(event, window.onJoinListenAck);

listen-ack、hb、hb-ack、leave-ack、refresh-ack、subscribe-ack、unsubscribe-ack、abort、/nack$/等类型也类似。

 

_addEvent(elm, evType, callback, useCapture):取得elm对象,调用以下三者之一:

obj.addEventListener(evType, callback, useCapture);

obj.attachEvent('on' + evType, callback);

obj['on' + evType] = callback。

当ajax-pushlet-client.js初始化时调用了该方法:PL._addEvent(window, 'load', PL._init, false);即初始化时相当于:window.onload=PL._init();

 

_doCallback(event,cbFunction):如果指定了回调方法,则调用cbFunction(event),如果没有指定那么调用window.onEvent(event)。

 

       _getObject(obj):获取对象引用,若参数为对象直接返回该对象引用,若为字符串则按ID取得该对象。

 

 

PushletEvent(XML):与nl.justobjects.pushlet.Event相对应。其内部方法或属性如下:

Arr:数组,用于存放链值对;

getSubject():取得p_subject标签值;

getEvent():取得p_event标签值;

put(name, value);将链值对放入arr中;

get(name):按链取得值;

toString:转换为链值对字符串;

toTable:转换为表格;

 

将传入xml解析到arr中。

 

其对外公开方法:

heartbeat():实质是调用了PL._doRequest('hb'),向后台请求“hb”事件。再被封装成了:p_heartbeat()精简方式。

相似的方法还有:

Join:_doRequest('join', PL.NV_P_FORMAT + '&' + PL.NV_P_MODE);

joinListen:PL._doRequest('join-listen', query);请求加入同时监听;

leave:_doRequest('leave');

listen(aSubject):_doRequest('listen', query);按主题监听;

publish(aSubject, theQueryArgs):_doRequest('publish', query);按主题发布;

subscribe(aSubject, aLabel):_doRequest('subscribe', query);按主题订阅;

unsubscribe(aSubscriptionId):_doRequest('unsubscribe', query);按订阅ID退订;

 

 

pushlet:为一个servlet拦截订阅请求,调用EventSourceManager激活配置文件sources.properties中的所有事件源。

EventSourceManager: 事件源管理器,加载sources.properties配置文件,读取事件类加入到一个verctor中,还包含事件的activate、passivate、stop方法。

EventPullSource: 为一线程类,用于被用户继承,包含两个抽象方法getSleepTime()和pullEvent(),前者返回线程睡眠时间,后者返回事件。线程启动后循环睡眠然后针对事件发送多播。Dispatcher.getInstance().multicast(event);

command命令类: 用于封装参数session,event,request,response

constroller: doPushlish doJoin doLeave doRefresh等一些主要的操作方法。



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [pushlet 学习] 推荐:

pushlet 学习

- - 企业架构 - ITeye博客
转自: http://blog.csdn.net/houpengfei111/article/details/7498481.     pushlet是一种comet实现,在servlet机制下,数据从server端的java对象直接推送(push)到(动态)HTML页面,而无需任何java applet或者插件的帮助.

pushlet初次使用心得

- - 编程语言 - ITeye博客
最新写项目遇到一个功能,大概需求是用户a 发起任务给用户b,用户b及时提示告知. 经过寻找资料,最终考虑使用pushlet. 第一次使用先从简单学起,下面写一下初次使用心得:. pushlet百度百科解释: 是一个开源的 Comet 框架,Pushlet 使用了观察者模式:客户端发送请求,订阅感兴趣的事件;服务器端为每个客户端分配一个会话 ID 作为标记,事件源会把新产生的事件以多播的方式发送到订阅者的事件队列里.

使用Pushlet将消息从服务器端推送到客户端

- - 企业架构 - ITeye博客
使用Pushlet来实现服务器端向客户端推送信息. 1.         通过配置文件来实现定时的从服务器端向客户端推送信息. 2.         通过API主动向另外一端推送信息. 在开始测试之前,有三点非常重要,需要实现讲明,否则程序将会无法正常运行:. 2.1.     JSP页面上的设定.

iptables NAT 学习

- - BlogJava-首页技术区
为了搞清楚iptables NAT的过程,做了这个实验. 使用了1台双网卡服务器和1台单网卡服务器,2个网段. 1.       为了看到调度服务器上的数据转发过程,首先在调度服务器上分出内核的debug日志:. l 在/etc/rsyslog.conf最后增加:kern.debug /var/log/iptables.log.

Servlet Filter 学习

- - CSDN博客架构设计推荐文章
最近在研究CAS , CAS 中的Servlet Filter 不太熟悉, 所以花了点时间学下了下这部分的知识, 分成以下几部分 学习. Servlet Filter  的功能和用法. Servlet Filter 顺序的注意事项. A filter is an object that performs filtering tasks on either the request to a resource (a servlet or static content), or on the response from a resource, or both.

mongo 学习

- - CSDN博客系统运维推荐文章
mongod 启动参数详解:. master 启动: ./mongod --dbpath /data/db/master --logpath /data/db/master.log --logappend  --fork --port 2717 --master --oplogSize 64 . slave 启动: ./mongod  --dbpath /data/db/slave  --logpath  /data/db/slaver.log --logappend  --fork  -port 27018  --slave --slavedelay 5 --autoresync --source localhost:27017  .

Storm Trident 学习

- - 小火箭
Storm支持的三种语义:. 至少一次语义的Topology写法. 参考资料: Storm消息的可靠性保障 Storm提供了Acker的机制来保证数据至少被处理一次,是由编程人员决定是否使用这一特性,要使用这一特性需要:. 在Spout emit时添加一个MsgID,那么ack和fail方法将会被调用当Tuple被正确地处理了或发生了错误.

「学习笔记-Linux」学习Shell Script

- - CSDN博客系统运维推荐文章
学习Shell Script. 1 什么是Shell Scipt. 2.2 例2 按日期建立相似名字的文件. 3.2.4 整数,字符串,多重条件判断. 4 Shell Script 参数. 5.2 if else 结构. 8 shell script的追踪与Debug. 1 什么是Shell Scipt.

真正的学习

- Yuli - 左岸读书_blog
前天突然发现,身边很多人在当年读书时有神话般的表现,比方说一个哥们小学、初中与高中永远是全校第一名. 比方说,高中的同桌在一次期末考试前生病,在家休养一个月,回来时距离考试仅三四天时间,但没想到他一鸣惊人,从以前的十二三名跃居到第四名. 不过,所有这些神话,都远不如一个看起来有些愚笨的故事令我感动.

学习的艺术

- 白肥 - 左岸读书_blog
一个男嘉宾刚从后面的背景门后走出来,. 一个女嘉宾就以迅雷不及掩耳盗铃响叮当之势把灯按掉. 主持人问她:姑娘,你怎么这么快就把灯按了呀. 第三,我不喜欢个子矮还穿西装的. 我脑中马上想出一个画面:如果这位姑娘看到敬爱的小平同志的尊荣不知是何反应. 当我们初见一个人、初学一门技艺、初尝试一样新的菜式、或者初到一个陌生的地方、、、、、、.