首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

讓您的應用程式呼叫我的應用程式,第 3 部分:資源適配器

     編輯:Java 相關知識

本系列教學共分三部分。在第 1 部分與第 2 部分中,您已學會如何開發訊息驅動 Bean (MDB) 與實體 Bean,並將它們部署到 Apache Geronimo 中測試;也了解如何建立電子郵件應用程式 (Mailet 和 Matcher) 並部署到 Apache James 伺服器。在本系列的最後一期,我們將深入探討 Java 2 Platform, Enterprise Edition (J2EE) Connector Architecture (JCA) 資源適配器的相關知識,並建立一個連接到 Apache James 伺服器的範例適配器,將整個應用程式串聯起來。

開始之前

本系列教學面向想要學習如何使用各種 Java EE 元件——包含 MDB 與 JCA 資源適配器——來建構整合解決方案的 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 的 SMTP、POP3 和 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 應用程式伺服器內,稱為受管理案例;不部署在任何伺服器內而獨立運作,則稱為非受管理案例。本教學使用受管理案例,因為您將把資源適配器部署在 Apache Geronimo (一個 J2EE 應用程式伺服器) 中。

J2EE 連接器架構規格

JCA 規格定義一組契約,管理資源適配器的各個層面,例如連接或交易等。本節說明 JCA 1.0 和 1.5 兩個版本中定義的契約。

JCA 1.0

JCA 1.0 於 2000 年 11 月發布,是第一個規格。它在較廣泛的層級定義了兩類契約:系統層級契約與應用程式層級契約。

  • 系統層級契約 (又稱 SPI) 定義資源適配器與 J2EE 應用程式伺服器之間的通訊與握手。
  • 應用程式層級契約 (又稱 CCI) 定義客戶端應用程式與資源適配器之間的通訊,對資源適配器的客戶端是透明的。

系統層級契約包含:

  • 連接管理:定義資源適配器與 J2EE 連接管理器 (J2EE 應用程式伺服器內的元件) 之間的通訊,支援連接池。資源適配器與 EIS 的連接在部署過程中建立,並置於連接池以提高擴展性與效能。
  • 交易管理:支援交易管理,定義交易管理器與資源適配器之間的握手。支援兩種交易:本地交易 (對 EIS 及其資源適配器為本地) 和 XA 交易 (對 EIS 為外部,由應用程式伺服器交易管理器管理)。XA 交易能封裝對不同資源的多重呼叫,支援兩階段提交協定。
  • 安全性管理:支援資源適配器與 J2EE 應用程式伺服器間的不同安全機制,例如身分驗證與授權。

應用程式層級契約 (CCI) 為應用程式元件 (如 EJB 和應用程式客戶端) 定義一組客戶端 API,以便與資源適配器互動。它們支援由客戶端應用程式發起的同步通訊。

JCA 1.0 契約關係

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 訊息,能夠偵聽來自資源適配器的任何類型訊息。
  • 交易流入:支援從 EIS 到資源適配器的交易導入。導入交易後,資源適配器應將這些交易傳播到應用程式伺服器,以啟用崩潰恢復、完成交易處理等。此契約確保應用程式伺服器為傳入的交易保留 ACID (原子性、一致性、隔離性、持久性) 屬性。
  • 管理契約:支援適配器生命週期管理和執行緒管理。
    • 生命週期管理:為資源適配器定義生命週期方法,類似 Servlet 或 EJB。這些方法由應用程式伺服器在各種事件中呼叫,例如適配器部署或伺服器關機期間。
    • 工作或執行緒管理:為 J2EE 應用程式伺服器指定處理和管理資源適配器執行緒的安全機制。也讓應用程式伺服器能提供執行緒池或執行緒的可重用性,提高效能。

JCA 1.5 契約與資源適配器關係

資源適配器類型

資源適配器有兩種基本類型:出站和入站。

出站資源適配器

出站資源適配器支援資源適配器與 EIS 之間的來回同步通訊。資源適配器先向 EIS 發送請求,EIS 處理後構建回應,再將回應送回資源適配器 (如圖 2)。

出站資源適配器同步通訊

入站資源適配器

入站資源適配器透過傳入資源適配器的資料,支援 EIS 與資源適配器之間的非同步通訊。在大多數情況下,資源適配器作為 EIS 上特定事件 (例如新增客戶帳戶) 的偵聽器註冊到 EIS。當此類事件發生時,後端 EIS 向所有已註冊的偵聽器發出通知。此過程屬於 EIS 提供偵聽器註冊機制的功能。圖 3 展示了 EIS 和資源適配器間的訊息流。

入站資源適配器非同步通訊

此時資源適配器產生一個執行緒,在 EIS 上註冊並偵聽 EIS 的事件,這些事件應符合工作管理契約。適配器不等待接收 EIS 的回應,因此是 EIS 和資源適配器之間的非同步通訊。

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

應用程式設計

既然您已熟悉 JCA、其契約和適配器的各種類型,便可以繼續開發範例應用程式中缺少的部分——一個 JCA 資源適配器。本節將建構一個與 Apache James 通訊、基於 JCA 1.5 的資源適配器,並部署到 Geronimo 中。James 適配器將處理虛構企業 Foo, Inc. 員工發送的所有經授權採購請求電子郵件。

回顧第 1 部分中的應用程式設計一節,有助於您選擇正確的適配器類型,並釐清資源適配器的職責。

用例與元件回顧

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

  1. 範例應用程式需處理員工傳入的採購請求電子郵件,並將其移動到採購部門可存取的指定資料夾中。
  2. 應用程式隨後讀取請求,檢查員工是否確實來自 Foo, Inc.。
  3. 一旦經授權,將建立新採購訂單,以提交給廠商。

在第 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():適配器被解除部署或應用程式伺服器關機期間呼叫。
  • endpointActivation():消息端點被啟用時 (例如初始化一個 MDB 時) 呼叫。
  • endpointDeactivation():消息端點停用時呼叫。

examples.po.adapter.spi.JamesResourceAdapter 實作 ResourceAdapter 介面中定義的所有生命週期方法,如程式碼片段 1 所示。

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 應用程式伺服器特定的描述符)。

程式碼片段 2 顯示 ra.xml 中配置 JamesResourceAdapter 的部分:

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

程式碼片段 3 顯示 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 方法。

public class JamesActivationSpec implements ActivationSpec, Serializable {
 private String emailUser = null;
 private String emailPassword = null;
 private String emailHost = null;
 private ResourceAdapter rar = null;
 // ... getter/setter 方法
}

ra.xml 定義了 ActivationSpec 初始化一個消息端點所需的全部屬性 (程式碼片段 5)。

<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 的屬性是電子郵件主機、使用者名稱和密碼。這些屬性是輪詢 James 伺服器上電子郵件收件箱時必需的。

工作管理契約

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

Work 介面

javax.resource.spi.work.Work 介面建模一個可由 WorkManager 管理的工作實體。這是一個執行緒,James 資源適配器使用它在 James 伺服器上輪詢電子郵件。

Work 介面包含以下兩個方法:

  • release():標記工作完成。
  • run():由於 Work 是一個執行緒,因此有一個 run() 方法,包含執行緒的實際執行內容。

examples.po.adapter.spi.JamesWork 是一個執行緒,用於輪詢 James 伺服器上的電子郵件。它擁有程式碼片段 6 所示屬性的一組 getter 和 setter 方法。

public void run() {
 // periodically check for unread emails.
 try {
 while (poll) {
 ilog("Polling for new emails ... ");
 inbox.open(Folder.READ_WRITE);
 int totalMessages = inbox.getMessageCount();
 if (totalMessages == 0) {
 ilog(inbox + " is empty");
 } else {
 Message[] messages = inbox.getMessages();
 for (int i = 0; i < messages.length; i++) {
 MimeMessage mime = (MimeMessage) messages[i];
 processMsg(mime);
 mime.setFlag(Flags.Flag.DELETED, true);
 }
 }
 inbox.close(true);
 mailStore.close();
 ilog("Waiting for 15 sec ...............");
 Thread.sleep(15000);
 }
 } catch (Exception e) {
 e.printStackTrace();
 throw new IllegalStateException("" + e);
 }
}

private void processMsg(MimeMessage msg) {
 ilog("In Process Message : " + msg);
 try {
 MessageEndpoint msgEndPoint = factory.createEndpoint(null);
 ilog("Message End Point is: " + msgEndPoint);
 if (msg != null) {
 JamesMessage email = new JamesMessage();
 PurchaseOrderBean pobean = createPurchaseOrderBean(msg);
 email.setPoBean(pobean);
 ((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 說明了入站資源適配器部署過程中的事件順序和資料流。

資源適配器部署序列圖

訊息流入契約

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

訊息偵聽器與訊息類型

James 資源適配器定義了一個自訂的訊息偵聽器介面——JamesMessageListener (程式碼片段 7)。它還定義了一個名為 JamesMessage 的自訂訊息類型。訊息偵聽器偵聽來自 James 資源適配器的訊息 (本例中是 PurchaseOrderMDB)。您必須實作自訂訊息偵聽器,才能接收來自資源適配器的訊息。

public interface JamesMessageListener {
 public void onMessage(JamesMessage email);
}

JamesMessageListener 介面僅定義一個 onMessage() 方法。此方法將被 James 資源適配器呼叫 (程式碼片段 8)。

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 實作的變更。

// 第 1 部分原始碼:
public class PurchaseOrderMDB implements MessageDrivenBean, MessageListener {

// 修改後:
public class PurchaseOrderMDB implements MessageDrivenBean, MessageListener, JamesMessageListener {

// 新增 onMessage 方法:
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);
 }
}

在程式碼片段 9 中,請注意本系列第 1 部分中的內容與更改後的內容之間的差異:PurchaseOrderMDB 實作 JamesMessageListener,從而實作了 onMessage(JamesMessage)。修改後的 PurchaseOrderMDB 原始檔可在下載的 $part3.zip/src/examples/po/mdb 中找到。

由於修改了 PurchaseOrderMDB,您必須對部署描述符作出相應修改。共有兩個部署描述符:ejb-jar.xml 和 openejb-jar.xml,修改如程式碼片段 10 所示。

// openejb-jar.xml 修改後片段:
<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>
</message-driven>

在程式碼片段 10 中,您可看到第 1 部分中的 PurchaseOrderMDB 是如何偵聽 POTopic 的 JMS 訊息,但現在 MDB 偵聽的是來自 James 資源適配器的訊息。

讓我們看一下 ejb-jar.xml 描述符 (程式碼片段 11)。

// ejb-jar.xml 修改後片段:
<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 描述符描述 PurchaseOrderMDB 偵聽來自 JMS 資源的 JMS 訊息——即 Topic。在本教學中,您修改了描述符,將 JamesMessageListener 作為 MDB 的偵聽器類型包含進來。新的描述符還包含了 JamesActivationSpec 屬性。

部署一個消息端點

圖 5 展示了在一個訊息端點 (本例中是一個 MDB) 部署過程中發生的活動順序。如圖 5 所示,在 MDB 部署過程中,J2EE 應用程式伺服器使用部署描述符中的屬性實例化並配置 ActivationSpec。隨後它實例化 MessageEndpointFactory,並呼叫 ResourceAdapter 的 endpointActivation() 方法。該方法主要負責向訊息使用者交付訊息。

在範例應用程式中,您在 JamesResourceAdapter 的 endpointActivation() 方法中啟動了 JamesWork 執行緒,它又啟動了對 James 伺服器上電子郵件訊息的輪詢。如果在 James 伺服器上找到了任何電子郵件,JamesWork 就會處理電子郵件,如程式碼片段 12 所示。

private void processMsg(MimeMessage msg) {
 MessageEndpoint msgEndPoint = factory.createEndpoint(null);
 PurchaseOrderBean pobean = createPurchaseOrderBean(msg);
 JamesMessage email = new JamesMessage();
 email.setPoBean(pobean);
 ((JamesMessageListener) msgEndPoint).onMessage(email);
}

JamesWork 使用 MessageEndpointFactory 來建立端點 (本例中端點是一個 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 所示。

<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)。

<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 檔案的 dds 目錄中。

部署步驟

  1. 解除部署第 1 部分的 EJB JAR:如果您未按照第 1 部分介紹部署 po-ejb.jar,則可跳過此步驟。透過 $GERONIMO_HOME\bin\startup.bat 啟動 Geronimo,在 https://localhost:8080/console 存取 Geronimo 主控台 (預設使用者 ID 為 system,密碼為 manager),點選左側的 EJB JARS,點選 Uninstall 解除部署 PurchaseOrderEJB 和 PurchaseOrderMDB。
  2. 檢查 POMailet 和 POMatcher:確保在第 2 部分介紹的電子郵件應用程式已在 James 中部署,確保 James 正在執行。
  3. 部署 .ear 檔案:確認 Apache James 和 Geronimo 伺服器都在執行後,部署 po.ear 檔案 (位於 $part3.zip/deploy 目錄下)。在 Geronimo 主控台點選 Deploy New,瀏覽選擇 po.ear,點選 Install。部署成功後將看到成功訊息。

成功部署 po.ear 檔案後,您將在 Geronimo 主控台中看到一些日誌記錄。適配器及其端點 (PurchaseOrderMDB) 部署完成後,JamesResourceAdapter endpointActivated() 方法將被 Geronimo 呼叫,您的適配器就會開始週期性地 (每 15 秒) 為使用者 authorized-orders 輪詢 James 伺服器中的電子郵件訊息。

測試應用程式

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

您將修改這個 EmailClient,以發送將採購請求資料作為電子郵件主體的電子郵件訊息。

電子郵件訊息格式

首先,您應決定一種標準格式,以便 James 資源適配器輕鬆解析電子郵件訊息,並建構 PurchaseOrderBean 將其發送到 PurchaseOrderMDB。電子郵件訊息的格式如程式碼片段 15 所示。

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

客戶端程式

客戶端程式 EmailClient.java (程式碼片段 16) 提供發送電子郵件的方法。完整客戶端程式可在 $part3.home/src/examples/po/test 目錄下查看。

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() 將訊息實際傳遞到收件人的收件箱。

程式碼片段 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";
 // ... 讀取命令列參數 ...
 String content = "Item=" + ITEM + ";" +
 "Description=" + DESCRIPTION + ";" +
 "Price=" + UNITPRICE + ";" +
 "Quantity=" + QUANTITY;
 // ... 建立 EmailClient 並發送 ...
}

main() 方法接受幾個用於發送和讀取電子郵件的命令列參數。如程式碼片段 17 所示,sendEmail() 方法需要電子郵件主機、from 和 to 值,readInbox() 需要電子郵件主機以及一個電子郵件帳戶的使用者名稱和密碼。

main() 方法負責建構電子郵件訊息,採用程式碼片段 15 指定的格式,隨後將電子郵件發送到 orders@localhost.com。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)。

測試前的 PurchaseOrder 表內容

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

現在您可以執行 runSendEmail.cmd 腳本,從電子郵件地址 user2@localhost.com (記住,user1@localhost.com 是經過授權的寄件人) 向 orders@localhost.com 發送採購訂單電子郵件。

執行 runSendEmail.cmd 後,您將看到一些日誌記錄 (圖 12)。

透過執行 runSendEmail.cmd 測試應用程式

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

James 伺服器首先呼叫 POMatcher 的 match() 方法。由於寄件人 user1@localhost.com 是一名經過授權的寄件人,因此 POMailet 的 service() 方法將被呼叫,電子郵件應已轉發到 authorized-orders@localhost.com。您可能會疑惑,為什麼再次呼叫了 POMatcher match()? (參見圖 13)。

James 伺服器的控制台日誌

POMatcher match() 被再次呼叫的原因是您的 POMailet 的 service() 方法向 authorized-orders@localhost.com 發送了一封電子郵件。這封電子郵件在 James 伺服器內的處理方式與其他任何傳入的電子郵件都相同,因此伺服器呼叫 POMatcher match() 方法。

提示:這就是在將電子郵件轉發到 authorized-orders@localhost.com 之前,將 from 更改為 root@localhost.com 或其他未經授權的寄件人的原因,目的是避免出現無限遞迴迴圈。這是一個小技巧,因為 POP3 協定不支援電子郵件在資料夾之間的移動 (IMAP 支援此功能)。

現在,由於寄件人的電子郵件地址是 root@localhost.com,不是一個經過授權的寄件人,因此 POMailet 不會處理這封轉發過來的電子郵件。

檢查一下,POMailet 是否確實將電子郵件傳遞到了 authorized-orders@localhost.com。執行 runReadEmail.cmd,您將看到如圖 14 所示的結果。

顯示電子郵件

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

如果您查看 Geronimo 主控台,您將看到來自 JamesWork 的日誌訊息 (圖 15)。

發送一封電子郵件後的 Geronimo 主控台

執行在 Geronimo 中的 JamesWork 執行緒接收到了電子郵件訊息,它將此訊息解析為 Item、Description、Unit Price 和 Quantity,從而建立一個 PurchaseOrderBean。這個 Bean 隨後將打包到 JamesMessage 中,並發送給 PurchaseOrderMDB,PurchaseOrderMDB 呼叫 PurchaseOrderEJB 在資料庫中建立一個採購訂單。

查看結果

現在您可以看看 PurchaseOrderEJB 是否在 Derby 資料庫中輸入了一個新的採購訂單。如果您轉到 Geronimo Web 主控台並存取左側導覽窗格中顯示的 DBManager,可看到 PurchaseOrder 表的內容 (圖 16)。

Geronimo Web 主控台 DBManager

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

結語

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

本文配套原始碼

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved