本文是探讨Java Apache Mail Enterprise Server(通常称为James)的系列文章中的第二篇。 在本系列的第一部分中,我们研究了James基础结构和功能的基础,并逐步完成了在测试系统上安装James的过程。 在本文中,我们将在此概述的基础上,通过开发支持将用户帐户标记为不可用的应用程序,直接在James基础结构中应用思想。 要使用我们的应用程序,用户将特定类型的消息发送到指定的邮件服务器地址; 该消息将用作对任何传入邮件的自动响应,直到用户发送后续消息,表明他(她)再次可用。 通常使用类似的客户端机制来提醒邮件发件人用户不在-例如,在休假期间。 但是,除非用户保持客户端软件处于活动状态,否则此类功能不是很有用。 通过使用服务器端解决方案,您可以在实际休假期间使用备用客户端软件登录,并随时更改消息。
应用设计
在设计我们的应用程序之前,我们应该明确要求。 以下几点将作为示例的基础:
- 用户可以将电子邮件发送到unavailable @ emailserver,以将其帐户设置为不可用模式。 已存储已发送到unavailable @ emailserver的消息以供将来使用。 如果已经为用户存储了一条不可用的消息,则该消息将被新的消息覆盖。 用户应该收到所有已发生的通知。
- 用户可以将电子邮件发送到available @ emailserver来取消所有不可用的消息。 发送到available @ emailserver的消息将被丢弃,并删除存储的不可用消息。 用户应收到状态已更改为可用的通知。
- 每当已为其存储了不可用消息的用户收到电子邮件时,都应向发送方发送已存储消息的副本,指示该用户不可用。 触发此过程的原始传入电子邮件应得到正常处理。
我已经在三个语句中编写了要求并不是巧合。 每个都直接映射到在特定条件下采取的一组操作。 每个条件都应由匹配器识别。 (有关匹配器和邮件的更多信息,请参阅本系列的第一部分 。)例如,发送到特定地址的电子邮件可以与内置的RecipientIs
匹配器匹配。 不幸的是,当我研究RecipientIs
的源代码时,我注意到可以使用任意数量的收件人,并且认为任何匹配都是合适的。 在大多数情况下,这可能很好,但是我们的应用程序需要确保只涉及一个指定的收件人,因此我们将开发一个简单的匹配器来完成此任务。 我们将开发的MatchSingleRecipient
类还将很好地介绍如何展示Matcher API。
识别已经存储了不可用消息准备处理的收件人稍微复杂一些。 为此,我们将开发一个MatchUnavailableUser
匹配器。 为了提高效率,我们将在测试不可用目录中是否存在文件之前检查用户是否为本地用户。 否则,该过程应该相当简单。 我们将首先开发两个匹配器类,然后继续进行邮件处理。
我们将大量使用不可用的目录。 实际上,需要执行多项操作,包括检测消息的存在,保存该消息,读取它并删除它。 因为每个函数都需要知道消息存储目录的位置以及有关用户的信息,所以我们将创建一个单独的类, MatchUnavailableUser
匹配程序可以使用该类,并在我们的应用程序中使用它mailet实施设计。
我们将实现的匹配器之间几乎没有共性。 但是处理我们规定的要求的邮件(每个要求只有一个邮件)都需要类似的配置信息并访问不可用的目录,因此我们将使用每个邮件都可以扩展的基类。 基类也是放置多个子类之一使用的实用程序方法以及基本初始化代码的好地方。 我们将基类称为UnavailableUserBase
。
-三个应用功能将由我们的mailet类来处理UnavailableMessageSave
, UnavailableMessageDrop
和UnavailableMessageSend
。 前两个具有非常相似的功能,并且在单个传入收件人(不可用或可用地址)上运行,因此它们共享一些代码,这些代码最终将在超类中结束。 UnavailableMessageSend
电子邮件组最复杂,主要是因为它必须处理多个收件人,并为每个不可用的收件人将消息发送给发件人。 幸运的是,Matcher和Mailet API易于使用。
写比赛者
基类GenericMatcher
简化了用James编写匹配器的过程,大多数实现将扩展该类。 我们将为两个匹配者都这样做。 首先,我们要在init()
方法中检索配置信息,并在match()
方法中进行处理。 从技术上讲,我们应该实现getMatcherInfo()
方法来报告供应商,版本等信息,但是为了使本示例的代码更加简洁,我省略了所有内容。
在James的config.xml文件中配置匹配器时(在本系列的第一部分中我们详细讨论了该文件),您可以将其指定为XML属性。 匹配器类名称后跟等号和其他文字; 可以使用getCondition()
方法检索getCondition()
。 为了利用James中提供的MailAddress
比较代码,我们使用从getCondition()
方法检索的值创建一个实例,并将其存储在实例变量中。 不用说, 清单1中显示的MatchSingleRecipient
期望将一个有效的本地电子邮件地址作为唯一的参数。
match()
方法获取正在处理电子邮件的收件人列表。 Mail
对象提供了几种有趣的方法。 最常用的方法是getRecipients()
,它返回一个包含MailAddress
实例的Java Collection
对象。 要查看是否存在匹配项,我们首先通过检查Collection
的大小来进行测试,以确保仅涉及一个收件人,然后确保Collection
(长度为1)包含我们在配置文件中指定的地址。
要编译这些类的代码,请确保已下载本系列第一部分中概述的所有组件。 您应该在类路径上具有JavaMail(mail.jar)和JavaBeans激活框架(activation.jar)JAR文件,以及james.jar文件。 如果找不到james.jar,可以从James-2.1.2 / apps目录中的James.SAR文件中提取它。
清单1. MatchSingleRecipient:标识发送到单个特定地址的电子邮件
package com.claudeduguay.mailets;
import java.util.*;
import javax.mail.*;
import org.apache.mailet.*;
public class MatchSingleRecipient
extends GenericMatcher
{
protected MailAddress addressToMatch;
public void init(MatcherConfig config)
throws MessagingException
{
super.init(config);
addressToMatch = new MailAddress(getCondition());
}
public Collection match(Mail mail)
throws MessagingException
{
Collection recipients = mail.getRecipients();
if (recipients.size() == 1 &&
recipients.contains(addressToMatch))
{
return recipients;
}
return null;
}
}
清单2中所示的MatchUnavailableUser
类使用UnavailableStore
类,我们将对其进行暂时介绍。 为了使其正常工作,我们期望通过getCondition()
方法检索用于存储不可用消息的目录。
清单2. MatchUnavailableUser:标识不可用的用户
package com.claudeduguay.mailets;
import java.util.*;
import javax.mail.*;
import org.apache.mailet.*;
public class MatchUnavailableUser
extends GenericMatcher
{
protected UnavailableStore store;
public void init(MatcherConfig config)
throws MessagingException
{
super.init(config);
String folder = getCondition();
store = new UnavailableStore(folder);
}
protected boolean isLocalAddress(MailAddress address)
{
String host = address.getHost();
MailetContext context = getMailetContext();
return context.isLocalServer(host);
}
protected boolean isUserUnavailable(MailAddress address)
{
return store.userMessageExists(address);
}
public Collection match(Mail mail)
throws MessagingException
{
Collection matches = new Vector();
Collection recipients = mail.getRecipients();
Iterator iterator = recipients.iterator();
while (iterator.hasNext())
{
MailAddress address = (MailAddress)iterator.next();
String user = address.getUser();
if (isLocalAddress(address) &&
isUserUnavailable(address))
{
matches.add(address);
}
}
return matches;
}
}
为了便于实现match()
方法,我实现了两个实用程序方法: isLocalAddress()
,它利用MailetContext
的isLocalServer()
方法确定用户是否是本地用户,以及isUserUnavailable()
,它使用UnavailableStore
的userMessageExists()
方法来确定用户是否指定了不可用消息。
匹配项将创建一个Collection
对象(一个Vector
)来收集应该由信箱处理的收件人。 然后,我们遍历收件人,并检查每个收件人,看看是否涉及本地地址,以及是否为该用户指定了不可用的消息。 如果两个条件都成立,则将收件人添加到列表中,并在完成后返回列表。
这就是实现我们的匹配器的全部内容。 让我们快速看一下清单3中的UnavailableStore
代码,该代码由MatchUnavailableUser
以及我们的每个mailet实现使用。 这个想法是在单个类中收集所有与访问不可用消息存储区有关的方法。 因此,如有必要,可以开发更复杂的实现。 该版本有意简化,仅将所有用户消息存储在单个目录中。 如果要在服务器上管理数千个用户,这可能是个问题,但对于大多数应用程序来说已经足够了。
清单3. UnavailableStore:访问不可用的消息存储
package com.claudeduguay.mailets;
import java.io.*;
import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
import org.apache.mailet.*;
public class UnavailableStore
{
protected File home;
public UnavailableStore(String folder)
{
home = new File(folder);
if (!home.exists())
{
home.mkdirs();
}
}
public File getUserMessageFile(MailAddress address)
{
String user = address.getUser();
File file = new File(home, user + ".msg");
return file;
}
public boolean userMessageExists(MailAddress address)
{
return getUserMessageFile(address).exists();
}
public void deleteMessage(MailAddress address)
{
getUserMessageFile(address).delete();
}
public void storeMessage(MailAddress address, MimeMessage msg)
throws MessagingException, IOException
{
File file = getUserMessageFile(address);
FileOutputStream out = new FileOutputStream(file);
msg.writeTo(out);
out.close();
}
public MimeMessage getMessage(MailAddress address)
throws MessagingException, IOException
{
File file = getUserMessageFile(address);
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props);
FileInputStream in = new FileInputStream(file);
MimeMessage msg = new MimeMessage(session, in);
in.close();
return msg;
}
}
UnavailableStore
的关键配置参数是在其中查找文件的目录。 我们通过构造函数传递该值,并确保如果目录不存在则创建该目录。 您必须确保您的配置(在config.xml中)将所有匹配器和邮件都指向同一目录,此应用程序才能正常工作。
其余方法主要用于文件访问。 getUserMessageFile()
方法提供了一种解决给定用户的文件名的常用方法。 在每个方法中,我们都使用James MailAddress
对象作为参数,因为使用匹配器和mailet代码中的对象更容易。 文件名的格式为[unavailabledirectory] / [username] .msg。
MimeMessage
对象是JavaMail API的一部分。 James在自己的基础架构中利用了这些对象,因此我们可以使用相同的API。 storeMessage()
和getMessage()
方法利用MimeMessage
的功能来读取和写入大多数邮件服务器使用的标准文本格式的消息。 以序列化对象的形式存储消息本来就很容易(并且可能更有效),但是当消息以文本形式可读时,调试起来就更容易了。
写信
现在,我们有了一种识别需要处理的电子邮件的机制(匹配器),我们需要以mailet实现的形式将处理元素本身放在一起。 我们将利用UnavailableStore
来存储和访问我们的不可用消息。
在继续我们的mailet实现之前,让我们快速看一下将用于保持一些常见行为的基类。 mailet配置参数以XML标记的形式在config.xml文件中提供。 可以使用我们扩展的GenericMailet
类中的getInitParameter()
方法来检索这些标签。 清单4中概述的UnavailableMessageBase
被声明为抽象,因为它没有直接实现service方法。
init()
方法类似于MatchUnavailableUser
类中的init()
方法,因为它存储对UnavailableStore
对象的引用。 UnavailableMessageBase
大多数代码都采用实用程序方法的形式。 我已经开发方法之间转换MailAddress
和数组Address
对象,并率先拿到MailAddress
从Collection
的地址,以用于包装单的方法沿着MailAddress
作为Collection
和创建电子邮件中的几个方法消息。
由于消息可能包含任意的多部分内容,因此最后一个createMessage()
方法使用Object
和String
参数分别指定内容和MIME类型。 这种简单的情况使用String
作为内容,而MIME类型为"text/plain"
,该情况通过另一种具有简化签名的方法来抽象化。 我们使用更一般的情况来发送不可用的消息,因为存储的消息可能具有任意复杂性。
清单4. UnavailableMessageBase:用于保持常用行为可用的基类
package com.claudeduguay.mailets;
import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
import org.apache.mailet.*;
public abstract class UnavailableMessageBase
extends GenericMailet
{
protected UnavailableStore store;
public void init(MailetConfig config)
throws MessagingException
{
super.init(config);
MailetContext context = config.getMailetContext();
String folder = getInitParameter("folder");
store = new UnavailableStore(folder);
}
protected Address[] toAddressArray(MailAddress address)
throws AddressException
{
InternetAddress[] array = new InternetAddress[1];
array[0] = address.toInternetAddress();
return array;
}
protected MailAddress getFirstAddress(Collection list)
{
Iterator iterator = list.iterator();
return (MailAddress)iterator.next();
}
protected Collection addressAsCollection(MailAddress address)
{
Collection list = new Vector();
list.add(address);
return list;
}
protected MimeMessage createMessage(
MailAddress from, MailAddress to,
String subject, String text)
throws MessagingException
{
return createMessage(from, to, subject, text, "text/plain");
}
protected MimeMessage createMessage(
MailAddress from, MailAddress to,
String subject, Object content, String type)
throws MessagingException
{
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props);
MimeMessage msg = new MimeMessage(session);
msg.addFrom(toAddressArray(from));
msg.addRecipients(Message.RecipientType.TO, toAddressArray(to));
msg.setSubject(subject);
msg.setContent(content, type);
return msg;
}
}
现在,我们可以直接处理邮件了。 我们需要做的第一件事是将发送的邮件保存到不可用的地址。 我们可以配置MatchSingleRecipient
匹配器,以将与不可用的接收者匹配的消息发送到UnavailableMessageSave
邮件MatchSingleRecipient
,如清单5所示。 为了最大程度地提高可配置性,我已经可以指定配置消息的主题和内容字符串。 这些值存储在init()
方法中的局部变量中。
服务方法完成所有工作。 因为我们扩展了基类UnavailableMessageBase
,所以我们可以访问UnavailableStore
的实例。 我们的目标是确定谁发送了消息,将MimeMessage
存储并放置在正确的目录中,然后向发送者发送确认电子邮件。 我们可以假设发送者是有效的; 如果不是,则匹配程序将不允许邮件通过。
注意我们处理异常的方式。 log()
方法用于确保报告问题,但是如果您没有看到预期的行为,则必须检查日志。 另外,请注意,我们将电子邮件的状态设置为GHOST
。 这将停止对已发送电子邮件的处理,因为我们将自己存储它,并且电子邮件服务器不需要任何其他操作。
清单5. UnavailableMessageSave:保存发送到unavailable @ emailserver的消息
package com.claudeduguay.mailets;
import java.io.*;
import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
import org.apache.mailet.*;
public class UnavailableMessageSave
extends UnavailableMessageBase
{
protected String subject;
protected String content;
public void init(MailetConfig config)
throws MessagingException
{
super.init(config);
subject = getInitParameter("subject");
content = getInitParameter("content");
}
public void service(Mail mail)
throws MessagingException
{
MailAddress sender = mail.getSender();
Collection recipients = mail.getRecipients();
MailAddress address = getFirstAddress(recipients);
MailetContext context = getMailetContext();
try
{
MimeMessage msg = (MimeMessage)mail.getMessage();
store.storeMessage(sender, msg);
mail.setState(Mail.GHOST);
}
catch (IOException e)
{
log("Unable to store user message", e);
}
try
{
MimeMessage msg = createMessage(address, sender, subject, content);
Collection target = addressAsCollection(sender);
context.sendMail(address, target, msg);
}
catch (MessagingException e)
{
log("Unable to send confirmation message", e);
}
}
}
下一个要研究的类是清单6中所示的UnavailableMessageDrop
mailet。该mailet与UnavailableMessageSave
非常相似,但是它在发送确认之前先删除消息,而不是保存它,因此所需的代码略少。 在这两种情况下,我们都利用UnavailableStore
类来访问我们的不可用消息存储。
清单6. UnavailableMessageDrop:删除不可用消息
package com.claudeduguay.mailets;
import java.io.*;
import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
import org.apache.mailet.*;
public class UnavailableMessageDrop
extends UnavailableMessageBase
{
protected String subject;
protected String content;
public void init(MailetConfig config)
throws MessagingException
{
super.init(config);
subject = getInitParameter("subject");
content = getInitParameter("content");
}
public void service(Mail mail)
throws MessagingException
{
MailAddress sender = mail.getSender();
Collection recipients = mail.getRecipients();
MailAddress address = getFirstAddress(recipients);
MailetContext context = getMailetContext();
store.deleteMessage(sender);
mail.setState(Mail.GHOST);
try
{
MimeMessage msg = createMessage(address, sender, subject, content);
Collection target = addressAsCollection(sender);
context.sendMail(address, target, msg);
}
catch (MessagingException e)
{
log("Unable to send confirmation message", e);
}
}
}
清单7中所示的UnavailableMessageSend
mailet是最复杂的,但是即使在这里,您也会看到Mailet API使这种工作相对简单。 init()
方法支持在处理给定用户的传入消息时应忽略的地址列表。 这是避免将确认消息发送给用户时可能发生的循环的必要条件。 在我们的配置中,我们设置了两个地址-一个用于可用状态,一个用于不可用状态-被忽略。 我已将这些设置配置为可配置的,以防管理员选择使用其他地址。 地址列表是用分号分隔的字符串标记的。
我已经包装了UnavailableStore
的getMessage()
方法来记录检索消息的任何问题,并添加了isIgnorable()
方法来检查MailAddress
是否包含在忽略列表中。 大部分工作都是通过service方法完成的。
清单7. UnavailableMessageSend:将不可用消息发送给发件人
package com.claudeduguay.mailets;
import java.io.*;
import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
import org.apache.mailet.*;
public class UnavailableMessageSend
extends UnavailableMessageBase
{
protected MailAddress[] ignore;
protected MimeMessage getMessage(MailAddress address)
{
try
{
return store.getMessage(address);
}
catch (Exception e)
{
log("Unable to read stored e-mail message: ", e);
}
return null;
}
public void init(MailetConfig config)
throws MessagingException
{
super.init(config);
String skip = getInitParameter("skip");
StringTokenizer tokenizer = new StringTokenizer(skip, ";", false);
int count = tokenizer.countTokens();
ignore = new MailAddress[count];
for (int i = 0; i < count; i++)
{
String address = tokenizer.nextToken().trim();
ignore[i] = new MailAddress(address);
}
}
protected boolean isIgnorable(MailAddress address)
{
for (int i = 0; i < ignore.length; i++)
{
if (address.equals(ignore[i]))
{
return true;
}
}
return false;
}
public void service(Mail mail)
throws MessagingException
{
MailAddress sender = mail.getSender();
MailetContext context = getMailetContext();
Collection recipients = mail.getRecipients();
Iterator iterator = recipients.iterator();
while (iterator.hasNext())
{
MailAddress address = (MailAddress)iterator.next();
// If the recipient is unavailable, send the message.
if (!isIgnorable(sender) &&
store.userMessageExists(address))
{
Collection target = addressAsCollection(sender);
MimeMessage msg = getMessage(address);
if (msg != null)
{
try
{
msg = createMessage(address, sender, msg.getSubject(),
msg.getContent(), msg.getContentType());
// Send e-mail from unavailable recipient, to sender
context.sendMail(address, target, msg);
}
catch (IOException e)
{
log("Unable to construct new message: ", e);
}
}
}
}
}
}
UnavailableMessageSend
服务方法需要检查所有收件人是否与存储了不可用消息的本地用户匹配。 我们使用两种方法来测试每个收件人。 isIgnorable()
方法用于确保地址不可忽略, UnavailableStore
类中的userMessageExists()
方法用于检查该用户是否是存储了不可用消息的用户。 如果满足这些条件,我们将检索该收件人的不可用消息,使用存储的主题和内容创建新消息,并将其发送给发送初始消息的用户。
构建和部署
在这个项目中构建类非常简单,但是部署它们则更加复杂。 实际上,只要JavaMail(mail.jar),JavaBeans激活框架(activation.jar-JavaMail API要求)和James包(james.jar)位于类路径上,我们就可以轻松构建这些类。 。 要使用James JAR文件,您必须将其从james.apps目录中的james.sar文件中删除。 (有关下载所有这些软件包的链接,请参阅参考资料 。您可以在本系列的第一部分中找到有关安装的更多信息。)
SAR文件(服务器应用程序资源)是Phoenix的概念。 您会回想起James是在Phoenix服务器基础结构中运行的,该基础结构是Avalon项目的一部分。 (请参阅相关主题以获得更多关于阿瓦隆)凤凰提供了Java平台的项目一个共同的服务器架构。 SAR文件是具有指定结构的JAR文件(依次为zip文件)。 您需要的james.jar位于SAR归档文件的SAR-INF / lib子目录中。 您可以使用任何合适的实用程序(例如WinZIP或UNIX unzip
实用程序)来获得它。
不幸的是,将邮件添加到James包的唯一可行方法是重现SAR文件。 这很尴尬,而且比应有的要复杂得多,但是James开发团队意识到这种不便之处,并打算将其改进。 但是,现在,我们必须将整个james.sar文件解压缩到某个位置,以便可以使用它,将它们放入SAR-INF / lib目录(也以JAR形式)中添加类,并更改James配置文件,以便正确识别和使用我们的代码。 然后,我们需要重新打包james.sar文件。 为避免冲突,我们将原始james.jar文件移动到一个安全的地方,并调用我们的james-plus.jar。
要添加邮件,我们需要编辑SAR-INF目录中的config.xml配置文件。 我们将两种类型的标签添加到配置文件中。 第一组仅告诉James查找匹配器和mailet类的软件包。 在这两种情况下,我们都扩展已经存在的标记以引用org.apache类。 清单8展示了我们的添加:
清单8. Matcher和mailet包声明
<mailetpackages>
<mailetpackage>org.apache.james.transport.mailets</mailetpackage>
<mailetpackage>com.claudeduguay.mailets</mailetpackage>
</mailetpackages>
<matcherpackages>
<matcherpackage>org.apache.james.transport.matchers</matcherpackage>
<matcherpackage>com.claudeduguay.mailets</matcherpackage>
</matcherpackages>
定义了类包之后,就可以找到匹配器和mailet,并且我们可以配置应用程序,如清单9所示:
清单9. Matcher和mailet流程声明
<mailet
match="MatchSingleRecipient=unavailable@localhost"
class="UnavailableMessageSave">
<folder>d:/james/james-2.1.2/unavailable</folder>
<subject>You have been marked as UNAVAILABLE</subject>
<content>Send a message to available@localhost to reset.</content>
</mailet>
<mailet
match="MatchSingleRecipient=available@localhost"
class="UnavailableMessageDrop">
<folder>d:/james/james-2.1.2/unavailable</folder>
<subject>You have been marked as AVAILABLE</subject>
<content>Send a message to unavailable@localhost to send the
message to users when you are unavailable.</content>
</mailet>
<mailet
match="MatchUnavailableUser=d:/james/james-2.1.2/unavailable"
class="UnavailableMessageSend">
<folder>d:/james/james-2.1.2/unavailable</folder>
<skip>available@localhost;unavailable@localhost</skip>
</mailet>
如果您从未使用过Ant,则可能会发现清单10中的构建脚本不熟悉。 Ant是来自Apache组的构建工具,现已广泛用于自动化构建。 我们将使用该工具来自动提取原始SAR文件,并编译,重建和部署我们的应用程序。 如果您不熟悉Ant,可以自己欠下更多的知识。 它不仅功能强大,而且完全可移植,并且在构建自动化方面有了巨大的飞跃。 要自定义此构建文件,您可以更改属性标签中指定的目录。 (“ 相关主题”部分包含有关Ant的更多信息。)
清单10. Ant构建脚本
<project name="unavailable" default="packageSAR">
<property name="project.dir" value="d:/james" />
<property name="javamail.dir" value="javamail-1.3" />
<property name="james.dir" value="james-2.1.2" />
<property name="jaf.dir" value="jaf-1.0.2" />
<property name="temp.dir" value="temp" />
<target name="init" >
<echo>Project dir: ${project.dir}</echo>
<echo>James dir: ${james.dir}</echo>
<echo>JavaMail dir: ${javamail.dir}</echo>
<echo>JAF dir: ${jaf.dir}</echo>
</target>
<target name="moveOriginalSAR" depends="init" >
<move todir="${project.dir}">
<fileset dir="${project.dir}/${james.dir}/apps" >
<include name="james.sar" />
</fileset>
</move>
<delete includeEmptyDirs="true" failonerror="false" >
<fileset dir="${project.dir}/${james.dir}/apps/james" />
</delete>
</target>
<target name="unzipSAR" depends="moveOriginalSAR" >
<delete includeEmptyDirs="true" failonerror="false" >
<fileset dir="${project.dir}/${temp.dir}" />
</delete>
<unjar
src="${project.dir}/james.sar"
dest="${project.dir}/${temp.dir}" />
</target>
<target name="compile" depends="unzipSAR" >
<echo>Compiling</echo>
<javac srcdir="." destdir=".">
<classpath>
<pathelement path="${project.dir}/${temp.dir}/SAR-INF/lib" />
</classpath>
</javac>
<echo>Compiled</echo>
</target>
<target name="packageJAR" depends="compile" >
<echo>Packaging</echo>
<copy file="config.xml" todir="${project.dir}/${temp.dir}/SAR-INF" />
<delete file="${project.dir}/${temp.dir}/SAR-INF/lib/unavailable.jar" />
<jar jarfile="${project.dir}/${temp.dir}/SAR-INF/lib/unavailable.jar"
basedir="." includes="com/claudeduguay/**/*.class" />
<echo>Packaged</echo>
</target>
<target name="packageSAR" depends="packageJAR" >
<delete includeEmptyDirs="true" failonerror="false" >
<fileset dir="${project.dir}/${james.dir}/apps/james-plus" />
</delete>
<jar jarfile="${project.dir}/${james.dir}/apps/james-plus.sar"
basedir="${project.dir}/${temp.dir}" includes="**/*" />
</target>
</project>
构建完成后,我们可以使用james / bin目录中的运行脚本来启动James服务器。 如果一切顺利,您将看到与本系列第1部分相同的输出,表明James已启动并运行。
在测试应用程序之前,我们需要设置红色,绿色和蓝色用户。 如果尚未执行此操作,则可以按照本系列第一篇文章中的说明进行操作。 设置用户之后,我们就可以继续进行了。
模拟用户
一旦我们所有的代码都在服务器上运行,我们需要应用一组测试,以确认我们的应用程序运行正常。 该JamesApplicationTest
类,所示清单11 ,类似于JamesConfigTest
在本系列的第一篇文章中开发的一类。 它使用我们随后开发的MailClient
类来发送和检索电子邮件。
我们如何测试我们的匹配器和邮件? 我们首先清除红色,绿色和蓝色用户的所有消息。 然后,我们从红色和绿色向蓝色用户发送几条消息,然后让蓝色发送不可用的消息。 下次蓝色检查消息时,他应该看到由红色和绿色发送的原始消息,以及来自不可用用户的确认消息,表明他的消息已正确保存,并且他现在处于不可用状态。 蓝色用户可以继续正常运行,发送和接收消息,但是在此状态下向蓝色发送消息的任何用户将收到蓝色存储的不可用消息。
我们可以通过向蓝色发送新消息来测试当用户不可用时发生的情况。 我们使用绿色用户来执行此操作,然后检查绿色的收件箱。 绿色用户将收到来自蓝色的不可用消息,该消息反映了蓝色发送到不可用地址的已存储消息的内容。 最后,blue发送一条可用消息并检查其电子邮件,以确保确认消息是通过可用地址发送的。
清单11. JamesApplicationTest:不断完善我们的匹配器和信箱
public class JamesApplicationTest
{
public static void main(String[] args)
throws Exception
{
// CREATE CLIENT INSTANCES
MailClient redClient = new MailClient("red", "localhost");
MailClient greenClient = new MailClient("green", "localhost");
MailClient blueClient = new MailClient("blue", "localhost");
// CLEAR EVERYBODY'S INBOX
redClient.checkInbox(MailClient.CLEAR_MESSAGES);
greenClient.checkInbox(MailClient.CLEAR_MESSAGES);
blueClient.checkInbox(MailClient.CLEAR_MESSAGES);
Thread.sleep(500); // Let the server catch up
// SEND A COUPLE OF MESSAGES TO BLUE (FROM RED AND GREEN)
redClient.sendMessage(
"blue@localhost",
"Testing blue from red",
"This is a test message");
greenClient.sendMessage(
"blue@localhost",
"Testing blue from green",
"This is a test message");
// BLUE SENDS UNAVAILABLE MESSAGE
blueClient.sendMessage(
"unavailable@localhost",
"On Vacation",
"I am on vacation at the moment. " +
"I will answer your mail as soon as I get back.");
Thread.sleep(500); // Let the server catch up
// LIST BLUE MESSAGES (RED, GREEN & UNAVAILABLE CONFIRMATION)
blueClient.checkInbox(MailClient.SHOW_AND_CLEAR);
// GREEN SENDS A NORMAL MESSAGE TO BLUE
greenClient.sendMessage(
"blue@localhost",
"Testing blue from green",
"This is a test message");
Thread.sleep(500); // Let the server catch up
// GREEN CHECKS MESSAGES (SHOULD SEE BLUE UNAVAILABLE MESSAGES)
greenClient.checkInbox(MailClient.SHOW_AND_CLEAR);
// BLUE SENDS MESSAGES TO BECOME AVAILABLE
blueClient.sendMessage(
"available@localhost",
"Ignored subject",
"Ignored content");
Thread.sleep(500); // Let the server catch up
// BLUE CHECKS MAIL, SHOULD SEE MESSAGE FROM GREEN AND AVAILABLE MSG
blueClient.checkInbox(MailClient.SHOW_AND_CLEAR);
}
}
JamesApplicationTest的输出应如下所示:
清单12. JamesApplicationTest输出
Clear INBOX for red@localhost
Clear INBOX for green@localhost
Clear INBOX for blue@localhost
SENDING message from red@localhost to blue@localhost
SENDING message from green@localhost to blue@localhost
SENDING message from blue@localhost to unavailable@localhost
Show and Clear INBOX for blue@localhost
From: green@localhost
Subject: Testing blue from green
Content: This is a test message
From: red@localhost
Subject: Testing blue from red
Content: This is a test message
SENDING message from green@localhost to blue@localhost
Show and Clear INBOX for green@localhost
From: blue@localhost
Subject: On Vacation
Content: I am on vacation at the moment. I will answer your mail
as soon as I get back.
SENDING message from blue@localhost to available@localhost
Show and Clear INBOX for blue@localhost
From: available@localhost
Subject: You have been marked as AVAILABLE
Content: Send a message to unavailable@localhost to send the
message to users when you are unavailable.
From: green@localhost
Subject: Testing blue from green
Content: This is a test message
From: unavailable@localhost
Subject: You have been marked as UNAVAILABLE
Content: Send a message to available@localhost to reset.
摘要
从本文提供的代码中可以看到,与James的工作非常简单。 目前唯一的麻烦是James团队正在努力的部署过程。 鉴于电子邮件实际上是Internet上最常用的应用程序-并且肯定比Web服务更流行-考虑一下James基础结构可访问的可能性很有趣。 这种方法势必会解决重要问题,并且仅受您的想象力限制。
翻译自: https://www.ibm.com/developerworks/java/library/j-james2/index.html