本系列教學共分三部分。在第 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 應用程式伺服器。
本系列的最後一期將詳細說明不同 J2EE 元件 (MDB 和 JCA 適配器) 之間的互動。您將了解基於 JCA 的資源適配器,並建構一個連接到 Apache James 伺服器的範例適配器。
本教學假設您熟悉基本的 Java、J2EE 和 Java EE 概念,例如 EJB、Java Message Service (JMS)、MDB 和 Unified Modeling Language (UML) 圖。不要求您預先具備任何 JCA 的知識。
完成本教學需要以下工具:
首先下載 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.cmdrunReadEmail.cmd在這一部分中,我們將探索應用程式整合的演進歷程,這最終導致了 JCA 的誕生。

回溯到 2000 年之前,整合異構的企業與舊有系統 (例如 SAP、Siebel 和 Peoplesoft) 不僅成本高昂,更是一項耗時漫長的特殊過程。這類過程通常稱為企業應用程式整合 (EAI),許多軟體廠商將其作為專有服務提供。
到了 2000 年底,J2EE Connector Architecture (簡稱 JCA,請勿與 Java Cryptography Architecture 混淆) 首次為 EAI 引入了標準。JCA 與 J2EE 1.3 的出現,標誌著 2001 年整合解決方案開發的巨大飛躍,也決定了專有技術在整合領域的衰退。
在研究資源適配器之前,先了解幾個關鍵定義:
資源適配器是一個 J2EE 元件,如同 EJB、MDB 等。每個 J2EE 元件都遵循特定規格,資源適配器必須遵循 J2EE Connector Architecture 這個定義完善的規格。資源適配器在整合 J2EE 解決方案中扮演關鍵角色:負責與後端 EIS 的所有通訊。
資源適配器扮演入口角色,讓任何 J2EE 元件能存取後端資源 (通常是 EIS)。它獨自承擔連接 EIS、呼叫 EIS 系統上可用服務等責任。資源適配器之於 EIS 系統,猶如實體 Bean 之於資料庫。
資源適配器可部署在 J2EE 應用程式伺服器內,稱為受管理案例;不部署在任何伺服器內而獨立運作,則稱為非受管理案例。本教學使用受管理案例,因為您將把資源適配器部署在 Apache Geronimo (一個 J2EE 應用程式伺服器) 中。
JCA 規格定義一組契約,管理資源適配器的各個層面,例如連接或交易等。本節說明 JCA 1.0 和 1.5 兩個版本中定義的契約。
JCA 1.0 於 2000 年 11 月發布,是第一個規格。它在較廣泛的層級定義了兩類契約:系統層級契約與應用程式層級契約。
系統層級契約包含:
應用程式層級契約 (CCI) 為應用程式元件 (如 EJB 和應用程式客戶端) 定義一組客戶端 API,以便與資源適配器互動。它們支援由客戶端應用程式發起的同步通訊。

JCA 1.5 是最新規格,補充了 JCA 1.0 缺失的部分。除了上述 JCA 1.0 契約外,JCA 1.5 還定義了管理契約和入站契約。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 部分中,您建構了一個 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 應用程式伺服器在各種事件上呼叫這些方法,如適配器部署、伺服器關機等。
資源適配器將 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。
資源適配器應將 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 伺服器上電子郵件收件箱時必需的。
現在您需要實作工作管理契約。
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() 訊息。
資源適配器不需要實作 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 以在資料庫中建立新採購訂單條目。
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 目錄中。
po-ejb.jar,則可跳過此步驟。透過 $GERONIMO_HOME\bin\startup.bat 啟動 Geronimo,在 https://localhost:8080/console 存取 Geronimo 主控台 (預設使用者 ID 為 system,密碼為 manager),點選左側的 EJB JARS,點選 Uninstall 解除部署 PurchaseOrderEJB 和 PurchaseOrderMDB。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 表中沒有任何條目。
現在您可以執行 runSendEmail.cmd 腳本,從電子郵件地址 user2@localhost.com (記住,user1@localhost.com 是經過授權的寄件人) 向 orders@localhost.com 發送採購訂單電子郵件。
執行 runSendEmail.cmd 後,您將看到一些日誌記錄 (圖 12)。

如果您看到如圖 12 所示的訊息,就表示電子郵件已成功提交。這個傳入 James 伺服器的電子郵件將由您的 POMatcher-POMailet 處理。
James 伺服器首先呼叫 POMatcher 的 match() 方法。由於寄件人 user1@localhost.com 是一名經過授權的寄件人,因此 POMailet 的 service() 方法將被呼叫,電子郵件應已轉發到 authorized-orders@localhost.com。您可能會疑惑,為什麼再次呼叫了 POMatcher match()? (參見圖 13)。

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 中的 JamesWork 執行緒接收到了電子郵件訊息,它將此訊息解析為 Item、Description、Unit Price 和 Quantity,從而建立一個 PurchaseOrderBean。這個 Bean 隨後將打包到 JamesMessage 中,並發送給 PurchaseOrderMDB,PurchaseOrderMDB 呼叫 PurchaseOrderEJB 在資料庫中建立一個採購訂單。
現在您可以看看 PurchaseOrderEJB 是否在 Derby 資料庫中輸入了一個新的採購訂單。如果您轉到 Geronimo Web 主控台並存取左側導覽窗格中顯示的 DBManager,可看到 PurchaseOrder 表的內容 (圖 16)。

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