James+Javamail构建邮件服务
本文描述如何使用James搭建具备一定邮件过滤、邮件操作功能的邮件服务器,以及使用Javamail实现对James服务器邮件的收发功能。
1 关于James与Javamail
Apache James(Java Apache Mail Enterprise Server)是Apache组织的子项目之一,完全采用纯Java技术开发,实现了SMTP、POP3与NNTP等多种邮件相关协议。James是一个邮件应用平台,可以通过Mailet扩充其功能,如Mail2SMS、Mail2Fax等。James提供了比较完善的配置方案,尤其是关于邮件内容存储和用户信息存储部分,可以选择在文件、数据库或其他介质中保存。James性能稳定、可配置性强,还是开源项目,所有源代码不存在版权问题,因此,James在项目中的应用日益广泛。
Javamail API是一个用于阅读、编写和发送电子消息的可选包(标准扩展),可以用来建立基于标准的电子邮件客户机,它支持各种因特网邮件协议,包括:SMTP、POP、IMAP、MIME、NNTP、S/MIME及其它协议。
在本文档中,我们使用James 2.3.1+Javamail 1.4.1作为介绍蓝本。
2 James服务器快速入门
我们可以从Apache James 的官方站点下载到我们所需要的James服务器及源码
James服务器(ZIP版)下载地址:
http://apache.mirror.phpchina.com/james/server/binaries/james-binary-2.3.1.zip
James源码下载地址:
http://apache.mirror.phpchina.com/james/server/source/james-2.3.1-src.zip
其他版本的服务器及源码可到官方下载页面下载:
http://james.apache.org/download.cgi
2.1 启动James
第一步:
将james-binary-2.3.1.zip解压到纯英文目录下
第二步:
运行bin目录下的run.bat(在运行之前请确保您的Java环境变量已配置成功,否则将提示运行失败)
显示以下内容表示James服务器已启动成功:
服务器启动成功后,将自动在apps目录下根据james.sar文件生成相应的James发布文件包,我们后面讨论的配置文件config.xml即存在于此发布文件包中。
2.2 用Telnet管理用户
用cmd进入MS-DOS,输入telnet localhost 4555 (注:localhost是邮件服务器名,4555是管理端口,可在\apps\james\SAR-INF\config.xml文件中配置,在此我们使用其默认配置)
然后输入用户名和密码(在配置文件config.xml中,默认为用户名:root密码:root)
出现如下信息说明登陆成功:
登陆成功后,我们就可以开始对用户信息进行增、删、查、改等操作了。
常用的用户信息操作命令参见下表:
? Currently implemented commands: 常用实现命令
? help display this help 显示帮助信息
? listusers display existing accounts 显示现有账户
? countusers display the number of existing accounts 显示现有账户数量
? adduser [username] [password] add a new user 添加一个新用户
? verify [username] verify if specified user exist 核实指定用户是否存在
? deluser [username] delete existing user 删除存在的用户
? setpassword [username] [password] sets a user‘s password 设置用户密码
? setalias [user] [alias] locally forwards all email for ‘user‘ to ‘alias‘ 设置邮箱别名
? showalias [username] shows a user‘s current email alias 显示别名
? unsetalias [user] unsets an alias for ‘user‘ 删除用户别名
? setforwarding [username] [emailaddress] forwards a user‘s email to another email address 转发用户的电子邮件地址到另一个电子邮件地址
? showforwarding [username] shows a user‘s current email forwarding 显示用户的当前邮件转发
? unsetforwarding [username] removes a forward 删除转发
? user [repositoryname] change to another user repository 改变另一个用户库
? shutdown kills the current JVM (convenient when James is run as a daemon) 关闭当前的服务
? quit close connection 关闭连接
我们可以输入命令:adduser chenfengcn 881213来添加一个的邮件用户,则其用户名为:chenfengcn,密码为:881213,邮箱地址为:chenfengcn[email protected](用户名后的域名跟服务器配置的域名相同,在config.xml文件中配置,我们将在后面部分讨论如何配置邮件服务器域名)。这样,我们就完成了一个最简单的邮件服务器的搭建操作。下面就让我们使用Foxmail来测试一下我们的邮箱帐户是否真正可以使用吧。
2.3 使用Foxmail测试邮箱帐户
打开Foxmail,点击“邮箱”->“新建邮箱帐户”
电子邮件地址:[email protected]
密码为:881213
POP3服务器:localhost
SMTP服务器:localhost
其余选项均使用默认即可,新建完成后,Foxmail将发送一测试邮件到我们新建的邮箱里,点击“收取”,即可收取我们邮件帐户里面的邮件了。当收到Foxmail发送的测试邮件,就说明我们的邮件帐户可以正常使用了。
作为程序设计人员,我们当然希望能使用自己写的程序来测试我们的邮件服务器是否搭建成功,而不是Foxmail。下面就让我们用Javamail来实现这一切吧。
3
Javamail快速入门
进行Javamail开发需要用到两个包:mail.jar和activation.jar,在开始Javamail编程之前,请自己将这两个包添加到IDE的Build
path中或将这两个包的路径配置到环境变量中。
3.1 使用Javamail向James的邮箱帐户发送邮件
3.1.1 业务描述
本例将使用Javamail实现邮件的发送功能。发送邮件需要配置邮件服务器属性信息,配置邮件接收地址,使用SMTP认证获得会话(Session),构建邮件体(MimeMessage),发送邮件。具体编码如下:
3.1.2 编码实现
发送邮件需要两个类:一个是SMTP用户身份认证类(James在默认情况下,是需要SMTP身份认证的);另一个就是我们的邮件发送类,为简单起见,我们直接将邮件的相关信息,如:标题、内容、发送者、接收者等信息直接写在类中,运行main()函数即发送。当然,你同样可以为自己的邮件发送系统构造一个邮件发送介面,通过Servlet将相关参数传递至后台进行处理与发送。其主要代码也就是此main()函数中的内容,故不赘述。
SmtpAuth.java用户身份认证代码
Java代码
- package com.newland.javamail.sample1;
- class SmtpAuth extends javax.mail.Authenticator {
- private String user, password;
- public void setUserinfo(String getuser, String getpassword) {
- user = getuser;
- password = getpassword;
- }
- protected javax.mail.PasswordAuthentication getPasswordAuthentication() {
- return new javax.mail.PasswordAuthentication(user, password);
- }
- public String getPassword() {
- return password;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- public String getUser() {
- return user;
- }
- public void setUser(String user) {
- this.user = user;
- }
- }
SendMail.java发送邮件代码
Java代码
- package com.newland.javamail.sample1;
- import java.util.Properties;
- import javax.activation.DataHandler;
- import javax.activation.DataSource;
- import javax.activation.FileDataSource;
- import javax.mail.Message;
- import javax.mail.MessagingException;
- import javax.mail.Session;
- import javax.mail.Transport;
- import javax.mail.URLName;
- import javax.mail.internet.AddressException;
- import javax.mail.internet.InternetAddress;
- import javax.mail.internet.MimeBodyPart;
- import javax.mail.internet.MimeMessage;
- import javax.mail.internet.MimeMultipart;
- public class SendMail {
- public SendMail() {
- // TODO Auto-generated constructor stub
- }
- public static void main(String[] args) {
- // 初始化信息
- String sender = "chenfengcn";
- String password = "881213";
- String smtpServer = "localhost";
- String recipient = "[email protected]";
- String subject = "测试邮件主题";
- String fileAttachment = ""; //附件
- String content = "测试邮件内容";
- // 配置服务器属性
- Properties proper = new Properties();
- proper.put("mail.smtp.host", smtpServer); // smtp服务器
- proper.put("mail.smtp.auth", "true"); // 是否smtp认证
- proper.put("mail.smtp.port", "25"); // 设置smtp端口
- proper.put("mail.transport.protocol", "smtp"); // 发邮件协议
- proper.put("mail.store.protocol", "pop3"); // 收邮件协议
- // 配置邮件接收地址
- InternetAddress[] receiveAddress = new InternetAddress[1];
- try {
- receiveAddress[0] = new InternetAddress(recipient);
- } catch (AddressException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- // smtp认证,获取Session
- SmtpAuth sa = new SmtpAuth();
- sa.setUserinfo(sender, password);
- Session session = Session.getInstance(proper, sa);
- session.setPasswordAuthentication(new URLName(smtpServer), sa
- .getPasswordAuthentication());
- // 构建邮件体
- MimeMessage sendMess = new MimeMessage(session);
- MimeBodyPart mbp = new MimeBodyPart();
- MimeMultipart mmp = new MimeMultipart();
- try {
- // 邮件文本内容
- mbp.setContent(content, "text/plain; charset=GBK");
- mmp.addBodyPart(mbp);
- // 附件处理
- if(fileAttachment!=null&&fileAttachment!=""){
- DataSource source = new FileDataSource(fileAttachment);
- String name = source.getName();
- mbp = new MimeBodyPart();
- mbp.setDataHandler(new DataHandler(source));
- mbp.setFileName(name);
- mmp.addBodyPart(mbp);
- }
- // 邮件整体
- sendMess.setSubject(subject);
- sendMess.setContent(mmp);
- // 发送邮件
- sendMess.setFrom(new InternetAddress(sender));
- sendMess.setRecipients(Message.RecipientType.TO, receiveAddress);
- Transport.send(sendMess);
- System.out.println("发送成功");
- } catch (MessagingException ex) {
- ex.printStackTrace();
- }
- }
- }
值得一提的是,本程序已经实现了带附件邮件的发送功能,如果要发送带附件的邮件,则只需要将附件的路径传到fileAttachment变量中就可以了。邮件发送成功后,程序将在后台打印出“发送成功”,这样我们就完成了邮件发送功能。那么,我们应该如何检验服务器是否确实收到我们发送的测试邮件呢?Javamail可以发送邮件,当然也能接收邮件啦,下面让我们一起使用Javamail编写邮件接收功能来检验吧。
3.2 使用Javamail接收邮件
3.2.1 业务描述
在上一节,我们已经向James的chenfengcn用户发送了一封测试邮件,我们应该如何使用Javamail来收取这封邮件呢?
为读取邮件,必须首先设置服务器属性(Properties),获取一个会话(Session),然后获取并连接邮箱所在的存储器(Store对象),打开该用户的邮箱(Folder),获取所希望阅读的消息,最后关闭目录和连接。
下面的程序实现了接收[email protected]邮箱中所有邮件,并将发送人和主题打印出来。
3.2.2 编码实现
Java代码
- package com.newland.javamail.sample1;
- import java.util.Properties;
- import javax.mail.Folder;
- import javax.mail.Message;
- import javax.mail.MessagingException;
- import javax.mail.NoSuchProviderException;
- import javax.mail.Session;
- import javax.mail.Store;
- public class ReceiveMail {
- public ReceiveMail() {
- }
- public static void main(String[] args) {
- // 初始化主机
- String host = "localhost";
- String username = "chenfengcn";
- String password = "881213";
- // 配置服务器属性
- Properties props = new Properties();
- props.put("mail.smtp.host", "localhost"); // smtp服务器
- props.put("mail.smtp.auth", "true"); // 是否smtp认证
- props.put("mail.smtp.port", "25"); // 设置smtp端口
- props.put("mail.transport.protocol", "smtp"); // 发邮件协议
- props.put("mail.store.protocol", "pop3"); // 收邮件协议
- // 获取会话
- Session session = Session.getDefaultInstance(props, null);
- // 获取Store对象,使用POP3协议,也可能使用IMAP协议
- try {
- Store store = session.getStore("pop3");
- // 连接到邮件服务器
- store.connect(host, username, password);
- // 获取该用户Folder对象,并以只读方式打开
- Folder folder = store.getFolder("inbox");
- folder.open(Folder.READ_ONLY);
- // 检索所有邮件,按需填充
- Message message[] = folder.getMessages();
- for (int i = 0; i < message.length; i++) {
- // 打印出每个邮件的发件人和主题
- System.out.println(i + ":" + message[i].getFrom()[0] + "\t"
- + message[i].getSubject());
- }
- folder.close(false);
- store.close();
- } catch (NoSuchProviderException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (MessagingException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
程序运行成功后,将会把chenfengcn用户的邮件从James服务器中取出,并将此邮箱中所有邮件的发件人、主题打印在后台。若要打印该邮件的内容等信息,则只要将message[i]对象中的邮件内容等信息读取出来就可以了。
注:鉴于邮件的存储结构(将在第五章介绍),读取邮件附件是一个比较复杂的过程,因为邮件的文本内容和附件信息都是保存在BodyPart对象中的,BodyPart用于标识类型的标记不明确,造成对附件的判断较为复杂。对于附件的操作本人将在今后的改进版本中加以介绍。
4 Mailet快速入门
Mailet
API是一个用来创建邮件处理程序的简单的API,它被配置在邮件服务器端执行,分匹配器Matcher和Mailet的接口两种,匹配器根据特定的条件匹配邮件消息,并触发相应的Mailet.
Mailet这个词是跟Servlet相似,功能也相似,他们的共同之处都是在服务器端触发并执行,只是Servlet的Matcher通常是url的pattern,跟Servlet的接口一样,Mailet也有init()方法,service()方法和destroy()方法.即他们都有类似的生命周期. Mailet的简单可编程接口可以用来做一些邮件处理,比如反垃圾邮件,检查邮件病毒以及邮件博客等等,利用移动设备可发送email的功能,可以做到手机通过mail发送信息到邮件服务器交给Mailet处理,形成移动博客的模型.
Mailet的运行需要mailet-2.3.jar和mailet-api-2.3.jar两个包的支持,James本身就有这两个包,可不作修改,但在开发的时候还是需要开发者自己将这两个包导入到工程的Build path中或配置到系统环境变量中。
4.1 用Mailet做一个Hello的例子
4.1.1 业务描述
我们要实现当外部发送给James服务器中名字含hello的邮箱时,服务器在这封邮件的主题前加入“Hello”,并在服务器后台输出“Received a piece of Email”。如前所述,Mailet包括匹配器Matcher和Mailet两种接口,现在就让我们用Mailet API实现这两个接口吧。
4.1.2 编码实现
匹配器BizMatcher.java
Java代码
- package com.newland.james.mailet.sample1;
- import org.apache.mailet.GenericRecipientMatcher;
- import org.apache.mailet.MailAddress;
- public class BizMatcher extends GenericRecipientMatcher {
- public boolean matchRecipient(MailAddress recipient) {
- // 邮件地址必须包含hello的
- if (recipient.getUser().indexOf("hello") != -1) {
- return true;
- }
- return false;
- }
- }
BizMaillet.java
Java代码
- package com.newland.james.mailet.sample1;
- import java.io.IOException;
- import javax.mail.MessagingException;
- import javax.mail.internet.MimeMessage;
- import org.apache.mailet.GenericMailet;
- import org.apache.mailet.Mail;
- public class BizMaillet extends GenericMailet {
- public void init() throws MessagingException {
- }
- public void service(Mail mail) throws MessagingException {
- MimeMessage mmp;
- mmp = (MimeMessage) mail.getMessage();
- mmp.setSubject("Hello "+mmp.getSubject());
- System.out.println("Received a piece of Email");
- }
- }
4.1.3 配置部署
Mailet跟Servlet一样,是服务器端程序,是不能直接在客户端运行的,必须要部署到服务器端方可生效。部署具体步骤如下:
1、 将我们编写的Matcher和Mailet打包成jar文件;
2、在\james-2.3.1\apps\james\SAR-INF目录下新建一个lib文件夹;
3、 将打包好的jar文件复制到刚刚新建的lib文件夹下;
4、 打开config.xml配置文件,找到以下这段代码:
Xml代码
- <</span>mailetpackages>
- <</span>mailetpackage>org.apache.james.transport.mailets</</span>mailetpackage>
- <</span>mailetpackage>org.apache.james.transport.mailets.smime</</span>mailetpackage></</span>mailetpackages>
- <</span>matcherpackages>
- <</span>matcherpackage>org.apache.james.transport.matchers</</span>matcherpackage>
- <</span>matcherpackage>org.apache.james.transport.matchers.smime</</span>matcherpackage></</span>matcherpackages>
前半部分是用于配置Mailet包所在位置,后半部分是用于配置Matcher包所在位置,我们把我们刚编写的Mailet和Matcher所在位置配置进去就可以了。配置后的结果如下:
Xml代码
- <</span>mailetpackages>
- <</span>mailetpackage>com.newland.javamail.sample1</</b>mailetpackage>
- <</span>mailetpackage>org.apache.james.transport.mailets</</span>mailetpackage>
- <</span>mailetpackage>org.apache.james.transport.mailets.smime</</span>mailetpackage>
- </</span>mailetpackages>
- <</span>matcherpackages>
- <</span>matcherpackage>com.newland.javamail.sample1</</b>matcherpackage>
- <</span>matcherpackage>org.apache.james.transport.matchers</</span>matcherpackage>
- <</span>matcherpackage>org.apache.james.transport.matchers.smime</</span>matcherpackage>
- </</span>matcherpackages>
这样就完成了包的配置。我们都知道,Mailet的工作过程是:首先由Matcher来匹配所接收到的邮件,然后提交给相应的Mailet处理,但是哪个匹配器对应哪个Mailet呢?我们还需要配置Mailet的对应关系。同样在config.xml中找到下面的代码:
Xml代码
- <</span>mailet match="All" class="PostmasterAlias"/>
在这段代码下面加入我们自己的Mailet:
Xml代码
- <</span>mailet match="All" class="PostmasterAlias"/>
Xml代码
- <</span>mailet match="BizMatcher" class="BizMaillet"/>
这样就完成了我们自定义的Mailet的配置部署工作了。重启James服务器,则此Mailet即可生效。
4.1.4 测试Mailet
前面我们已经完成了Mailet的编码和部署工作,现在就让我们来测试一下我们的Mailet是否生效吧。首先,需要在James服务器上新建一个名称含Hello的用户。前面已介绍过新建用户的方法了,在此就不重复叙述了。
使用adduser helloworld 881213命令新建一个helloworld用户。
使用第三章所谈及的“使用Javamail向James的邮箱帐户发送邮件”来向[email protected]发送一封邮件(当然,你同样可以使用Foxmail或Outlook向此地址发送邮件),邮件发送成功后,James服务器后台将输出“Receive a piece of email”。运行效果如下图所示: