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類的初始化方法首先會加載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方法會加載配置項,但關於如何建立核心類實例的配置項將不會再被使用。框架