Pushlets的初始化陷阱

Pushlets是在类名为Pushlet的servlet的init方法中进行初始化的。通常咱们会在web.xml配置pushlet的时候,指定其servlet在Web应用启动时就进行初始化,即使这样,仍是有可能初始化失败,致使整个Pushlets失效。Pushlet.init代码以下:web

 1 public void init()
 2     throws ServletException
 3   {
 4     try
 5     {
 6       String webInfPath = getServletContext().getRealPath("/") + "/WEB-INF";
 7       Config.load(webInfPath);
 8 
 9       Log.init();
10 
11       Log.info("init() Pushlet Webapp - version=" + Version.SOFTWARE_VERSION + " built=" + Version.BUILD_DATE);
12 
13       SessionManager.getInstance().start();
14 
15       Dispatcher.getInstance().start();
16 
17       if (Config.getBoolProperty("sources.activate"))
18         EventSourceManager.start(webInfPath);
19       else
20         Log.info("Not starting local event sources");
21     }
22     catch (Throwable t) {
23       throw new ServletException("Failed to initialize Pushlet framework " + t, t);
24     }
25   }
Pushlet.init()源代码

Pushlet类的初始化方法首先会加载Pushlets配置和日志打印组件。其首先调用的Config.load()方法会在class path及WEB-INF中查找pushlet.properties配置文件,并加载全部配置项。这一步相当重要,由于Pushlet会根据配置项决定如何实例化controller、dispatcher、session manager、session、subscriber、subscription这些核心类以及日志类,默认的配置指向这些类的默认实现。事实上,Pushlets正是经过pushlet配置文件来实现其扩展性和灵活性的。若是Pushlets的默认实现没法知足你的业务要求,你能够添加本身的实现,只要继承自Pushlet默认的实现,而后重写其中的某个方法便可。spring

 

    举个例子,若是要实现Pushlet点对点推送消息,须要知道被推送的对象,于是须要“固定”住Pushlet产生的pushlet session id(有别于web的session id,是有pushlet本身产生和维护的,默认的实现是一串随机字符串),在网上搜索的有些实现是直接修改Pushlets的源代码直接改写SessionManager的createSession来生成跟用户ID强绑定的pushlet session id,例如直接使用用户ID做为pushlet session id,实际上不须要修改Pushlets的源码这么暴力,能够继承SessionManager实现本身的MySessionManager并重写createSessionId方法来返回用户ID,而后在pushlet.properties配置文件中将sessionmanager.class=nl.justobjects.pushlet.core.SessionManager配置项改写为咱们本身的实现例如com.test.MySessionManager。这样的实现方式能够比较好的将咱们本身的业务代码与开源组件的代码分离,至少在Pushlets.jar版本升级的时候(为了使用新功能或修复bug),可以省去很多合并代码的麻烦。session

 

    回头来讲Pushlets的初始化陷阱,Dispatcher提供broadcast、multicast、unicast来进行消息的广播、多播和单播,这都会调用到SessionManager类。而Dispatcher和SessionManager都是单例实现,经过getInstance来获取自身对象的引用,它是采用一段静态代码来实现单例建立的,SessionManager的实例化代码以下app

  static
  {
    try
    {
      instance = (SessionManager)Config.getClass("sessionmanager.class", "nl.justobjects.pushlet.core.SessionManager").newInstance();
      Log.info("SessionManager created className=" + instance.getClass());
    } catch (Throwable t) {
      Log.fatal("Cannot instantiate SessionManager from config", t);
    }
  }

这段代码只会在程序第一次引用SessionManager时执行一次,若是咱们的业务代码在调用Dispatcher类的相应方法执行消息推送(无论是广播、多播仍是单播)的时候,系统还没有调用Pushlet.init方法初始化Pushlets,那么就会由于配置还没有加载而形成instance对象建立失败,其后整个Pushlets都是瘫痪的,虽然应用程序可以成功启动,但消息推送的功能就是失效的。例如,咱们采用Spring框架的时候,通常是配置org.springframework.web.context.ContextLoaderListener来装载Spring框架,这样一来Spring框架会在Pushlet servlet以前初始化完成。在这个空挡若是业务逻辑触发了Pushlets消息推送,就将致使Dispatcher、SessionManager类的静态代码块被执行。虽然随后Pushlet.init方法会加载配置项,但关于如何建立核心类实例的配置项将不会再被使用。框架

相关文章
相关标签/搜索