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

讓您的應用程式呼叫我的應用程式,第 2 部分:James 應用程式

     編輯:Java 相關知識

承接第 1 部分未完成的學習,在第 1 部分中,您學習了如何開發訊息驅動 Bean (MDB)、實體 Bean 和容器管理持久性 (CMP),並了解如何在 Apache Geronimo 中部署與測試這些元件。本期是共分三部分系列教學的第 2 部分,將為您展示 Java Apache Mail Enterprise Server (Apache James) 的整體架構。您將了解如何建構、部署和測試電子郵件應用程式 (Mailet 和 Matcher),以及如何在 Apache James 電子郵件伺服器中部署這個應用程式。

開始之前

本系列教學面向想要學習如何使用各種 Java EE 元件——包含 MDB 和 JCA 資源適配器——來建構整合解決方案的 Java EE 程式設計師。本教學假設您熟悉基本的 Java 和 Java EE 概念,例如 EJB、JMS、MDB 和 UML 圖。

關於本系列

在這三部分的系列教學中,您將建構一個範例應用程式,透過整合不同的 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 互動。

關於本教學

在第 1 部分中,您建構了 MDB 和實體 Bean,從而在 Apache Derby 資料庫中建立一個採購訂單項目。在本期教學中,您將了解 Apache James 的整體架構,並建構一個電子郵件應用程式,用於處理傳入的電子郵件。

先決條件

本教學不要求您預先具備任何 Apache James 或 JavaMail 的知識,但希望您了解電子郵件的運作原理。

系統需求

完成本教學需要以下工具:

  • Apache Geronimo — Apache 提供的 Java EE 應用程式伺服器
  • Apache James 2.2 — 基於 Java 的 SMTP、POP3 和 NNTP 新聞伺服器
  • Apache Derby 資料庫 — 開放原始碼、輕量級資料庫,內嵌於 Geronimo 中,無需獨立安裝
  • Sun Microsystems 提供的 Java 1.4.2
  • 範例原始檔

要開始學習,請下載 part2.source.zip (請參閱下載部分),其中包含本教學中提到的原始檔、Mailet 二進位檔和 .bat 檔案。part2.source.zip 的組成如下:

  • deploy (po-mailet.jar,包含 Mailet 和 Matcher)
  • lib (tester.jar)
  • src (Mailet、Matcher 和測試客戶端的 Java 檔案)
  • deploy.cmd
  • undeploy.cmd
  • runSendEmail.cmd
  • runReadEmail.cmd

Apache James — 概觀

為了繼續開發範例應用程式,您需要很好地理解 Apache James 伺服器。本節簡單介紹 James 伺服器及其元件。

James 伺服器架構概覽

Apache James 是什麼?

Apache James 也可簡稱為 James,它是 Java Apache Mail Enterprise Server 的縮寫。James 是基於 100% 純 Java 的電子郵件伺服器,使用 Java 2 SDK 和 JavaMail 1.3 API 建構而成,因而具有高度的可移植性,可以移植到不同的平台上。James 是一種獨立的郵件伺服器,提供了一個完整的電子郵件解決方案,既可發送又可儲存電子郵件,無需任何其他軟體。

Apache James 架構

如圖 1 所示,就像其他任何電子郵件伺服器一樣,James 提供了大多數標準的電子郵件服務,例如用於發送電子郵件的 SMTP 和檢索電子郵件的 POP3 等。

James 還支援閱讀新聞群組、在新聞群組中發文的 NNTP,以及有助於從外部電子郵件伺服器檢索電子郵件訊息的 FetchMail。目前,James 的 IMAP 實作仍處於實驗階段,有望在 James V3 中發布。

除了上述服務之外,James 還有一個稱為假脫機管理器 (Spool Manager) 的郵件引擎,用於處理傳入的電子郵件。James 伺服器的所有元件,包括假脫機管理器在內,均使用自己的儲存庫來儲存各種資料,例如使用者資訊、電子郵件、新聞等。James 支援不同的儲存庫格式,例如檔案系統、資料庫、DBFile。

當今的大多數電子郵件伺服器都提供了這些特性,那麼我們為什麼要用 James 呢?

James 不僅僅是一種可移植、獨立的電子郵件伺服器,更提供了獨一無二的可插入式架構,允許使用者建構定製的電子郵件應用程式。這些應用程式稱為 Matcher 和 Mailet (如圖 1 所示),與 Java Servlet 類似,在 James 郵件引擎 (也稱為 Mailet 容器) 內部署和配置。

James 提供了一種低調、開箱即用的管理控制台,稱為遠程管理器。這是一種基於 Telnet 的命令列工具,可用於新增或刪除使用者、配置、轉發和別名等服務。

Mailet 和 Matcher

James 建構在健壯、可定製的架構之上,這有助於使用者開發定製的電子郵件應用程式,允許他們發送電子郵件或根據業務規則對傳入的電子郵件進行處理。這些定製的電子郵件應用程式就稱為 Mailet 和 Matcher。James 提供了一種非常公開的 Mailet API,使開發人員能夠輕鬆開始編寫定製應用程式。

Mailet 或 Matcher 與 James Mailet 容器的關係,就像 Servlet 與 Java Web 容器的關係。如表 1 所示,Mailet 和 Matcher 都具有與 Servlet 類似的定義良好的生命週期。但與接收 HTTP 請求的 Servlet 不同,Mailet 和 Matcher 接收的是傳入的電子郵件訊息。Apache James 提供了大量的 Mailet 和 Matcher。

ServletMailetMatcher
開發工具:Java Servlet APIJames Mailet API 和 JavaMail APIJames Mailet API 和 JavaMail API
生命週期方法:init(ServletConfig)init(MailetConfig)init(MatcherConfig)
service(ServletRequest, ServletResponse)service(Mail)match(Mail)
doGet()/doPost()
destroy()destroy()destroy()
部署環境:Java Web 伺服器 (如 Tomcat)James 伺服器James 伺服器

Matcher 和 Mailet 與 James 伺服器交互

Mailet

Mailet 是一種定製的應用程式元件,擁有處理傳入電子郵件訊息的應用程式邏輯。例如,您可編寫一個 Mailet 來過濾垃圾郵件、在您外出度假期間自動回覆郵件,或在接收到新電子郵件時向您的手機發送簡訊 (SMS)。可能性無窮無盡。圖 2 列出了定製 Mailet 必須實作的所有生命週期方法。在部署定製 Mailet 後,James 伺服器將初始化定製 Mailet,方法是呼叫其 init() 方法一次。此後,每傳入一條新的電子郵件訊息,service() 方法就會被呼叫。取消部署 Mailet 時,或者伺服器關閉期間,Mailet 的 destroy() 方法將被呼叫,以釋放 Mailet 應用程式使用的所有資源。

Matcher

另一方面,Matcher 負責篩選電子郵件,而不是處理它們。Matcher 的生命週期方法的行為與 Mailet 類似,不同之處只有 match() 方法。與 Mailet 的 service() 方法不同,match() 方法返回必須由 Mailet 處理的一組收件人電子郵件地址。

Matcher 通常與 Mailet 配對使用,以開發複雜的電子郵件應用程式。圖 2 描述了 James 伺服器中對傳入電子郵件訊息的處理流程。

James 伺服器在開發或啟動過程中初始化 Matcher 和 Mailet。James 伺服器接收到電子郵件時,它將呼叫部署好的 Matcher 的 match() 方法。若 Matcher 返回收件人電子郵件地址列表,James 會將電子郵件地址發送給相應的 Mailet,供其進一步處理。

安裝和配置 James 伺服器

這一部分詳細介紹 Apache James 伺服器的安裝和配置。如果您尚未下載 James 伺服器,請立即下載 (下載連結請參閱系統需求部分)。我們將使用 Apache James 2.2。

安裝

下載 James 2.2 的 .zip 檔案後,將其解壓縮到本地磁碟機 (假設是 c:\),您將擁有目錄 c:\james-2.2.0。James 安裝與其他 Apache 產品安裝並無差別,只需要解壓縮封存檔即可。

在啟動 James 伺服器之前,請確保您已設定好 JAVA_HOME。如果您尚未在環境中設定 JAVA_HOME,請在 c:\james-2.2.0\bin 目錄下的 run.bat 檔案中進行設定。建議您搭配 JRE 1.4.x 與 James 使用。

現在您已經為啟動 James 伺服器做好一切準備。開啟命令提示字元,執行 c:\james-2.2.0\bin\run.bat 檔案。成功啟動 James 後,您將看到一些日誌記錄 (圖 3)。

帶有 James 伺服器日誌記錄的控制台

在 James 啟動時,有些服務將在其預設連接埠啟動:SMTP 服務在連接埠 25、POP3 服務在連接埠 110、NNTP 服務在連接埠 119、遠程管理器服務在連接埠 4555。

要停止 James 伺服器,可在控制台視窗中按下 Ctrl+C。

配置

以下介紹一些配置細節。雖然在本教學中不會用到所有這些內容,但在將 James 用於生產環境中的電子郵件伺服器時,這些細節無疑是有用的。James 的大多數配置資訊都儲存在 C:\james-2.2.0\apps\james\SAR-INF 目錄下的 config.xml 檔案中。初次安裝 James 時,您不會看到這個檔案,該檔案只有在第一次執行伺服器後才會建立。

讓我們來看看一些基本但重要的配置細節。預設情況下,伺服器名稱為 localhost,但為學習本教學,請將其更改為 localhost.com。如果願意,您可在這裡按自己的意願指定伺服器名稱 (程式碼片段 1)。

<servernames autodetect="true" autodetectIP="true">
 <servername>localhost</servername>
</servernames>

預設情況下,James 中已建立一名使用者 root,其密碼為 root:<account login="root" password="root"/>。

localhost IP 127.0.0.1 將預設新增為 DNS 伺服器。對於本教學來說,這已足夠,因為您將在本地機器上執行 James。但在生產環境中,您必須在以下部分中為 DNS 伺服器新增一項 (程式碼片段 2)。

<dnsserver>
 <servers>
 <server>127.0.0.1</server>
 </servers>
 <autodiscover>true</autodiscover>
 <authoritative>false</authoritative>
</dnsserver>

James 設定為使用檔案系統作為預設儲存庫。同樣,對於我們的範例應用程式來說,您不必更改這一設定,但 James 確實提供了其他可選資料庫或 DBFile 儲存庫,在生產環境中的電子郵件伺服器中,它們可能是更好的選擇 (程式碼片段 3)。

<inboxRepository>
 <repository destinationURL="file://var/mail/inboxes/" type="MAIL"/>
</inboxRepository>

這樣,透過在 config.xml 檔案中使用不同的選項,您就可以按照自己的需求來打造 James。後文中您還將為本文的範例應用程式再次配置 James。

應用程式設計

您已經熟悉了 Apache James,那麼就可以繼續建構範例應用程式了。在這一部分中,您將建構一個電子郵件應用程式,包含一個 Matcher 和一個 Mailet,用於處理 Foo, Inc. 員工發送的採購請求電子郵件。

首先識別您的業務規則,因為它們將管理 Matcher 和 Mailet 的邏輯。

業務規則

首先,您需要一種機制來區分採購請求和非採購請求電子郵件。假設所有主旨為 Purchase Request 的電子郵件都是採購請求電子郵件。一旦您識別出一封採購請求電子郵件,就需要驗證寄件人是否有權限請求採購。您將利用寄件人的電子郵件地址驗證授權。如果寄件人經過授權,則將採購請求電子郵件轉發到指定的電子郵件地址,所有經過授權的採購請求都儲存在那裡。如果寄件人未經過授權,則不對採購請求電子郵件進行任何處理。

整合了業務規則之後,您就有了以下匹配規則和處理行為:

  • 匹配規則:主旨是 Purchase Request;寄件人的電子郵件地址有效。
  • 處理行為:將電子郵件轉發到儲存經過授權的採購請求電子郵件的指定資料夾。

在這個練習中,您已識別出 Matcher (匹配規則) 和 Mailet (處理行為) 的應用程式邏輯。現在我們開始編寫一些程式碼。

Matcher

您已為 Matcher 識別出兩條匹配規則:第一條是主旨匹配,第二條是檢查寄件人是否經過授權。要驗證寄件人的授權情況,您需要根據一個經過授權的電子郵件地址列表驗證寄件人的電子郵件地址。我們在初始化過程中為 Matcher 提供經過授權的電子郵件列表。您將在部署一節中了解如何為 Matcher 配置初始化屬性。

編寫定製 Matcher 的第一步就是查看 James Mailet API 提供的 GenericMatcher,並對其加以擴展。將您的 Matcher 命名為 POMatcher。GenericMatcher 有一個預設實作,提供了大多數必需的方法,僅有一個抽象方法 match(Mail)。程式碼片段 4 顯示 POMatcher.java 的 init() 方法。

public void init(MatcherConfig config) throws MessagingException {
 super.init(config);
 ilog("Entering POMatcher init() " + config);
 // 逗號分隔的授權電子郵件地址列表
 String senders = getCondition();
 // 初始化授權寄件人列表
 StringTokenizer st = new StringTokenizer(senders, ",");
 authorizedSenderList = new HashMap();
 while (st.hasMoreTokens()) {
 String sender = st.nextToken();
 authorizedSenderList.put(sender, sender);
 }
 ilog("Authorized Senders are: " + authorizedSenderList);
 ilog("Exiting POMatcher init()");
}

在 init() 方法中,您呼叫了 getCondition(),它返回配置時設定的參數值。這是一個以逗號分隔的經授權電子郵件地址列表。現在看程式碼片段 5 中的 match() 方法。

public Collection match(Mail mail) throws MessagingException {
 ilog("Entering POMatcher match() " + mail);
 MimeMessage mimeMsg = mail.getMessage();
 String subject = mimeMsg.getSubject();
 InternetAddress[] fromList = (InternetAddress[]) mimeMsg.getFrom();
 // 檢查主旨是否為 'Purchase Request'
 if (subject != null && subject.indexOf(PURCHASE_REQUEST) > 0) {
 // 檢查寄件人是否被授權請求採購
 if (isSenderAuthorized(fromList)) {
 ilog("Exiting POMatcher match()");
 return mail.getRecipients();
 }
 }
 ilog("Sender is not authorized. " + fromList);
 ilog("Exiting POMatcher match()");
 return null;
}

private boolean isSenderAuthorized(InternetAddress[] fromList) {
 boolean foundSender = false;
 for (int i = 0; i < fromList.length; i++) {
 if (authorizedSenderList.containsKey(fromList[i].toString())) {
 foundSender = true;
 }
 }
 return foundSender;
}

isSenderAuthorized() 方法檢查寄件人的電子郵件地址是否出現在經授權的寄件人列表中,若找到匹配項,則返回 true。在 match() 方法中,您根據兩條匹配規則檢查主旨和經過授權的寄件人。如果兩個條件都滿足,則返回整個收件人列表,否則返回 null。

POMatcher 的原始檔 (.java) 可在 $part2.home/src/examples/po/email/matcher 目錄下找到。

Mailet

根據您的業務規則,Mailet 只有一種處理行為,就是將電子郵件訊息轉發到其他電子郵件資料夾。Mailet 的初始化屬性 (在部署過程中設定) 包括:

  • forward:這是轉發電子郵件的目標地址。
  • from:這是轉發電子郵件的新寄件人電子郵件地址。如果您在轉發電子郵件之前未更改寄件人,Mailet 將進入無限遞迴迴圈處理。

編寫定製 Mailet 的第一步與 Matcher 類似,不同之處在於您必須查看 James Mailet API 提供的 GenericMailet 並擴展它。您將呼叫 Mailet 類別 POMailet。GenericMailet 有一個預設實作,提供了大多數必需的方法,只有一個抽象方法 service(Mail)。程式碼片段 6 顯示 POMailet.java 的 init() 方法。

public void init(MailetConfig config) throws MessagingException {
 super.init(config);
 ilog("Entering POMailet init() " + config);
 this.forwardEmail = getInitParameter("forward");
 this.from = getInitParameter("from");
 ilog("Forward is: " + this.forwardEmail);
 ilog("From is: " + this.from);
 ilog("Exiting POMailet init()");
}

在 POMailet.java 的 init() 中,您讀取了初始化參數 forward 和 from。這些參數的值是在部署過程中設定的。程式碼片段 7 顯示 POMailet 的 service() 方法。

public void service(Mail mail) throws MessagingException {
 ilog("Entering PO Mailet : " + mail);
 MimeMessage email = mail.getMessage();
 ilog("Email is: " + email);
 // 將電子郵件移動到特定使用者收件箱
 InternetAddress address = new InternetAddress(forwardEmail);
 Address[] list = { address };
 MimeMessage copymsg = new MimeMessage(email);
 // 修改主旨,加入請求者電子郵件地址
 Address[] sentByList = copymsg.getFrom();
 String sentBy = sentByList[0].toString();
 copymsg.setSubject("Purchase Request by: " + sentBy);
 // 修改寄件人電子郵件為 'from'
 InternetAddress newFrom = new InternetAddress(from);
 copymsg.setFrom(newFrom);
 // 發送電子郵件到新收件人 (如轉發電子郵件)
 Transport.send(copymsg, list);
 ilog("Exiting PO Mailet");
}

service() 方法不進行任何檢查,因為檢查已經由 POMatcher 完成了,它只是修改主旨,使主旨中包含寄件人的電子郵件地址,並將電子郵件訊息轉發到 forward 參數指定的其他電子郵件資料夾中。

您可在 $part2.home/src/examples/po/email/mailet 目錄下找到 POMailet 的原始檔 (.java)。

至此,我們已經編寫好 Mailet 和 Matcher,這就完成了範例電子郵件應用程式。由於您已安裝了 James,所以可以配置 James,然後再部署您的範例電子郵件應用程式。

配置 James

在這一部分中,您將透過建立使用者帳戶來配置 James,完成之後就可以在下一節中部署範例電子郵件應用程式了。

建立使用者帳戶

與其他配置不同,要建立使用者,您不必忙於處理 config.xml,而是使用 James 提供的基於 Telnet 的客戶端——遠程管理器。

就本教學的目的而言,您將建立以下使用者帳戶,以 使用者名稱/密碼 的格式列出:

  • user1 / password
  • user2 / password
  • user3 / password
  • orders / password
  • authorized-orders / password

為此,使用 c:\james-2.2.0\bin\run.bat 啟動 James 伺服器,這將啟動遠程管理器服務。現在,Telnet 連接埠 4555 接入 localhost,您將看到如圖 4 所示的登入提示。

遠程管理器服務的登入提示

輸入預設使用者/密碼,如果您未在 config.xml 檔案中加以改動,應該是 root / root,輸入後按下 Enter。建立新使用者的命令是 adduser username password。建立全部 5 個使用者帳戶 (user1、user2、user3、orders 和 authorized-orders),如圖 5 所示。

建立全部 5 個使用者

建立好所有使用者後,輸入 quit 登出遠程管理器服務。既然配置好使用者帳戶,就可以部署您的電子郵件應用程式了。

部署

在 James 伺服器中部署一個電子郵件應用程式共分兩步。在部署範例應用程式時,請確保您的伺服器未在執行,因為您將修改 config.xml 檔案。

步驟 1:部署定製 Mailet 和 Matcher .jar 檔案

所下載的檔案 part2.source.zip 的 $part2.home 目錄中具有部署 (deploy.cmd) 和取消部署 (undeploy.cmd) 的腳本 (請參閱下載部分)。在使用前務必修改命令檔案中的 JAVA_HOME 和 JAMES_HOME。

定製電子郵件應用程式的部署實際上只是將一個 .jar 檔案 (由 POMatcher.class 和 POMailet.class 構成的 po-mailet.jar) 複製到 $JAMES_HOME\lib 目錄中。

要執行這個範例應用程式,需要以下 .jar 檔案。您可在 C:\james-2.2.0\work\james-xxxxxxxxxxxxx\SAR-INF\lib 目錄下找到這些 .jar 檔案 (其中的變數 x 代表特定於您機器的號碼)。

  • activation.jar
  • mailet_1_0.jar
  • mail-1.3.1.jar

將這些 .jar 檔案複製到 $JAMES_HOME\lib 目錄。

步驟 2:配置

對於每一個定製的 Mailet 和 Matcher,您都必須在 config.xml 檔案中為其新增一項,格式如程式碼片段 9 所示。

在 config.xml 檔案中為您的範例 Matcher (POMatcher) 和 Mailet (POMailet) 新增套件。預設情況下,config.xml 具有以下由 James 預設為 Mailet 和 Matcher 提供的項 (程式碼片段 8)。

<mailetpackages>
 <mailetpackage>org.apache.james.transport.mailets</mailetpackage>
</mailetpackages>
<matcherpackages>
 <matcherpackage>org.apache.james.transport.matchers</matcherpackage>
</matcherpackages>

為我們的範例應用程式新增套件 (examples.po.email.mailet 和 examples.po.email.matcher),如程式碼片段 9 所示。

<mailetpackages>
 <mailetpackage>org.apache.james.transport.mailets</mailetpackage>
 <mailetpackage>examples.po.email.mailet</mailetpackage>
</mailetpackages>
<matcherpackages>
 <matcherpackage>org.apache.james.transport.matchers</matcherpackage>
 <matcherpackage>examples.po.email.matcher</matcherpackage>
</matcherpackages>

在 config.xml 檔案中為您的範例 Mailet 和 Matcher 新增項,如程式碼片段 10 所示。

<mailet match="POMatcher=user1@localhost.com,user2@localhost.com"
 class="POMailet">
 <forward>authorized-orders@localhost.com</forward>
 <from>root@localhost.com</from>
</mailet>

這就是通常情況下將定製 Mailet 和 Matcher 同樣定義的方法。一個 Matcher 總是與一個 Mailet 一起定義。

定義 Matcher 的語法如下:MatcherClassName[=][properties]。MatchClassName 是必需的,但初始化屬性是可選的。

在本例中,有 POMatcher= user1@localhost.com,user2@localhost.com。參見程式碼片段 4 中的 POMatcher.java;在 init() 方法中,您讀取的是一個以逗號分隔的經授權電子郵件地址列表。這一列表在此處設定為 user1@localhost.com,user2@localhost.com。getCondition() 方法在 POMatcher 中返回這個值。這就意味著,僅有 user1 和 user2 經過授權,可以發送帶有採購請求的電子郵件 (user3 未經授權)。

程式碼片段 11 展示了定義 Mailet 的語法。

<mailet match="MatchClassName[=][properties]"
 Class="MailetClassName">
 <initialization-property-name>value</initialization-property-name>
</mailet>

在範例中可以看出,您使用了兩個初始化參數,其名稱分別為 forward 和 from。從範例應用程式的角度來看,經過授權的電子郵件將被轉發給 forward 屬性中指定的電子郵件地址處。

您已配置好範例 Mailet 和 Matcher,那麼繼續操作,啟動伺服器。您將了解在伺服器啟動過程中,POMatcher 和 POMailet 是如何初始化的,如圖 6 所示。

POMatcher 和 POMailet 在 James 啟動時初始化

如圖 6 所示,POMatcher 和 POMailet 都是由 James 伺服器按此順序初始化的。

您已成功地部署了範例電子郵件應用程式。現在可以建立一個客戶端或測試程式來測試您的應用程式了。

測試應用程式

在這部分中,您將編寫一個電子郵件客戶端,它可以發送電子郵件並讀取使用者收件箱中的電子郵件。

電子郵件客戶端

客戶端程式 EmailClient.java (程式碼片段 12 和 13) 提供了發送電子郵件和顯示使用者收件箱中電子郵件的方法。您可在 $part2.home/src/examples/po/test 目錄下查看完整客戶端程式 EmailClient.java,獲取此程式的方式請參閱下載部分。

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() 來實際地將訊息傳遞到收件人的收件箱中。您將使用此方法來發送採購請求電子郵件。

現在來看看如何讀取使用者收件箱中的電子郵件 (程式碼片段 13)。您將使用 readInbox 方法來驗證您的範例電子郵件應用程式 (Matcher 和 Mailet) 是否將來自經授權寄件人的採購請求電子郵件發送到另一個電子郵件資料夾中。

public void readInbox(String user, String password) {
 Session session = createSession(host, user, password);
 Store store = null;
 Folder inboxFolder = null;
 try {
 store = session.getStore();
 store.connect();
 Folder rootFolder = store.getDefaultFolder();
 inboxFolder = rootFolder.getFolder("inbox");
 inboxFolder.open(Folder.READ_ONLY);
 Message[] emails = inboxFolder.getMessages();
 System.out.println("Inbox for User: [" + user + "]");
 System.out.println("---------------------------------------");
 for (int i = 0; i < emails.length; i++) {
 MimeMessage msg = (MimeMessage) emails[i];
 System.out.println("Email Message No. :[" + (i+1) + "]");
 System.out.println();
 System.out.println(" - From: " + msg.getFrom()[0]);
 System.out.println(" - Subject: " + msg.getSubject());
 System.out.println(" - Content: " + msg.getContent());
 System.out.println();
 }
 System.out.println("---------------------------------------");
 } catch (Exception e) {
 e.printStackTrace();
 } finally {
 try { inboxFolder.close(true); } catch (MessagingException e) { e.printStackTrace(); }
 try { store.close(); } catch (MessagingException e) { e.printStackTrace(); }
 }
}

readInbox 方法負責在電子郵件伺服器 (James 伺服器) 上讀取一名使用者的收件箱,並顯示收件箱資料夾中出現的所有電子郵件訊息。如程式碼片段 13 所示,您需要建立一個新的會話物件,並連接到儲存,這樣才能獲得對 James 伺服器上使用者收件箱資料夾的存取權限。確保以唯讀模式開啟收件箱資料夾 (inboxFolder.open(Folder.READ_ONLY)),然後在其中讀取郵件。獲得收件箱資料夾的控制代碼後,您可讀取所有訊息、遍歷列表並顯示每一條電子郵件訊息。讀取完成後,不要忘記關閉收件箱資料夾和儲存以釋放資源。

程式碼片段 14 展示了實際呼叫 sendEmail 和 readInbox 方法的 main() 方法的部分內容。

public static void main(String a[]) throws Exception {
 // 檢查是否提供 from 和 to
 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 = "Purchase Request sent on :" + new Date(cal.getTime().getTime());
 boolean isSend = false;
 // 檢查命令列參數並列印用法
 if(mode != null && mode.equalsIgnoreCase("send")) {
 host = a[0]; from = a[1]; to = a[2]; isSend = true;
 } else if(mode != null && mode.equalsIgnoreCase("read")) {
 host = a[0]; user = a[1]; password = a[2];
 }
 EmailClient client = new EmailClient(host);
 if(isSend) client.sendEmail(from, to, subject, content);
 else client.readInbox(user, password);
}

main() 方法公開了幾個命令列參數,用於發送和讀取電子郵件。在程式碼片段 14 中可以看到,sendEmail() 方法需要一個電子郵件主機,還需要 from 和 to 值,readInbox() 需要電子郵件主機、一個電子郵件帳戶的使用者名稱和密碼。

執行測試

part2.source.zip 檔案中提供了腳本 (runSendEmail.cmd 和 runReadEmail.cmd),用於測試範例電子郵件應用程式 (可在下載部分中獲取)。如果您按本教學的指導正確配置好應用程式,只需修改 JAVA_HOME 以執行這些測試即可。

如果 James 伺服器未執行,請啟動它。首先,您執行 runSendEmail.cmd 腳本,從電子郵件地址 user2@localhost.com (切記,user1@localhost.com 是一個經過授權的寄件人) 向 orders@localhost.com (該地址可透過更改 EMAIL_TO 參數在 runSendEmail.cmd 中修改) 發送採購請求電子郵件。

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

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

如果您看到圖 7 所示的訊息,就表示電子郵件已成功發送。在 James 伺服器中,這一傳入的電子郵件將由 POMatcher-POMailet 處理,如圖 8 所示。

James 伺服器的控制台日誌

James 伺服器首先呼叫 POMatcher match() 方法。由於寄件人 user1@localhost.com 是一個經過授權的寄件人,POMailet 的 service() 方法將被呼叫,電子郵件應已轉發到 authorized-orders@localhost.com。您一定很疑惑,為什麼又一次呼叫了 POMatcher match() (參見圖 9)。答案是:我們的 POMailet 的 service() 方法將電子郵件發送到 authorized-orders@localhost.com。這一電子郵件在 James 伺服器中得到了與其他任何傳入的電子郵件相同的處理,因此,伺服器呼叫 POMatcher match() 方法。(提示:這就是首先將 from 更改為 root@localhost.com 或其他任何未經授權的寄件人、再將電子郵件轉發到 authorized-orders@localhost.com 的原因所在,這是為了避免出現無限遞迴迴圈)。這是一個小小的工作區,因為 POP3 協定不支援在資料夾之間移動電子郵件 (IMAP 支援這一功能)。

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

下面檢查一下,看看 POMailet 是否確實將電子郵件發送到了 authorized-orders@localhost.com。

檢查結果

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

顯示電子郵件

如圖 9 所示,authorized-orders 收件箱中的電子郵件顯示了出來。

結語

恭喜您!第 2 部分的應用程式至此已全部完成,您已學會了使用 Apache James 伺服器建構、部署和測試電子郵件應用程式的方法。

在本系列教學的最後一期:第 3 部分中,您將為 Apache James 伺服器開發一個 JCA 資源適配器 (也可稱為連接器),它可將第 2 部分的應用程式 (Mailet 和 Matcher) 連接到第 1 部分的應用程式 (MDB)。

本文配套原始碼

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