使用嵌入式 Tomcat 簡化程序調試

  在開發基於 Tomcat 的 Web 程序時,一個比較頭痛的問題就是如何將以接口分離的後臺服務與前臺

頁面進行聯調。以 Tomcat 爲中心的運行環境中雖然能夠支持 IDE 直接進行跟蹤調試,但配置麻煩、速

度較慢且限制很多,總不如直接對後臺程序進行調試來的方便。
一個較好的解決方法,是利用 Tomcat 的嵌入式版本,將 Tomcat 反過來嵌入到後臺服務中,以後臺

服務爲主進行調試。這樣一來 Tomcat 從整體容器變爲後臺服務的一種,在不改變行爲的前提下,能夠自

行定製調試環境。例如筆者開發的一個後臺服務調試環境,支持基於命令行的簡便移用的調試命令,直接

對各種後臺服務進行控制,並通過前臺界面驗證結果,大大減輕了整合時的調試難度。

雖然普通配置的 Tomcat 理論上也可以直接嵌入到後臺程序,但推薦還是使用 Tomcat 定製的

Embeded 版本,這樣集成度更高且性能較好。同時因爲代碼完全相同,不會存在調試環境內外的功能上的

差異問題。

Tomcat 5.0.28 Embed tar.gz

在解壓 Tomcat Embed 版本後,將其 lib 目錄下所有 .jar 文件加入到 Java 項目的 classpath 中

,就可以着手在 Java 程序中啓動 Tomcat 了。與普通的 Tomcat 配置類似,其運行需要創建如下組織的

結構:
java代碼: 


<Server>
  <Service>
    <Connector />
    <Engine>
      <Host>
        <Context />
      </Host>
    </Engine>
  </Service>
</Server>


只不過平時是配置 web.xml,而在嵌入版本中直接以程序方式完成。

首先是建立 Tomcat 服務器,並指定其運行目錄,此目錄最好與 Tomcat Embed 版本路徑相同。
java代碼: 


  Embedded tomcat = new Embedded();

  tomcat.setCatalinaHome(path);
  tomcat.addEngine(engine);
  tomcat.setDebug(Logger.WARNING);


然後創建缺省 Engine 和 Host,並將 Host 加入到 Engine 中。這裏的名字只是起到標記作用,但

Host 的路徑最好與 tomcat 路徑保持一致。同一 Engine 實際上是可以有多個虛擬 Host 的,對大型站

點的自動測試可以將之分離進行。
java代碼: 


  Engine engine = tomcat.createEngine();
  engine.setName("EspServer");

  Host host = tomcat.createHost("localhost", tomcat.getCatalinaHome() + "/webapps");

  engine.addChild(host);
  engine.setDefaultHost(host.getName());


對 Host 的內容填充,實際上就是具體 Web 應用程序的環境的建立過程。首先應該有一個缺省的

Context,在 URL 路徑不匹配的時候會被使用。缺省 Context 的虛擬路徑可以被設置爲 "",內部實現時

自動轉換爲 "/";而其物理路徑可以直接使用 Tomcat 自帶的 /webapps/ROOT 內容,或者使用自定義內

容。
java代碼: 


  Context ctxtRoot = tomcat.createContext("", host.getAppBase() + "/ROOT");
  ctxtRoot.setPrivileged(true);

  host.addChild(ctxtRoot);


值得注意的是這裏設置 ROOT 爲特權程序,其區別主要在於 Context 容器的 ClassLoader 等。具體

細節有興趣的朋友可以參考 Tomcat 中關於 classloading 的文檔
java代碼: 


     Bootstrap
          |
       System
          |
       Common
      /      /
Catalina   Shared
             /   /
        Webapp1  Webapp2 ...


而對用戶自己的 WebApp 實際上並不限於相同目錄,完全可以任意設置,使用與創建 ROOT 程序類似

的方式即可。

最後需要創建合適的 Connector 接受 http/https 請求。推薦將 web 服務綁定在本地 loopback 地

址上,限制只能本機訪問。
java代碼: 


  try
  {
    tomcat.addConnector(tomcat.createConnector(
        InetAddress.getByName("127.0.0.1"), 8080, false));
  }
  catch (UnknownHostException e)
  {
    System.err.println("Bind tomcat server to 127.0.0.1:8080 failed.");

    e.printStackTrace();

    tomcat = null;
  }


完整的嵌入式 Tomcat 創建代碼示例如下:
java代碼: 


private Embedded createTomcat(String path)
{
  Embedded tomcat = new Embedded();

  tomcat.setCatalinaHome(path);

  Engine engine = tomcat.createEngine();
  engine.setName("EspServer");

  Host host = tomcat.createHost("localhost", tomcat.getCatalinaHome() + "/webapps");

  engine.addChild(host);
  engine.setDefaultHost(host.getName());

  Context ctxtRoot = tomcat.createContext("", host.getAppBase() + "/ROOT");
  ctxtRoot.setPrivileged(true);

  host.addChild(ctxtRoot);

  String espPath = ConfigManager.getProperty("ESP_ROOTDIR");

  if(espPath == null || !new File(espPath).exists())
  {
    espPath = host.getAppBase() + "/esp";

    if(!new File(espPath).exists())
    {
      System.err.println("You should set ESP_ROOTDIR in esp.config.");

      return null;
    }
  }

  Context ctxtEsp = tomcat.createContext("/esp", espPath);

  host.addChild(ctxtEsp);

  tomcat.addEngine(engine);
  tomcat.setDebug(Logger.WARNING);

  try
  {
    tomcat.addConnector(tomcat.createConnector(
        InetAddress.getByName("127.0.0.1"), 8080, false));
  }
  catch (UnknownHostException e)
  {
    System.err.println("Bind tomcat server to 127.0.0.1:8080 failed.");

    e.printStackTrace();

    tomcat = null;
  }

  return tomcat;
}


然後就可以在合適的時候調用其 start/stop 方法啓動或停止服務,感覺比標準配置的 tomcat 反映

迅速許多。
此外可以通過一個輔助類的方法 ServerInfo.getServerInfo() 獲取當前 Tomcat 版本信息用於顯式

狀態 

在此過程中有一些需要注意的細節問題。
1.運行此程序時需要使用 JDK 而非 JRE,因爲 Tomcat 需要動態編譯 JSP 頁面,可能還需要手工把

JDK 的 /lib/tools.jar 加入到項目 classpath 中。
2.因爲嵌入式版本 Tomcat 沒有 common/lib 目錄,如果碰到 JAXP 的 Provider 沒有找到的 bug,

可能需要直接將 xercesImpl.jar 等實現包複製到 JDK 的 /jre/lib/endorsed 目錄下。
3.注意 classpath 中不要有其他版本 tomcat 的包,否則可能會出現衝突。

如果需要進一步瞭解相關信息,可以參考 Tomcat 自帶 JavaDoc 文檔,或者 O'Reilly 的

 

http://www.onjava.com/lpt/a/1547

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章