WEB开发网
开发学院软件开发Java 使您的应用程序调用我的应用程序,第 2 部分: Jam... 阅读

使您的应用程序调用我的应用程序,第 2 部分: James 应用程序

 2010-03-19 00:00:00 来源:WEB开发网   
核心提示:继续您在 第 1 部分 中尚未完成的学习,在第 1 部分中,使您的应用程序调用我的应用程序,第 2 部分: James 应用程序,您学习了如何开发消息驱动 bean(MDB)、实体 bean 和容器管理的持久性(CMP),还了解了如何在 Apache Geronimo 中部署并测试这些组件,在本系列教程的最后一期:第

继续您在 第 1 部分 中尚未完成的学习,在第 1 部分中,您学习了如何开发消息驱动 bean(MDB)、实体 bean 和容器管理的持久性(CMP),还了解了如何在 Apache Geronimo 中部署并测试这些组件。本期是共分三部分的系列教程的第 2 部分,为您展示 Java Apache Mail Enterprise Server(也称为 Apache James)的整体架构。您将了解如何构建、部署和测试电子邮件应用程序(mailet 和 matcher),以及如何在 Apache James 电子邮件服务器中部署这个应用程序。

开始之前

本系列教程面向希望学习如何使用各种 Java EE 组件 —— 包括 MDB 和 Java 2 Platform,Enterprise Edition (J2EE)Connector Architecture(JCA)资源适配器 —— 构建集成化解决方案的 Java™ Platform,Enterprise Edition(Java EE)程序员。本教程假设您熟悉基本的 Java 和 Java EE 概念,例如 EJB、Java Message Service(JMS)、MDB 和 Unified Modeling Language(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 的 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

示例源文件

要开始学习,请下载 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 服务器及其组件。

Apache James 是什么?

Apache James 也可简称为 James,它是 Java Apache Mail Enterprise Server 的缩写。James 是基于 100% 纯 Java 的电子邮件服务器,使用 Java 2 SDK 和 JavaMail 1.3 API 构建而成,因而具有高度的可移植性,可以移植到不同的平台上。James 是一种独立的邮件服务器,提供了一个完整的电子邮件解决方案,既可发送又可存储电子邮件,无需任何其他软件。

Apache James 架构

下面更详细地介绍 James 架构及其部分优点。如 图 1 所示,正像其他任何电子邮件服务器一样,James 提供了大多数标准的电子邮件服务,例如用于发送电子邮件的 Simple Mail Transfer Protocol(SMTP)和检索电子邮件的 Post Office Protocol(POP3)等。

James 还支持阅读新闻组、在新闻组中发帖的 Net News Transfer Protocol(NNTP)和有助于从外部电子邮件服务器检索电子邮件消息的 FetchMail。目前,James 的 Internet Message Access Protocol(IMAP)实现依然处于实验阶段,有望在 James V3 中发布。

图 1. James 服务器架构概览
使您的应用程序调用我的应用程序,第 2 部分: James 应用程序

除了上述服务之外,James 还有一个称为假脱机管理器(spool manager)的邮件引擎,用于处理传入的电子邮件。James 服务器的所有组件,包括假脱机管理器在内,均使用自己的储存库来存储各种数据,例如用户信息、电子邮件、新闻等等。James 支持不同的储存库格式,例如文件系统、数据库、DBFile。当今的大多数电子邮件服务器都提供了这些特性,那么我们为什么要用 James 呢?

James 不仅仅是一种可移植、独立的电子邮件服务器,更提供了独一无二的可插入式架构,允许用户构建定制的电子邮件应用程序。这些应用程序称为 matcher 和 mailet(如 图 1 所示),与 Java servlet 类似,在 James 邮件引擎(也称为 mailet 容器)内部署和配置。

James 提供了一种低调、开箱即用的管理控制台,称为远程管理器。这是一种基于 Telnet 的命令行工具,可用于添加或删除用户、配置、转发和别名等服务。

现在您对 James 服务器的架构和其中的组件应该已经有了很好的理解,接下来探讨 mailet 和 matcher。

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。

表 1. servlet、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 服务器

mailet 是一种定制的应用程序组件,具有处理传入的电子邮件消息的应用程序逻辑。例如,您可编写一个 mailet 来过滤垃圾邮件、在您外出度假期间自动回复邮件,或在接收到新电子邮件时向您的手机发送短消息(SMS)。可能性无穷无尽。图 2 列出了定制 mailet 必须实现的所有生命周期方法。在部署定制 mailet 后,James 服务器将初始化定制 mailet,方法是调用其 init () 方法一次。此后,每传入一条新的电子邮件消息,service () 方法就会被调用。取消部署 mailet 时,或者服务器关闭期间,mailet 的 destroy () 方法将被调用,以释放 mailet 应用程序使用的所有资源。

另一方面,matcher 负责发送电子邮件,而不是处理它们。matcher 的生命周期方法的行为与 mailet 类似,不同之处只有 match () 方法。与 mailet 的 service () 方法不同,match () 方法返回必须由 mailet 处理的一组收件人电子邮件地址。

matcher 通常与 mailet 配对使用,以开发复杂的电子邮件应用程序。图 2 描述了 James 服务器中对传入电子邮件消息的处理流。

图 2. 与 James 服务器交互的 Matcher 和 mailet
使您的应用程序调用我的应用程序,第 2 部分: James 应用程序

James 服务器在开发或启动过程中初始化 matcher 和 mailet。James 服务器接收到电子邮件时,它将调用部署好的 matcher 的 match () 方法。若 matcher 返回收件人电子邮件地址列表,James 会将电子邮件地址发送给相应的 mailet,供其进一步处理。

现在您已经熟悉了 James,就可以安装和配置 James 服务器了。

安装和配置 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 所示。

图 3. 带有 James 服务器的日志记录的控制台
使您的应用程序调用我的应用程序,第 2 部分: 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)。

清单 1. 服务器名

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

默认情况下,James 中已创建了一名用户 root,其口令是 root,如下所示:

<account login="root" password="root"/>

localhost IP 127.0.01 将默认添加为 DNS 服务器。对于本教程来说,这已足够,因为您将在本地机器上运行 James。但在生产环境中,您必须要在以下部分中为 DNS 服务器添加一项(参见 清单 2)。

清单 2. DNS 服务器

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

James 设置为使用文件系统作为默认储存库。同样,对于我们的示例应用程序来说,您不必更改这一设置,但 James 确实提供了其他可选数据库或 DBFile 储存库,在生产环境中的电子邮件服务器中,它们可能是更好的选择(参见 清单 3)。

清单 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 () 方法。

清单 4. POMatcher.java 的 init() 方法

/* 
 * This method initializes the list of authorized senders. 
 * 
 * @param config Contains configuration information for this matcher. 
 * 
 * @see org.apache.mailet.Matcher#init(org.apache.mailet.MatcherConfig) 
 */ 
public void init(MatcherConfig config) throws MessagingException { 
   super.init(config); 
   ilog("Entering POMatcher init() " + config); 
 
   //comma-separated list of authorized e-mail addresses 
   String senders = getCondition(); 
 
   // initialize the authorized senders list 
   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()"); 
 
}//end init() method 

在 init() 方法中,您调用了 getCondition(),它返回配置时设置的参数值。这是一个以逗号分隔的经授权的电子邮件地址列表。现在看一下 清单 5 中的 match () 方法。

清单 5. POMatcher.java 中的 match () 方法

/* 
 * Checks if subject is 'Purchase Request' and 
 * Checks if the sender is authorized to request a purchase. 
 * 
 * @see org.apache.mailet.Matcher#match(org.apache.mailet.Mail) 
 */ 
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(); 
 
   // checks if the subject is 'Purchase Request' 
   if (subject != null && subject.indexOf(PURCHASE_REQUEST) 
> 0) { 
 
      // checks if the sender is authorized to request a purchase 
      if (isSenderAuthorized(fromList)) { 
         ilog("Exiting POMatcher match()"); 
         return mail.getRecipients(); 
      } 
   } 
   ilog("Sender is not authorized. "+ fromList); 
   ilog("Exiting POMatcher match()"); 
   return null; 
 
}//end match method 
 
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; 
 
}//end isSenderAuthorized 

isSenderAuthorized () 方法检查发件人的电子邮件地址是否出现在经授权的发件人列表中,若找到匹配项,则返回 true。在 match () 方法中,您根据两条匹配规则检查主题和经过授权的发件人。如果两个条件都满足,则返回整个收件人列表,否则返回 null。

POMatcher 的源文件(.java)可在 $part2.home/src/examples/po/email/matcher 目录下找到。

我们已经准备好了 matcher。接下来介绍 mailet。

mailet

根据您的业务规则,mailet 只有一种处理行为,就是将电子邮件消息转发到其他电子邮件文件夹。mailet 的初始化属性(在部署过程中设置)包括:

forward:这是转发电子邮件的目标地址。

from:这是转发电子邮件的新发件人的电子邮件地址。如果您在转发电子邮件之前未更改发件人,mailet 将进入无穷递归循环处理。

编写定制 mailet 的第一步与 matcher 类似,不同之处在于您必须查看 James mailet API 提供的 GenericMailet 并扩展它。您将调用 mailet 类 POMailet。GenericMailet 有一个默认实现,提供了大多数必需的方法,只有一个抽象方法 service (Mail)。让我们来看看 POMailet.java 的 init () 方法,如 清单 6 所示。

清单 6. POMailet.java 中的 init() 方法

/* 
 * This method read the 'forward' and 'from' e-mail addresses from 
 * the mailet configuration. 
 *  
 * @param config Contains configuration information for this mailet. 
 *  
 * @see org.apache.mailet.Mailet#init(org.apache.mailet.MailetConfig) 
 */ 
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()"); 
 
}// end init() 

在 POMailet.java 的 init () 中,您读取了初始化参数 forward 和 from。这些参数的值是在部署过程中设置的。清单 7 展示了 POMailet 的 service() 方法。

清单 7. POMailet.java 中的 service () 方法

/* 
 * This method processes the e-mail received and forwards the 
 * e-mail to a specific 
 * e-mail address set by 'forward' property. 
 * 
 * @mail Email Message 
 * 
 * @see org.apache.mailet.Mailet#service(org.apache.mailet.Mail) 
 */ 
public void service(Mail mail) throws MessagingException { 
 
   ilog("Entering PO Mailet : " + mail); 
   MimeMessage email = mail.getMessage(); 
 
   ilog("Email is: " + email); 
 
   // move the email to a specific user inbox 
   InternetAddress address = new InternetAddress(forwardEmail); 
   Address[] list = { address }; 
 
   MimeMessage copymsg = new MimeMessage(email); 
 
   // modifying the subject by adding the requestor email address 
   Address[] sentByList = copymsg.getFrom(); 
   String sentBy = sentByList[0].toString(); 
   copymsg.setSubject("Purchase Request by: " + sentBy); 
 
   // modifying the sender email to 'from' 
   InternetAddress newFrom = new InternetAddress(from); 
   copymsg.setFrom(newFrom); 
 
   // sending the email to a new recipient (like forwarding email) 
   Transport.send(copymsg, list); 
   ilog("Exiting PO Mailet"); 
 
}// end service 

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 所示的登录提示。

图 4. 远程管理器服务的登录提示
使您的应用程序调用我的应用程序,第 2 部分: James 应用程序

输入默认用户/口令,如果您未在 config.xml 文件中加以改动,应该是 root / root,输入后按下 enter。创建新用户的命令是 adduser username password。创建全部 5 个用户账户(user1、user2、user3、orders 和 authorized-orders),如 图 5 所示。

图 5. 创建全部 5 个用户
使您的应用程序调用我的应用程序,第 2 部分: James 应用程序

创建好所有用户之后,键入 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)。

清单 8. 用于 mailet 和 matcher 的默认 config.xml 项

<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 所示。

清单 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 所示。

清单 10. 示例 mailet 和 matcher 的项

<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=<initialization 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 的语法。

清单 11. 定义 mailet 的语法

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

在示例中可以看出,您使用了两个初始化参数,其名称分别为 forward 和 from。从示例应用程序的角度来看,经过授权的电子邮件将被转发给 forward 属性中指定的电子邮件地址处。

您已配置好了示例 mailet 和 matcher,那么继续操作,启动服务器。您将了解在服务器启动过程中,POMatcher 和 POMailet 是如何初始化的,如 图 6 所示。

图 6. POMatcher 和 POMailet 是在 James 启动时初始化的
使您的应用程序调用我的应用程序,第 2 部分: James 应用程序

如 图 6 所示,POMatcher 和 POMailet 都是由 James 服务器按此顺序初始化的。

您已成功地部署了示例电子邮件应用程序。现在可以创建一个客户机或测试程序来测试您的应用程序了。

测试应用程序

在这部分中,您将编写一个电子邮件客户机,它可以发送电子邮件并读取用户收件箱中的电子邮件。

电子邮件客户机

客户机程序 EmailClient.java(请参见 清单 12 和 清单 13)提供了发送电子邮件和显示用户收件箱中电子邮件的方法。

您可在 $part2.home/src/examples/po/test 目录下查看整个客户机程序 EmailClient.java,获得此程序的方式请参见 下载 部分。
















清单 12. 电子邮件客户机的 sendEmail 方法

/** 
 * Sends an e-mail message 
 * 
 * @param from Sender's e-mail address 
 * @param to Recipient's e-mail 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() 来实际地将消息传递到收件人的收件箱中。您将使用此方法来发送采购请求电子邮件。

现在来看看如何读取用户收件箱中的电子邮件(参见 清单 13)。您将使用 readInbox 方法来验证您的示例电子邮件应用程序(matcher 和 mailet)是否将来自经授权的发件人的采购请求电子邮件发送到另外一个电子邮件文件夹中。

清单 13. 电子邮件客户机的 readInbox 方法

/** 
 * Reads all the messages in user's inbox. 
 * 
 * @param user user name 
 * @param password password 
 */ 
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(); 
      }//end for 
 
      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 方法的部分内容。

清单 14. EmailClient.java 的 main() 方法的代码片段

public static void main(String a[]) throws Exception { 
 
   //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 = "Purchase Request sent on :" + new Date(cal 
 .getTime().getTime()); 
 
   boolean isSend = false; 
 
//check if there are enough command-line parameters and print usage 
 
   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 
          ); 
 
}//end main 

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 所示。

图 7. 通过运行 runSendEmail.cmd 测试应用程序
使您的应用程序调用我的应用程序,第 2 部分: James 应用程序

如果您看到 图 7 所示的消息,就表示电子邮件已成功发送。在 James 服务器中,这一传入的电子邮件将由 POMatcher-POMailet 处理,如 图 8 所示。

图 8. James 服务器的控制台日志
使您的应用程序调用我的应用程序,第 2 部分: James 应用程序

James 服务器首先调用 POMatchermatch() 方法。由于发件人 user1@localhost.com 是一个经过授权的发件人,POMailet 的 service() 方法将被调用,电子邮件应已转发到 authorized-orders@localhost.com。您一定很疑惑,为什么又一次调用了 POMatchermatch() (参见 图 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. 显示电子邮件
使您的应用程序调用我的应用程序,第 2 部分: James 应用程序

如 图 9 所示,authorized-orders 收件箱中的电子邮件显示了出来。

结束语

祝贺您!第 2 部分的应用程序至此已全部完成,您已学会了使用 Apache James 服务器构建、部署和测试电子邮件应用程序的方法。

在本系列教程的最后一期:第 3 部分中,您将为 Apache James 服务器开发一个 JCA 资源适配器(也可称为连接器),它可将第 2 部分的应用程序(mailet 和 matcher)连接到 第 1 部分 的应用程序(MDB)。

本文示例源代码或素材下载

Tags:应用程序 调用 应用程序

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接