程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 使您的應用程序調用我的應用程序,第3部分: 資源適配器

使您的應用程序調用我的應用程序,第3部分: 資源適配器

編輯:關於JAVA

本系列教程共分三部分,在 第 1 部分 和 第 2 部分 中,您學習了如何開發消息驅動 bean(MDB)和實體 bean,並在 Apache Geronimo 中部署和測試它們;還了解了如何創建一個電子郵件應用程序,並將其部署到 Java Apache Mail Enterprise Server(Apache James)中。在系列教程的最後這一期中,您將學習與 Java 2 Platform,Enterprise Edition(J2EE)Connector Architecture(JCA) 資源適配器有關的一切內容,構建一個連接到 Apache James 服務器的示例適配器,從而將整個應用程序匯總到一起。

開始之前

本系列教程面向希望學習如何使用各種 Java EE 組件 —— 包括 MDB 和 Java 2 Platform,Enterprise Edition (J2EE)Connector Architecture(JCA)資源適配器 —— 構建集成解決方案的 Java™ Platform, Enterprise Edition(Java EE)程序員。

關於本系列

在這個共分三部分的系列教程中,您將構建一個示例應用程序,通過 這種方式了解如何將不同的 Java EE 組件集成在一起,來開發復雜的應用程序。這個示例應用程序示范了 Apache James 中電子郵件的數 據通過 JCA 資源適配器、MDB、EJB 流向 Apache Geronimo 應用服務器。

本系列的 第 1 部分 介紹了如何開發 MDB、實體 bean 和容器管理的持久性(CMP),以及如何在 Apache Geronimo 中部署和測試這些組件。

本系列的 第 2 部分 解釋如何創建電子郵 件應用程序(mailet 和 matcher)並將其在 Apache James 電子郵件服務器中進行部署。

第 3 部分將整個應用程序匯總在一起。您將學習為 Apache James 電子郵件服務器開發、部署和測試 JCA 資源適配器,它將通過 MDB 與 James 和 Geronimo 交互。

關於本教程

這個共分三部分的系列教程的最後一期將詳細說明不同 J2EE 組件(MDB 和 JCA 適配器)之間的交互。您將了解基於 JCA 的資源適配 器,並構建一個連接到 Apache James 服務器的示例適配器。

先決條件

本教程假設您熟悉基本的 Java、J2EE 和 Java EE 概念,例如 EJB、Java Message Service(JMS)、MDB 和 Unified Modeling Language(UML)圖。不要求您預先具備任何 JCA 的知識。

系統要求

為完成本教程的學習,您需要具備以下工具:

Apache Geronimo -- Apache 提供的 Java EE 應用服務器

Apache James 2.2 -- 基於 Java 的 Simple Mail Transfer Protocol(SMTP)、Post Office Protocol version 3(POP3)和 Network News Transfer Protocol(NNTP)新聞服務器

Apache Derby 數據庫 —— 開放源碼、輕量級數據庫,嵌入在 Geronimo 之中,無需獨立安裝

Sun Microsystems 提供的 Java 1.4.2

示例源文件

首先下載 part3.zip(參見 下載 部分),其中包括第 3 部分的源代碼、適配器、MDB 和 EJB 二進制文件。下面詳細列出了 part3.zip 文件的組成部分:

- deploy(po.ear,包含 mailet 和 matcher)

- lib(tester.jar、examples.jar、mail-1.3.1.jar、activation.jar)

- src(資源適配器、MDB、EJB 和測試客戶機的 Java 文件)

- runSendEmail.cmd

- runReadEmail.cmd

概述

在這一部分中,我們將探索應用程序集成的演化史,這最終導致了 JCA 的出現。

應用程序集成簡史

讓我們回溯到 2000 年之前(比如說),當時集成異構的企業和遺留系統(例如 SAP、Siebel 和 Peoplesoft)不僅僅是花費巨大,更 是一種耗時長的特殊過程。這種過程通常稱為企業應用程序集成(EAI),有許多軟件供應商將其作為專利服務提供。

到了 2000 年年底,J2EE Connector Architecture(縮寫為 JCA,不要與 Java Cryptography Architecture 混淆)第一次為 EAI 引 入了標准。JCA 及 J2EE 1.3 的出現標志著集成解決方案開發在 2001 年的巨大飛躍,也決定了專利技術在集成領域中的衰落。

定義

在研究資源適配器之前,讓我們先了解一下以下這些定義:

企業信息系統(EIS):組織中進行傳統數據處理的後台層。就本系列教程而言,Apache James 服務器是 EIS。

企業應用程序集成(EAI):鏈接一個組織內不同企業系統(EIS)的過程。

應用程序編程接口(API)契約:一組預定義的 API,用於在不同的 J2EE 組件間通信。

資源適配器歸檔(RAR):包含一個資源適配器所需的全部庫和描述符。

服務提供者接口(SPI):JCA 規范定義的契約。

通用客戶機接口(CCI):JCA 規范定義的契約。

什麼是資源適配器?

資源適配器是一個 J2EE 組件,例如 EJB、MDB 等。每個 J2EE 組件都遵循某些規范,一個資源適配器必須遵循一種定義良好的規范, 即 J2EE Connector Architecture。資源適配器在集成 J2EE 解決方案中扮演著一個重要的角色:負責與後台 EIS 的全部通信。

資源適配器起著入口的作用,使任何 J2EE 組件可訪問後台資源(通常是 EIS)。它獨自承擔著連接 EIS、調用 EIS 系統上可用服務 等責任。資源適配器之於 EIS 系統,正如實體 bean 之於數據庫。

資源適配器可在 J2EE 應用服務器內部署,這稱為托管場景(managed scenario)。非托管場景 就是資源適配器不部署在任何服務器 內,而是獨立的。本教程中使用的是托管場景,因為您要將資源適配器部署在 Apache Geronimo 中,它是一個 J2EE 應用服務器。

後文將介紹資源適配器的各種組件。首先,您需要了解規范本身。

J2EE 連接器架構規范

JCA 規范定義一組契約,管理一個資源適配器的各個方面,例如連接或事務等。這一部分解釋 JCA 1.0 和 1.5 這兩個版本中定義的契 約。

JCA 1.0

JCA 1.0 是在 2000 年 11 月發布的第一個規范。它在更廣泛的級別定義了兩種類型的契約,即系統級和應用程序級契約。

系統級契約 定義了資源適配器和 J2EE 應用服務器之間的通信和握手,而應用程序級契約 定義了客戶機應用程序和資源適配器之間的 通信。這些契約對於一個資源適配器的客戶機來說是透明的。

系統級契約(也稱為 SPI)定義了多種不同的契約,例如:

連接管理:定義一個資源適配器和一個 J2EE 連接管理器(J2EE 應用服務器內的一個組件)之間的通信,以支持連接池。資源適配器 與 EIS 的連接是在部署過程中創建的,並置於連接池,以提高可伸縮性和性能。

事務管理:支持事務管理,定義事務管理器和資源適配器之間的握手。它支持兩種類型的事務:本地和 XA。本地事務 名符其實,對於 EIS 及其資源適配器來說是本地的。XA 事務 對於 EIS 來說是外部的,由應用服務器事務管理器托管。XA 事務能夠封裝對不同資源的多 個調用,例如不同的 EIS 系統、數據庫等等。它們支持一種兩階段提交協議。

安全性管理:支持資源適配器和 J2EE 應用服務器間的不同安全性機制,例如身份驗證和授權。

應用程序級契約(也稱為 CCI)為應用程序組件定義一組客戶機 API,例如 EJB 和應用程序客戶機,以便與資源適配器交互。它們支 持資源適配器和客戶機應用程序之間由客戶機應用程序發起的同步通信。

JCA 1.5

JCA 1.5 是最新的規范,它定義了 JCA 1.0 中缺失的部分。除了上面提到的 JCA 1.0 契約之外,JCA 1.5 還定義了管理契約和入站契 約。我們來看看這些新契約。

JCA 1.5 所支持契約使用不同的分類,如下所示。

出站契約: JCA 1.0 部分 中指定的三種契約 —— 連接、事務和安全性管理。這些契約主要支持從資源適配器到 EIS 並返回的同步 出站通信。

入站契約:支持從 EIS 到資源適配器的入站連通性,這是通過提供插入消息提供者和導入事務的功能實現的。

消息流入:支持一個資源適配器和任何消息提供者之間的交互。該契約擴展了 MDB 的定義。MDB 不再局限於接收 JMS 消息。根據這一 契約,MDB 能夠偵聽來自一個資源適配器的任何類型的消息。

事務流入:支持從一個 EIS 到一個資源適配器的事務導入。導入事務之後,資源適配器應將這這些事務傳播到應用服務器,以啟用崩 潰還原、完成事務處理等。該契約提供了確保應用服務器為傳入的事務保留 ACID(原子性、一致性、獨立性和持久性)屬性的功能。

管理契約:支持適配器生命周期管理和線程管理。

生命周期管理:為一個資源適配器定義生命周期方法,類似於 servlet 或 EJB。這些方法由應用服務器在各種事件中調用,例如在適 配器部署或服務器宕機期間。

工作或線程管理:為 J2EE 應用服務器指定一種處理和管理資源適配器線程的安全的機制。它還使應用服務器能夠提供線程池或線程的 可重用性,從而提高性能,因為線程可能是資源密集的。

圖 1 展示了 JCA 契約和資源適配器與其他 J2EE 組件之間的關系。

圖 1. 資源適配器

接下來介紹資源適配器的不同類型。

資源適配器類型

資源適配器的兩種基本類型就是出站 和入站。

出站資源適配器

出站資源適配器支持資源適配器和 EIS 之間的來回同步通信。資源適配器首先向 EIS 發送一條請求,EIS 處理請求,並構建一條響應 ,然後將響應回發給資源適配器,如 圖 2 所示。

圖 2. 資源適配器和 EIS 之間的同步通信

在 圖 2 中可以看到,向 EIS 發送了一條請求後(1),資源適配器等待接收 EIS 的響應(2)。這是雙向的同步通信。

入站資源適配器

入站資源適配器通過傳入資源適配器的數據支持 EIS 和資源適配器之間的異步通信,因而得名入站。在大多數情況下,資源適配器作 為 EIS 上特定事件(例如,添加一個新客戶賬戶時)的偵聽器注冊到 EIS。當此類事件發生時,後台 EIS 向所有已注冊的偵聽器發出關 於新客戶賬戶的通知。這個過程屬於 EIS 提供偵聽器注冊機制的功能。圖 3 展示了 EIS 和資源適配器間的消息流。

圖 3. 資源適配器和 EIS 之間的異步通信

在這種情況下,資源適配器產生一個線程,在 EIS 上注冊並偵聽 EIS 的事件,這些事件應符合工作管理契約。適配器不等待接收 EIS 的響應,因而這是 EIS 和資源適配器之間的異步通信。

兩種類型的資源適配器可以在一個應用程序中並存,具體要取決於您的應用程序的需求。您可使用出站適配器、入站適配器,也可同時 使用這兩種類型的資源適配器。

應用程序設計

既然您已經熟悉了 JCA、其契約和適配器的各種類型,那麼就可以繼續開發示例應用程序中缺少的部分了,也就是一個 JCA 資源適配 器。在這一部分中,您將構建一個與 Apache James 通信、基於 JCA 1.5 的資源適配器,並在 Geronimo 中部署這個適配器。James 適配 器將在 James 中處理假想企業 Foo, Inc. 員工發送的所有經過授權的采購請求電子郵件。

回顧 第 1 部分 中的應用程序設計部分,這可以幫助您選擇正確的適配器類型,並確定資源適配器的職責。

用例和組件復習

在 第 1 部分 和 第 2 部分 中,您為示例應用程序構建了各種 J2EE 組件,以處理以下需求:

示例應用程序需要處理員工傳入的采購請求電子郵件,並將其移動到可由采購部門訪問的指定文件夾中。

應用程序隨後讀取請求,將檢查員工是否確實來自 Foo, Inc。

一旦經過授權,將創建新采購訂單,以提交給廠商。

在第 1 部分中,您構建了一個 MDB,在接收到一條 JMS 消息時,它將調用一個 實體 EJB 在數據庫中創建新采購訂單(上述第 3 條 需求)。

在第 2 部分中,您編寫了一個電子郵件應用程序(mailet 和 matcher),並將其部署到了 Apache James 中。這個電子郵件應用程序 處理所有傳入的電子郵件,進行檢查,確定電子郵件發送者確實經過授權(上述第 1 條需求)。

在這一期中,您將構建示例應用程序的組件,將第 1 部分和第 2 部分中的理念匯總在一起。您將為 Apache James 服務器(EIS)構 建一個資源適配器,它將部署到 Geronimo 服務器中。James 適配器處理所有經過電子郵件應用程序授權的采購請求電子郵件。構建資源 適配器的用例和需求如下:

用例:檢查采購請求電子郵件。

需求:應用程序將連續檢查等待處理的新采購請求電子郵件。

既然已經確定了適配器的業務流程,那麼就可以來評估哪種類型的適配器更適合您的需求了。

出站還是入站適配器?

按照應用程序的需求,您的資源適配器組件必須與一個 MDB 交互。在接收到電子郵件之後,資源適配器需要向 MDB 發送一條消息。 MDB 隨後向一個實體 EJB 發送請求,從而在 Purchase Order 數據庫中創建一個新條目。

入站適配器的消息流入契約明確地為資源適配器和 MDB 之間的這種交互作用提供了支持。確定適配器的類型之後,就可以開發入站適 配器了。

構建入站適配器

您的入站適配器在 Geronimo 內運行,應能夠向 Apache James 服務器注冊,並接收所有經過授權的電子郵件。接收電子郵件後,適配 器隨之應向 MDB 發送一條帶有創建新采購訂單請求的消息。

如前所述,只有在 EIS 提供支持的情況下,適配器才能向 EIS 注冊。在本例中,James 使用 POP3 協議,該協議不支持注冊偵聽器( 在其他一些情況下,EIS 不允許注冊偵聽器)。資源適配器必須定期輪詢 James(EIS),檢查收件箱中是否有新電子郵件消息。這就叫做 Pull/Poll 機制。

在 Geronimo 服務器中部署的過程中,資源適配器將啟動一個輪詢線程,定期檢查 James 上的電子郵件收件箱。生命周期和工作管理 契約定義了這個過程。要構建入站適配器,您必須實現工作管理、生命周期管理和與這些契約相關的消息流入契約。

讓我們來看看這些契約定義的基本接口和適配器實現。

生命周期管理契約

生命周期管理契約為一個資源適配器定義了一組生命周期方法。J2EE 應用服務器在各種事件上調用這些方法,如適配器部署、服務器 宕機等等。

ResourceAdapter 接口

資源適配器將 javax.resource.spi.ResourceAdapter 接口作為 Java bean 實現,在描述符(ra.xml)中指定實現類名。

ResourceAdapter 中定義的各生命周期方法包括:

start(BootstrapContext):在部署資源適配器或應用服務器啟動時,應用服務器調用 start 方法。資源適配器可從 BootstrapContext 獲得 WorkManager,在 start 方法中啟動工作或在激活端點時其工作。本例中的消息端點是一個使用來自資源適配器 的消息的應用服務器。

stop():stop 方法將在適配器被解除部署或應用服務器宕機期間被調用。

endpointActivation():應用服務器在消息端點被激活(例如,初始化了一個 MDB 時)時調用此方法。

endpointDeactivation():應用服務器在消息端點停用時調用此方法。

examples.po.adapter.spi.JamesResourceAdapter 實現 ResourceAdapter 接口中定義的所有生命周期方法,如 清單 1 所示。

清單 1. JamesResourceAdapter 片段

public JamesResourceAdapter() {
  super();
  }

  public void start(BootstrapContext arg0)
   throws ResourceAdapterInternalException {
  ilog("In start()");
  workMgr = arg0.getWorkManager();
  }

  public void stop() {
  ilog("In stop()");
  }

  public void endpointActivation(MessageEndpointFactory arg0,
   ActivationSpec arg1) throws ResourceException {
  ilog("In endpointActivation");

emailWork = new JamesWork(emailSpec.getEmailUser(),
  emailSpec
  .getEmailPassword(),
  emailSpec.getEmailHost(),
  arg0);

  //starts polling the email inbox
     workMgr.startWork(emailWork);
  }

  public void endpointDeactivation(MessageEndpointFactory arg0,
   ActivationSpec arg1) {

  //stops polling email inbox
emailWork.release();
  }

如 清單 1 所示,JamesResourceAdapter 在消息端點被激活時初始化並啟動對 James 服務器上電子郵件收件箱的輪詢。

要部署一個資源適配器,需要兩個描述符(類似於 EJB):ra.xml(標准 J2EE 描述符)和 geronimo-ra.xml(Apache Geronimo 應用 服務器特定的描述符)。現在來看部署描述符(geronimo-ra.xml)中配置 JamesResourceAdapter 的部分(參見 清單 2)。

清單 2. JamesResourceAdapter 的 ra.xml

<resourceadapter>
<resourceadapter-class>examples.po.adapter.spi.JamesResourceAdapter
</resourceadapter-class>

請注意 ra.xml 是如何為 ResourceAdapter 定義實現類的,參見 清單 3。

清單 3. JamesResourceAdapter 的 geronimo-ra.xml

<resourceadapter-instance>
<resourceadapter-name>James Inbound Resource
  Adapter</resourceadapter-name>
<workmanager>
<gbean-link>DefaultWorkManager</gbean-link>
</workmanager>
</resourceadapter-instance>

geronimo-ra.xml 描述符定義了一個默認的 WorkManager,在啟動時,它將隨 BootStrapContext 一起傳遞給 JamesResourceAdapter 。

ActivationSpec 接口

資源適配器應將 javax.resource.spi.ActivationSpec 接口實現為 Java bean。它包含特定於 EIS 的屬性,用以激活消息端點。這些 屬性是在適配器的部署描述符(ra.xml)中指定的。

Validate() 方法可用於驗證 activationspec 屬性。它通常由部署工具這樣的工具使用。

examples.po.adapter.spi.JamesActivationSpec 具有 清單 4 中所示屬性的一組 getter 和 setter 方法。

清單 4. JamesActivationSpec 片段

public class JamesActivationSpec implements ActivationSpec, Serializable {

  private String emailUser = null;

  private String emailPassword = null;

  private String emailHost = null;

  private ResourceAdapter rar = null;

ra.xml 定義了 ActivationSpec 初始化一個消息端點所需的全部屬性(參見 清單 5)。

清單 5. ra.xml 中顯示 ActivationSpec 屬性的片段

<inbound-resourceadapter>
   <messageadapter>
   <messagelistener>

  <messagelistener-type>examples.po.mdb.JamesMessageListener
</messagelistener-type>
    <activationspec>

  <activationspec-class>examples.po.adapter.spi.JamesActivationSpec
</activationspec-class>
    <required-config-property>
     <config-property-name>emailHost</config-property-name>
    </required-config-property>
    <required-config-property>
     <config-property-name>emailUser</config-property-name>
    </required-config-property>
    <required-config-property>
     <config-property-name>emailPassword</config-property-name>
    </required-config-property>
    </activationspec>
   </messagelistener>
   </messageadapter>
  </inbound-resourceadapter>

JamesActivationSpec 的屬性是 e-mail host、e-mail user name 和 e-mail password。這些屬性是輪詢 James 服務器上電子郵件收 件箱時必需的。

工作管理契約

現在您需要實現工作管理契約。

Work 接口

javax.resource.spi.work.Work 接口建模一個可由 WorkManager 管理的工作實體。這是一個線程,James 資源適配器使用它在 James 服務器上輪詢電子郵件。

Work 接口包含以下兩個方法:

release():該方法標記工作完成。

run():由於 Work 是一個線程,因此有一個 run() 方法,該方法具有線程的實際執行。

examples.po.adapter.spi.JamesWork 是一個線程,用於輪詢 James 服務器上的電子郵件。它具有 清單 6 中所示屬性的一組 getter 和 setter 方法。

清單 6. JamesWork 片段

public void run() {
     // periodically check for unread emails.

     try {
        while (poll) {
           ilog("Polling for new emails ... ");

           // read the messages and delete them
           inbox.open(Folder.READ_WRITE);
           int totalMessages = inbox.getMessageCount();

           if (totalMessages == 0) {
              ilog(inbox + " is empty");
           } else {
              // Get Messages
              Message[] messages = inbox.getMessages();

              // Process each message
           for (int i = 0; i < messages.length; i++) {
           MimeMessage mime = (MimeMessage) messages[i];

           processMsg(mime);
           mime.setFlag(Flags.Flag.DELETED, true);
           }
         }

         // close inbox and main store
         inbox.close(true);
         mailStore.close();

         // Wait
         ilog("Waiting for 15 sec ...............");

         Thread.sleep(15000);

       }// end while
    } catch (Exception e) {
       e.printStackTrace();
       throw new IllegalStateException("" + e);
}

}// end run

private void processMsg(MimeMessage msg) {

    ilog("In Process Message : " + msg);

    try {
       MessageEndpoint msgEndPoint = factory.createEndpoint(null);

       ilog("Message End Point is: " + msgEndPoint);

       // msgEndpoint is an app server proxy

       if (msg != null) {

         JamesMessage email = new JamesMessage();

         PurchaseOrderBean pobean = createPurchaseOrderBean(msg);

         //set PurchaseOrder bean
         email.setPoBean(pobean);

         //ilog("Before posting Message to End Point");
         ((JamesMessageListener) msgEndPoint).onMessage(email);
         ilog("Message is posted to End Point");
       }
    } catch (Exception e) {
       e.printStackTrace();
    }
}

如 清單 6 所示,run() 方法周期性地輪詢 James 服務器上的電子郵件收件箱。如果發現電子郵件,它就會調用 processMsg(),創建 一個名為 JamesMessage 的新消息。它還會解析電子郵件內容,以創建 PurchaseOrderBean、createPurchaseOrderBean()。它隨後調用端 點上的 onMessage() 消息。

WorkManager 接口

資源適配器不需要實現 javax.resource.spi.work.WorkManager 接口。就本教程的目的而言,您將使用 Geronimo 中的 DefaultWorkManager。工作管理器實現類是在部署描述符(ra.xml)中設置的。

圖 4 說明了入站資源適配器部署過程中的事件順序和數據流。

圖 4. 資源適配器部署序列圖

在適配器部署過程中,應用服務器創建 ResourceAdapter 的一個新實例,並調用 start() 方法。ResourceAdapter 可能會初始化所需 的任何資源,以便此後進行處理。在 start() 方法中,ResourceAdapter 接收 BootStrapContext 對象,它可用於檢索 WorkManager。 ResourceAdapter 可使用 WorkManager 提交工作,WorkManager 隨後又啟動工作線程。ResourceAdapter 的 stop() 方法將在應用服務器 宕機或資源適配器解除部署時調用。

消息流入契約

為實現消息流入契約,您的 James 資源適配器定義了一個名為 JamesMessageListener 的自定義 MessageListener 和一個名為 JamesMessage 的自定義消息類型。您將修改 PurchaseOrderMDB,來偵聽 James 資源適配器定義的自定義類型的消息。

消息偵聽器和消息類型

James 資源適配器定義了一個自定義的消息偵聽器名稱 —— JamesMessageListener,如 清單 7 所示。它還定義了一個名為 JamesMessage 的自定義消息類型。消息偵聽器偵聽來自 James 資源適配器(本例中是 PurchaseOrderMDB)的消息。您必須實現自定義消 息偵聽器,這樣才能接收來自資源適配器的消息。

清單 7. JamesListener

public interface JamesMessageListener {

  public void onMessage(JamesMessage email);

}

JamesMessageListener 接口僅定義了一個 onMessage() 方法。此方法將被 James 資源適配器調用(參見 清單 8)。

清單 8. JamesMessage

public class JamesMessage {

  private PurchaseOrderBean poBean = null;

  public JamesMessage() {
  super();
  }

  public PurchaseOrderBean getPoBean() {
  return poBean;
  }

  public void setPoBean(PurchaseOrderBean poBean) {
  this.poBean = poBean;
  }

JamesMessage 具有 PurchaseOrderBean,可將其傳遞給實體 bean 來在數據庫中創建一個新采購訂單條目。

消息驅動 bean

MDB 就是一個將被應用服務器(Geronimo)調用的消息端點。回顧您在本系列第 1 部分中構建的 PurchaseOrderMDB。您實現了 PurchaseOrderMDB 來偵聽 JMS 消息。為實現消息流入,您將修改 PurchaseOrderMDB,以便偵聽自定義偵聽器類型的消息,假設是 JamesMessageListener。清單 9 顯示了應為 PurchaseOrderMDB 實現的更改。

清單 9. 修訂後的 PurchaseOrderMDB

Part 1:
  public class PurchaseOrderMDB
  implements MessageDrivenBean, MessageListener {

Revised:
  public class PurchaseOrderMDB
  implements MessageDrivenBean, MessageListener, JamesMessageListener {


Part 1:
  There was no method for JamesMessgeListener
Revised:

  public void onMessage(JamesMessage email) {
  Logger log = Logger.getLogger("PurchaseOrderMDB");
  log.info("Received James Message : "+ email);
  PurchaseOrderBean poBean = null;

  try {
   poBean = email.getPoBean();
   log.info("A new Purchase Order will be added to POSystem.");

   addPurchaseOrder(poBean);

  } catch (Exception e) {
   e.printStackTrace();
   log.severe(""+e);
  }
  }//end onMessage(JamesMessage)

在 清單 9 中,請注意本系列 第 1 部分 中的內容與更改後的內容之間的差別:PurchaseOrderMDB 實現 JamesMessageListener,從 而實現了 onMessage(JamesMessage)。在 下載 中獲得的 $part3.zip/src/examples/po/mdb 中可找到修改後的 PurchaseOrderMDB 的源 文件。

由於修改了 PurchaseOrderMDB,所以您必須對部署描述符作出相應的修改。共有兩個部署描述符,分別是 ejb-jar.xml 和 openejb- jar.xml,修改如 清單 10 所示。

清單 10. MDB 部署描述符(openejb-jar.xml)的修改

Part 1:

<message-driven>
<ejb-name>PurchaseOrderMDB</ejb-name>
<resource-adapter>
  <target-name>geronimo.server:J2EEApplication=null,J2EEServer=geronimo,
JCAResource=geronimo/activemq/1.0/car,j2eeType=JCAResourceAdapter,name=
ActiveMQ RA</target-name>
</resource-adapter>
<activation-config>
<activation-config-property>
<activation-config-property-name>destination</activation-config-
property-name>
<activation-config-property-value>POTopic</activation-config-
property-value>
</activation-config-property>
<activation-config-property>
<activation-config-property-name>destinationType</activation-
config-property-name>
<activation-config-property-value>javax.jms.Topic</activation-
config-property-value>
</activation-config-property>
</activation-config>

<!--This is a reference to PurchaseOrderEJB CMP deployed in Geronimo Server. -->
     <ejb-ref>
       <ref-name>ejb/PurchaseOrderEJB</ref-name>
       <ejb-link>PurchaseOrderEJB</ejb-link>
     </ejb-ref>

  </message-driven>

Revised Part 3:
<message-driven>
  <ejb-name>PurchaseOrderMDB</ejb-name>
<resource-adapter>
<resource-link>James Inbound Resource Adapter</resource-link>
</resource-adapter>
<activation-config>
<activation-config-property>
<activation-config-property-name>emailHost</activation-config-
property-name>
<activation-config-property-value>localhost</activation-config-
property-value>
</activation-config-property>

<activation-config-property>
<activation-config-property-name>emailUser</activation-config-
property-name>
<activation-config-property-value>authorized-orders</activation-
config-property-value>
</activation-config-property>

<activation-config-property>
<activation-config-property-name>emailPassword</activation-
config-property-name>
<activation-config-property-value>password</activation-
config-property-value>
</activation-config-property>
</activation-config>

在 清單 10 中,您可看到第 1 部分中的 PurchaseOrderMDB 是如何偵聽 POTopic 的 JMS 消息的,但在這裡發生了變化,MDB 現在偵 聽的是來自 James 資源適配器的消息。

讓我們看一下 ejb-jar.xml 描述符(參見 清單 11)。

清單 11. ejb-jar.xml

Part 1:
    <message-driven>
      <ejb-name>PurchaseOrderMDB</ejb-name>
      <ejb-class>examples.po.mdb.PurchaseOrderMDB</ejb-class>
      <transaction-type>Container</transaction-type>
  <message-destination-type>javax.jms.Topic</message-destinati 
on-type>
    </message-driven>

Part 3:

<message-driven>
<ejb-name>PurchaseOrderMDB</ejb-name>
<ejb-class>examples.po.mdb.PurchaseOrderMDB</ejb-class>
  <messaging-type>examples.po.mdb.JamesMessageListener</messaging-
type>
<transaction-type>Container</transaction-type>

<activation-config>

<activation-config-property>
<activation-config-property-name>emailHost</activation-config-
property-name>
<activation-config-property-value>localhost</activation-config-
property-value>
</activation-config-property>

<activation-config-property>
<activation-config-property-name>emailUser</activation-config-
property-name>
<activation-config-property-value>authorized-orders</activation-
config-property-value>
</activation-config-property>

<activation-config-property>

  <activation-config-property-name>emailPassword</activation-
config-property-name>
<activation-config-property-value>password</activation-
config-property-value>
</activation-config-property>

</activation-config>

</message-driven>

第 1 部分中的 ejb-jar.xml 描述符(如 清單 11 所示)描述,PurchaseOrderMDB 偵聽來自一個 JMS 資源的 JMS 消息 —— 也就是 Topic。在本教程中,您修改了描述符,將 JamesMessageListener 作為 MDB 的偵聽器類型包含進來。新的描述符還包含了 JamesActivationSpec 屬性。

圖 5 展示了在一個消息端點(本例中是一個 MDB)部署過程中發生的活動的順序。

圖 5. 部署一個消息端點

如 圖 5 所示,在 MDB 部署過程中,J2EE 應用服務器使用部署描述符中的屬性實例化並配置 ActivationSpec。隨後它實例化 MessageEndpointFactory,並調用 ResourceAdapter 的 endpointActivation() 方法。該方法主要負責向 Message 使用者交付消息。

在示例應用程序中,您在 JamesResourceAdapter 的 endpointActivation() 方法中啟動了 JamesWork 線程,它又啟動了對 James 服 務器上電子郵件消息的輪詢。如果在 James 服務器上找到了任何電子郵件,JamesWork 就會處理電子郵件,如 清單 12 所示。

清單 12. processMsg 方法的 JamesWork 線程片段

private void processMsg(MimeMessage msg) {

  MessageEndpoint msgEndPoint = factory.createEndpoint(null);

  PurchaseOrderBean pobean = createPurchaseOrderBean(msg);

JamesMessage email = new JamesMessage();

  //set PurchaseOrder bean
  email.setPoBean(pobean);

  ((JamesMessageListener) msgEndPoint).onMessage(email);

JamesWork 使用 MessageEndointFactory 來創建端點(在本例中,端點是一個 MDB),然後通過電子郵件消息創建一個新 PurchaseOrderBean,在新的 JamesMessage 中設置 PurchaseOrderBean,最後通過調用 MDB 上的 onMessage() 方法將消息傳遞到端點。

本教程的 下載 文件 part3.zip 中包含 James 資源適配器、PurchaseOrderMDB 以及 ra.xml 和 geronimo-ra.xml 描述符的所有源文 件(.java)。

除了描述符(ra.xml 和 geronimo-ra.xml)之外,無需對本教程中的資源適配器進行其他任何配置。但要使整個應用程序運作起來, 請查看本系列 第 1 部分 和 第 2 部分 中配置部分的內容,確保 Apache James 和 Geronimo 已配置。

部署

現在您已經完成了全部配置,是時候部署您的示例應用程序了。看看示例應用程序的打包。

部署示例應用程序

您將以 .ear 文件的形式部署示例應用程序,.ear 表示企業歸檔文件。.ear 文件需要兩個描述符:application.xml 和特定於應用服 務器的 .xml 文件(在本例中是 geronimo-application.xml),如 清單 13 所示。

清單 13. application.xml 描述符

<application>
  <display-name>ExampleApp</display-name>
  <description />
  <module> <ejb>po-ejb.jar</ejb> </module>
<module><connector>james.rar</connector></module>
</application>

如 清單 13 所示,.ear 文件包含兩個模塊:EJB 和資源適配器。資源適配器打包為 .rar 文件,其中 RAR 表示資源適配器歸檔。

因而,po-ejb.jar 包含 PurchaseOrderEJB 和 PurchaseOrderMDB,而 james.rar 是 James 資源適配器(參見 清單 14)。

清單 14. geronimo-application.xml

<module>
    <ejb>po-ejb.jar</ejb>
    <alt-dd>dds/openejb-jar.xml</alt-dd>
  </module>
  <module>
   <connector>james.rar</connector>
  <alt-dd>dds/geronimo-ra.xml</alt-dd>
  </module>

geronimo-application.xml 描述符具有對 EJB 和連接器的引用。它還指定了特定於 Apache Geronimo 的描述符所在的位置,位於 .ear 文件的 names dds 目錄中。

讓我們開始部署過程。

第 1 步:解除部署第 1 部分的 EJB JAR

如果您未按照第 1 部分中的介紹部署 po-ejb.jar,則可跳過這一步。

將第 1 部分中部署的 po-ejb.jar 解除部署,如 圖 6 所示。

圖 6. 解除部署 PurchaseOrderEJB

通過 $GERONIMO_HOME\bin\ startup.bat 啟動 Geronimo。

在 http://localhost.com:8080/console 訪問 Geronimo 控制台(默認用戶 ID 是 system,password 是 manager)。

單擊左側的 EJB JARS,您將看到 PurchaseOrderEJB。

單擊 Uninstall 來解除部署 PurchaseOrderEJB 和 PurchaseOrderMDB。您會將這些 J2EE 組件作為 EAR 文件的一部分部署。

第 2 步:檢查 POMailet 和 POMatcher。

確保在 James 中部署了第 2 部分介紹的電子郵件應用程序,確保 James 正在運行。

第 3 步:部署 .ear 文件

確認了 Apache James 和 Geronimo 服務器都在運行之後,您就可以部署 po.ear 文件了。這個文件位於 $part3.zip/deploy 目錄下 。

在 http://localhost:8080/console 訪問 Geronimo 服務器控制台(默認用戶 ID 是 system,password 是 manager)。

在左側單擊 Deploy New,您將看到類似於 圖 7 的屏幕。

圖 7. Apache Geronimo 的部署控制台

單擊 Archive 旁的 Browse 按鈕,在 $part3.zip/deploy 目錄中選擇 po.ear 文件,如 圖 8 所示。

圖 8. Apache Geronimo 的 部署控制台

選中 po.ear 文件之後,單擊 Install。Apache Geronimo 現在將部署 po.ear 文件內的描述符中類出的所有模塊,若部署成功,您將 看到一條消息,如 圖 9 所示。

圖 9. EAR 在 Geronimo 中部署成功

成功部署了 po.ear 文件之後,您將在 Geronimo 控制台中看到一些日志記錄,如 圖 10 所示。

圖 10. 部署後的 Geronimo 控制台

如您所見,適配器及其端點(PurchaseOrderMDB)部署好之後,JamesResourceAdapter endpointActivated() 方法將被 Geronimo 調 用,您的適配器就會開始周期性地(每 15 秒)為用戶 authorized-orders 輪詢 James 服務器中的電子郵件消息。但如 圖 10 所示, INBOX is empty 消息表示名為 authorized-orders 的用戶的收件箱中沒有任何電子郵件。

您已成功地部署了示例應用程序,其中包括 MDB、實體 EJB 和一個用於 Apache James 的資源適配器。現在您可以創建一個客戶機或 測試程序來測試您的應用程序了。

測試應用程序

您不必編寫一個全新的測試客戶機。只要將為 第 2 部分 編寫的 EmailClient.java 客戶機梢加修改即可。EmailClient 能夠發送電 子郵件以及讀取用戶收件箱中的電子郵件。

您將修改這個 EmailClient,以發送將采購請求數據作為電子郵件主體的電子郵件消息。

電子郵件消息格式

首先,您應決定一種標准格式,以便 James 資源適配器輕松解析電子郵件消息,並構建 PurchaseOrderBean 來將其發送到 PurchaseOrderMDB。電子郵件消息的格式如 清單 15 所示。

清單 15. 電子郵件消息格式

Item=Pens;
Description=Low on stock for Marketing Department;
Price=10;
Quantity=250;

客戶機

客戶機程序 EmailClient.java(參見 清單 16)提供發送電子郵件的方法。

可在 $part3.home/src/examples/po/test 目錄下查看整個客戶機程序。

清單 16. 電子郵件客戶機的 sendEmail 方法

/**
  * Sends an email message
  *
  * @param from Sender's email address
  * @param to Recipient's email address
  * @param subject Email Subject
  * @param content Email Message Content
  * @throws MessagingException
  */
public void sendEmail(String from,
   String to,
   String subject,
   String content)
  throws MessagingException {

  Session session = createSession(host, null, null);

  MimeMessage msg = new MimeMessage(session);
  msg.setFrom(new InternetAddress(from));
  msg.addRecipients(Message.RecipientType.TO, to);
  msg.setSubject(subject);
  msg.setText(content);
  Transport.send(msg);

  System.out.println("Email message is successfully sent to " + to);
}

在 sendEmail 方法中,您創建了一個新電子郵件消息(MimeMessage),帶有指定的發件人電子郵件地址(from)、收件人電子郵件地 址(to)、主題(Subject)和消息主體(content)。調用 Transport.send() 將消息實際傳遞到收件人的收件箱。您將使用此方法來發 送采購請求電子郵件。

現在來看一下建立電子郵件消息的 main() 方法,如 清單 17 所示。

清單 17. 電子郵件客戶機的 main 方法

public static void main(String a[]) throws Exception {

  //Purchase Order Properties
  String ITEM = "Pens";
  String DESCRIPTION = "Low on stock for Marketing Department";
  String UNITPRICE = "2";
  String QUANTITY="250";

  //check if from and to have been provided
  String mode = System.getProperty("mode");
  String host = null;
  String user = null;
  String password = null;
  String from = null;
  String to = null;
  String subject = "Purchase Request";
  Calendar cal = Calendar.getInstance();

     String content = "Item="+ITEM+";"+
     "Description="+DESCRIPTION+";"+
     "Price="+UNITPRICE+";"+
     "Quantity="+QUANTITY;
.
.
.
.

EmailClient client = new EmailClient(host);

  if(isSend)
   client.sendEmail(from, to, subject, content);
  else
   client.readInbox(user, password);

  }//end main

main() 方法接受幾個用於發送和讀取電子郵件的命令行參數。如 清單 17 所示,sendEmail() 方法需要電子郵件主機、from 和 to 值,readInbox() 需要電子郵件主機以及一個電子郵件賬戶的用戶名和密碼。

main() 方法負責構建電子郵件消息,采用 清單 15 中指定的格式,隨後將電子郵件發送到 [email protected]。 POMailet/POMatcher 將獲取此電子郵件,若發件人經過授權,電子郵件將轉移到另外一個名為 authorized-orders 的文件夾中。

現在 James 資源適配器輪詢此文件夾,一旦檢測到電子郵件,即獲取郵件、解析電子郵件消息、創建 PurchaseOrderBean,並將 JamesMessage 發送給 PurchaseOrderMDB。這個 MDB 隨後調用 PurchaseOrderEJB,這是一個實體 bean,將在 Geronimo Derby 數據庫表 中創建一個新采購訂單條目。

運行測試

您將使用 part3.zip 文件 中提供的 runSendEmail.cmd 腳本來測試整個應用程序流。如果您按照本教程中的說明配置了應用程序,那 麼只需要修改 JAVA_HOME 來運行這些測試即可。

現在,若 Apache James 和 Apache Geronimo 服務器尚未運行,啟動它們。

運行測試之前,先看一下 PurchaseOrder 表(參見 圖 11)。

圖 11. 測試之前的 PurchaseOrder 表內容

可以看到,PurchaseOrder 表中沒有任何條目。

現在您就可以運行 runSendEmail.cmd 腳本,從電子郵件地址 [email protected](記住,[email protected] 是經過授權的發件 人)向 [email protected](這個地址可在 runSendEmail.cmd 中修改,方法是修改 EMAIL_TO 參數)發送采購訂單電子郵件。

在運行 runSendEmail.cmd 之後,您將看到一些日志記錄,如 圖 12 所示。

圖 12. 通過運行 runSendEmail.cmd 測試應用程序

如果您看到如 圖 12 所示的消息,就表示電子郵件已成功提交。這個傳入 James 服務器的電子郵件將由您的 POMatcher-POMailet 處 理。

James 服務器首先調用 POMatcher 的 match() 方法。由於發件人 [email protected] 是一名經過授權的發件人,因此 POMailet 的 service() 方法將被調用,電子郵件應已轉發到 [email protected]。您一定感到疑惑,為什麼再次調用了 POMatcher match()?(參見 圖 13)。

圖 13. James 服務器的控制台日志

POMatcher match() 被再次調用的原因是您的 POMailet 的 service() 方法向 [email protected] 發送了一封電子郵 件。這封電子郵件在 James 服務器內的處理方式與其他任何傳入的電子郵件都相同,因此服務器調用 POMatcher match() 方法。

提示:這就是在將電子郵件轉發到 [email protected] 之前,將 from 更改為 [email protected] 或其他未經授權 的發件人的原因,目的在於避免出現無窮遞歸循環。這是一個小技巧,因為 POP3 協議不支持電子郵件在文件夾之前的移動(IMAP 支持此 功能)。

現在,由於發件人的電子郵件地址是 [email protected],不是一個經過授權的發件人,因此 POMailet 不會處理這封轉發過來的電 子郵件。

檢查一下,POMailet 是否確實將電子郵件傳遞到了 [email protected]

運行 runReadEmail.cmd,您將看到如 圖 14 所示的結果。

圖 14. 顯示電子郵件

顯示了 authorized-orders 收件箱中的電子郵件。

如果您查看 Geronimo 控制台,您將看到來自 JamesWork 的日志消息,如 圖 15 所示。

圖 15. 發送一封電子郵件後的 Geronimo 控制台

運行在 Geronimo 中的 JamesWork 線程接收到了電子郵件消息,它將此消息解析為 Item、Description、Unit Price 和 Quantity, 從而創建一個 PurchaseOrderBean。這個 bean 隨後將打包到 JamesMessage 中,並發送給 PurchaseOrderMDB,PurchaseOrderMDB 調用 PurchaseOrderEJB 在數據庫中創建一個采購訂單。

查看結果

現在您可以看看 PurchaseOrderEJB 是否在 Derby 數據庫中輸入了一個新的采購訂單。如果您轉到 Geronimo Web 控制台並訪問左側 導航窗格中顯示的 DBManager,可看到 PurchaseOrder 表的內容,如 圖 16 所示。

圖 16. Geronimo Web 控制台 DBManager

瞧!在表中有一個采購訂單的條目。

結束語

祝賀您!示例應用程序至此已經完成了。在這個系列教程中,您學習了使用 MDB、實體 bean、Mailet、Matcher 和資源適配器構建集 成解決方案。您還了解了 Apache Geronimo 中的各種配置和部署以及 James 服務器。現在您算得上是 Java 專業人士了!

本文配套源碼

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