程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 在Apache Geronimo上部署J2EE應用程序

在Apache Geronimo上部署J2EE應用程序

編輯:關於JAVA

學習如何在 Apache Geronimo 上部署 Java™ 服務器頁面(JSP)、servlet 和不 同的企業 JavaBean(EJB)。這篇文章包含 Apache Geronimo 需要的部署步驟,這與其他 Java 2 平台企業版(J2EE)容器不太一樣。

學習在運行 Apache Geronimo 的計算機 上部署簡單 J2EE 應用程序的步驟,了解這些步驟與其他符合 J2EE 規范的應用服務器的相 似之處。雖然這篇文章主要側重於如何在 Apache Geronimo 上部署 EJB,但也會學到關於 JSP 和 servlet 方面的內容。這篇文章假設讀者知道如何在其他 J2EE 容器上編寫和部署 EJB,例如 IBM WebSphere® 應用服務器、JBoss 或 WebLogic。

注: 請使用 Apache Geronimo 的當前發行版(在編寫這篇文章時,是版本 1.0 M5)處理這篇文章中的代 碼示例。

J2EE 部署過程

Apache Maven 用來構建和部署這篇文章中的全部示例代 碼。所以,有許多文件是特定於 Maven 的構建腳本。輸出結果是企業檔案(EAR)文檔。在 .ear 文件構建完成後,必須運行以下命令在 Apache Geronimo 上部署它:

$ java -jar bin/deployer.jar deploy phonebook.ear

我要感謝 Neal Sanche 允許我使用他的 文章 “利用 Geronimo 深入 EJB Web 應用程序”(developerWorks,2005 年 7 月)中的代碼作為解釋部署過程的基礎。在閱讀本文時,請下載代碼並參考它(請參閱 下載 一節)。

JSP 和 servlet

JSP 和 servlet 是驅動 J2EE 應用程序的用戶界面 (UI)層的兩種基本 J2EE 技術。JSP 主要用於表示邏輯和 HTML 代碼。Servlet 構成典型 的模型-視圖-控制器(MVC)體系結構的控制器層,並充當表示層和模型層的接口。

示例代碼中的簡單應用程序是用 Apache Struts 編寫的。代碼包含少量 Struts 動作類和兩 個 JSP 頁面。圖 1 顯示了源代碼的結構。

圖 1. 示例的源代碼結構

Struts 動作類在 Phonebook/src/java/org/acme/phonebook/struts 目錄中。JSP 頁面 在 Phonebook/src/webapp/pages 目錄中。

這個示例中唯一一個真正的 servlet 是 Struts 動作 Servlet,它控制動作類的調用。請在代碼樹中找到 servlet-mappings.xml 和 servlets.xml,並查看 Struts 動作 Servlet 的聲明方式。這些文件的代碼分別顯示在 清 單 1 和 清單 2 中。

清單 1. Servlet-mappings.xml<servlet- mapping>
  <servlet-name>action</servlet-name>
  <url-pattern>*.do</url-pattern>
</servlet-mapping>
   
<!-- Session Config -->
<session-config>
 <!-- Make sessions last two hours -->
 <session- timeout>120</session-timeout>
</session-config>
清單 2. Servlets.xml<servlet>
  <servlet-name>action</servlet -name>
  <servlet-class>
      org.apache.struts.action.ActionServlet
  </servlet-class>
  <init-param>
  <param-name>config</param-name>
   <param-value>
     /WEB-INF/conf/struts-config.xml
   </param-value>
  </init-param>
  <init-param>
  <param-name>debug</param-name>
  <param- value>0</param-value>
  </init-param>
  <init- param>
  <param-name>detail</param-name>
   <param-value>0</param-value>
  </init-param>
  <load-on-startup>2</load-on-startup>
</servlet>

對於 J2EE Web 程序員來說,這個代碼很熟悉。servlet-mappings.xml 和 servlets.xml 文 件在 Maven 構建期間被合並到 J2EE Web 檔案(WAR)文件的 web.xml 文件中。請執行構建 過程並查看生成的 web.xml 文件。

EJB

Apache Geronimo 用 OpenEJB 作為 EJB 容器系統。示例包含兩個 EJB:

容器管理的實體 EJB

無狀態會話 EJB

容器管理的實體 EJB

Neal 在他的文章中解釋說,在他的 EJB 中非常多地利用了 XDoclet。XDoclet 像 EJBGen 一樣,會替您生成大部分 EJB 代碼,包括部署描述符。 XDoclet 的概念與 Java 標 注類似。Javadoc 樣式的注釋被用作 XDoclet 編譯器的觸發器,並生成許多必要的代碼。請 浏覽 清單 3 所示的 PhoneBookEntryBean.java,查看 XDoclet 使用的標注樣式。

清單 3. PhoneBookEntryBean.javapackage org.acme.phonebook.ejb;
/**
*
* @ejb.bean
*  type="CMP"
*  cmp-version="2.x"
*  name="PhoneBookEntry"
*  local-jndi-name=
*    "org.acme.phonebook.ejb/PhoneBookEntryLocalHome"
*  view-type="local"
*  primkey-field="name"
*  
* @ejb.finder
*  signature="java.util.Collection findAll()"
*  query="SELECT OBJECT(o) from PhoneBookEntry AS o"
*
* @xx-ejb.data-object
*  container="true"
*  setdata="true"
*  generate="true"
*  
* @ejb.value-object
*
* @ejb.transaction type="Required"
* @ejb.permission unchecked="true"
* @struts.form include-all="true"
*
* @web.ejb-local-ref
*  name="ejb/PhoneBookEntryLocal"
*  type="Entity"
*  home="org.acme.phonebook.ejb.PhoneBookEntryLocalHome"
*  local="org.acme.phonebook.ejb.PhoneBookEntryLocal"
*  link="PhoneBookEntry"
*
* @ejb.persistence table-name="PhoneBookEntry"
*
*/
public abstract class PhoneBookEntryBean
    implements javax.ejb.EntityBean
{
  /**
   *
   * @ejb.pk-field
   * @ejb.persistence
   *   column-name="NAME"
   *    jdbc-type="VARCHAR"
   *    sql-type="VARCHAR(250)"
   *
   * @ejb.interface-method view-type="local"
   *
   */
  public abstract java.lang.String getName();

  /**
   * @ejb.interface-method view-type="local"
   */
  public abstract void setName(java.lang.String newValue);
  /**
   *
   * @ejb.persistence
   *   column-name="PHONE_NUMBER"
   *    jdbc-type="VARCHAR"
   *    sql-type="VARCHAR(250)"
   *
   * @ejb.interface-method view-type="local"
   *
   */
  public abstract java.lang.String getPhoneNumber();

  /**
   * @ejb.interface-method view-type="local"
   */
  public abstract void setPhoneNumber(java.lang.String newValue);

  /**
   * @ejb.interface-method
   */
  public abstract org.acme.phonebook.ejb.PhoneBookEntryValue
    getPhoneBookEntryValue();
  /**
   * @ejb.create-method
   */
  public java.lang.String ejbCreate(java.lang.String name,
    java.lang.String phoneNumber)
      throws javax.ejb.CreateException
  {
     setPhoneNumber(phoneNumber);
     setName(name);
     return null; // should not return primaryKey for CMP:
  }
  public void ejbPostCreate (java.lang.String name,
     java.lang.String phoneNumber)
      throws javax.ejb.CreateException
  {
  }
  /**
   * This is a create method which takes only the value of the
   * primary key, because this object does not have automatic
   * key generation turned on.
   *
   * @ejb.create-method
   */
  public java.lang.String ejbCreate(java.lang.String name) throws
    javax.ejb.CreateException {
    setName(name);
    return null;
  }

  public void ejbPostCreate(java.lang.String name)
    throws javax.ejb.CreateException {
  }
}

在 清單 3 所示的 PhoneBookEntryBean.java 中,進一步觀察 Javadoc 注釋。在這個代 碼中,Javadoc 注釋更有趣,包含比代碼本身更多的信息。標注定義了 EJB 的類型、容器管 理的字段、發現器查詢、數據類型和其他在 EJB 部署描述中通常會出現的信息。另外,不需 要編寫 home 接口、remote 接口和 local 接口類。Maven 和 XDoclet 在構建過程中會在後 台把這些工作全做了。

使用 OpenEJB 容器系統所需要的基本部署描述符是 openejb-jar.xml,如 清單 4 所示 。這個描述符與 PhoneBookEntryBean.java 的 Javadoc 標注組合在一起,最終構成了 ejb -jar.xml 和符合標准 J2EE 規范的 EJB 應用程序所需要的其他描述符。

清單 4. Openejb-jar.xml<?xml version="1.0"?>
  <openejb-jar
   xmlns="http://www.openejb.org/xml/ns/openejb-jar"
   configId="org/acme/PhonebookEJB"
   parentId="MysqlDatabase">
   <cmp-connection-factory>
    <resource-link>MysqlDataSource</resource-link>
   </cmp-connection-factory>
   <enterprise-beans>
    <entity>
     <ejb-name>PhoneBookEntry</ejb-name>
     <jndi-name>PhoneBookEntry</jndi-name>
     <local-jndi-name>
        java:comp/env/ejb/PhoneBookEntryLocal
     </local-jndi-name>
     <table-name>phone</table-name>
     <cmp-field-mapping>
      <cmp-field-name>name</cmp-field-name>
      <table-column>name</table-column>
     </cmp-field-mapping>
     <cmp-field-mapping>
      <cmp-field-name>phoneNumber</cmp-field-name>
      <table-column>phone</table-column>
     </cmp-field-mapping>
    </entity>
    <session>
     <ejb-name>PhoneBookSession</ejb-name>
     <jndi-name>
        org.acme.phonebook.ejb/PhoneBookSession/Home
     </jndi-name>
     <local-jndi-name>
        java:comp/env/ejb/PhoneBookSessionLocal
     </local-jndi-name>
    </session>
   </enterprise-beans>
  </openejb-jar>

注: Aaron Mulder 編寫的 Apache Geronimo Development and Deployment 一書中有這 個 XML 文件結構的精彩可視表示。(請參閱 參考資料 獲得這本書的鏈接。)

基本上,這個文件:

定義在容器中部署的實體 bean。

設置 Java 命名和目錄接口(JNDI)名稱。

聲明該 bean 是實體 EJB。

定義使用的數據源。

定義這個實體 EJB 代表的表的名稱。

用數據庫表中對應的列,對代表列的容器管理字段進行定義。

這就夠了!使用一個簡單的 Java 類和這個部署描述符,就完成了部署實體 EJB 需要做 的全部工作。在過去,編寫實體 EJB 至少需要一到兩天。現在,只要兩個小時。

無狀態會話 EJB

在部署時,會話 EJB 需要的工作實際上與實體 EJB 相同。實際上,在這裡 XDoclet 的 使用方式也與前面用於實體 EJB 的方式類似。清單 5 所示的 PhoneBookSessionBean.java 顯示了處理會話 EBJ 時需要的部署描述符的基本情況。它實際上比實體 EJB 的部署描述符 簡單得多。

清單 5. PhoneBookSessionBean.javapackage org.acme.phonebook.ejb;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* Phone book session bean. A place for all of the useful business
* methods that can be done with phone book entries.
*
* @ejb.bean name="PhoneBookSession"
*    type="Stateless"
*    local-jndi-name="java:comp/env/ejb/PhoneBookSessionLocal"
*    jndi-name="org.acme.phonebook.ejb/PhoneBookSession/Home"
*    view-type="both"
*
* @ejb.permission unchecked="true"
*
* @ejb.interface generate="local,remote"
*    remote-class="org.acme.phonebook.ejb.PhoneBookSession"
*    local-class="org.acme.phonebook.ejb.PhoneBookSessionLocal"
* @ejb.home generate="local, remote"
*    remote-class="org.acme.phonebook.ejb.PhoneBookSessionHome"
*    local-class="
*   org.acme.phonebook.ejb.PhoneBookSessionLocalHome"
* @ejb.util generate="physical"
* @ejb.ejb-ref ejb-name="PhoneBookEntry" view-type="local"
*   ref-name="ejb/PhoneBookEntryLocal"
* @web.ejb-local-ref
*  name="ejb/PhoneBookSessionLocal"
*  type="Session"
*  home="org.acme.phonebook.ejb.PhoneBookSessionLocalHome"
*  local="org.acme.phonebook.ejb.PhoneBookSessionLocal"
*  link="PhoneBookSession"
*
*/
public abstract class PhoneBookSessionBean implements
    javax.ejb.SessionBean {
  /**
   * Add a phone book entry.
   * @param name the name
   * @param number the number
   *
   * @ejb.interface-method view-type="both"
   * @ejb.transaction   type="Required"
   */
  public void addEntry(java.lang.String name,
    java.lang.String number) {
     try {
       PhoneBookEntryLocal entry =
          PhoneBookEntryUtil.getLocalHome().
            create(name,number);
     } catch(Throwable ex) {
       ex.printStackTrace();
     }
  }
  /**
   * Delete a phone book entry for the given name
   * @param name the name to remove
   * @ejb.interface-method view-type="both"
   * @ejb.transaction   type="Required"
   */
  public void deleteEntry(java.lang.String name) {
     try {
       PhoneBookEntryLocal entry =
        PhoneBookEntryUtil.getLocalHome().
             findByPrimaryKey(name);
       entry.remove();
     } catch (Throwable ex) {
       ex.printStackTrace();
     }
  }
  /**
   * Update a phone book entry for the given
   * name with the given number.
   * @param name the name
   * @param number the phone number
   * @ejb.interface-method view-type="both"
   * @ejb.transaction   type="Required"
   */
  public void updateEntry(java.lang.String name,
    java.lang.String number) {
     try {
       PhoneBookEntryLocal entry =
         PhoneBookEntryUtil.getLocalHome().
          findByPrimaryKey(name);
       entry.setPhoneNumber(number);
     } catch (Throwable ex) {
       ex.printStackTrace();
     }
  }
  /**
   * Find an entry by name.
   * @param name the name to look up.
   * @return a phone book entry value representing the entry.
   *
   * @ejb.interface-method view-type="both"
   * @ejb.transaction   type="Required"
   */
  public PhoneBookEntryValue findEntry(java.lang.String name) {
     try {
       PhoneBookEntryLocal entry =
         PhoneBookEntryUtil.getLocalHome().
           findByPrimaryKey(name);
       return entry.getPhoneBookEntryValue();
     } catch (Throwable ex) {
       ex.printStackTrace();
     }
     return null;
  }
  /**
   * List all of the phone book entries.
   * @return a collection of PhoneBookEntryValue objects.
   *
   * @ejb.interface-method view-type="both"
   * @ejb.transaction   type="Required"
   */
  public java.util.Collection listEntries() {
     ArrayList values = new ArrayList();
     try {
       Collection entries =
         PhoneBookEntryUtil.getLocalHome().findAll();
       Iterator i = entries.iterator();
       while(i.hasNext()) {
         PhoneBookEntryLocal entry =
           (PhoneBookEntryLocal)i.next();
         values.add(entry.getPhoneBookEntryValue());
       }
     } catch (Throwable ex) {
       ex.printStackTrace();
     }
     return values;
  }
  /**
   * @ejb.create-method
   * @ejb.transaction type="Required"
   */
  public void ejbCreate ()
      throws javax.ejb.CreateException
  {
  }
  public void ejbPostCreate ()
      throws javax.ejb.CreateException
  {
  }
  protected javax.ejb.SessionContext _ctx = null;
  public void setSessionContext(javax.ejb.SessionContext ctx)
  {
     _ctx = ctx;
  }
  protected javax.ejb.SessionContext getSessionContext()
  {
     return _ctx;
  }
}

請注意,PhoneBookSessionBean.java 實際上只定義了 JNDI 名稱、方法的事務屬性、 home 接口的名稱、remote 接口以及會話 EJB 的其他東西。在會話 EJB 的 J2EE 部署符中 通常會看到的其他東西,是用會話 EJB 的 Java 源代碼中定義的標注生成的。這包括每個方 法的事務類型,甚至 EJB 是無狀態 EJB 這一事實。

XDoclet 還生成一個工具類,用來查詢 home 和 remote 接口。為大量不同的應用程序編 寫 EJB 的開發人員會認識到這一點的好處。這個查詢代碼很麻煩,而且說實話,編寫起來很 煩人。開發人員總是在以前做過的工作中尋找可利用的代碼,或者搜索 Web,並把這個代碼 復制、粘貼到自己正在開發的新應用程序中。現在 XDoclet 替他們做了這件事。

最後的步驟

在編寫完 EJB 並像前面描述的那樣生成了部署描述符之後,在運行 Maven 構建代碼之前 ,還有一些事要做。清單 6 顯示的 application.xml 是 Apache Geronimo 要求的部署描述 符。這只是一個標准的 J2EE 1.4 要求。它定義了 Java 檔案(JAR)文件、WAR 文件和作為 這個 J2EE EAR 的組成部分所需要的其他外部資源。

清單 6. Application.xml<application
    xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="
      http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
    http://java.sun.com/xml/ns/j2ee/application_1_4.xsd"
    version="1.4">
   <module>
     <ejb>phonebook-ejb.jar</ejb>
   </module>
   <module>
     <web>
       <web-uri>phonebook.war</web-uri>
       <context-root>/phonebook</context-root>
     </web>
   </module>
   <module>
     <connector>
      tranql-connector-1.0-SNAPSHOT.rar</connector>
   </module>
</application>

在所有這些 XML 文件就位之後,可以運行示例提供的 Maven 構建腳本,迅速構建全部源 代碼。這個過程包括使用 XDoclet 庫生成代碼。Maven 設置起來有點兒麻煩。請按照 “Dive into EJB Web applications with Geronimo” 這篇文章中的步驟設置並運行它。

結束語

這篇文章解釋了如何生成使用 Apache Geronimo 部署 J2EE 應用程序所需要的不同的部 署描述符。通過展示在運行 Apache Geronimo 的計算機上編寫和部署 EJB 有多麼簡單,演 示了 XDoclet 和 Maven 的威力。

這篇文章有力地證明:在運行 Apache Geronimo 的計算機上部署 EJB 應用程序的步驟實 際上與其他符合 J2EE 規范的應用服務器(例如 JBoss、WebSphere 或 WebLogic)沒有什麼 不同。即使使用其他應用服務器,也可以使用 Maven 和 XDoclet。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved