1 电子邮件是如何工作的?
原则上,电子邮件很简单.使用邮件用户代理(MUA)。MUA有多种形式,包括基于文本的、基于Web的和GUI应用程序;MicrosoftOutlook和Netscape Messenger属于最后一类。每个电子邮件客户端被配置为将邮件发送到邮件传送代理(MTA),并可用于轮询MTA以获取发送到用户地址的电子邮件。为此,您需要邮件服务器上的电子邮件帐户(技术上是MTA),并且可以使用标准的Internet协议,或者脱机处理电子邮件(使用POP 3),或者将电子邮件保留在服务器上(使用IMAP)。用于从客户端向MTA发送邮件以及在MTA之间发送邮件的协议是SMTP(简单邮件传输协议)。
MTA之间真正发生的事情只是稍微有趣一些。电子邮件服务器严重依赖dns和特定于电子邮件的记录。邮件传送(或MX)记录。MX记录与用于解析URL的DNS记录略有不同,后者包含一些用于更有效地路由邮件的附加优先级信息。我不会在这里深入研究这些细节,但重要的是要理解DNS是成功和高效地路由电子邮件的关键。James是一个MTA,而JavaMailAPI为MUA提供了一个框架。在本文中,我们将使用JavaMail为我们的James安装设置一个测试。在本系列文章中,我们将使用JamesMailetAPI来展示如何开发您自己的James应用程序。
2 Javamail快速入门
进行Javamail开发需要用到两个包:mail.jar和activation.jar,在开始Javamail编程之前,请自己将这两个包添加到IDE的Build path中或将这两个包的路径配置到环境变量中。
2.1使用Javamail向James的邮箱帐户发送邮件
2.1.1业务描述
本例将使用Javamail实现邮件的发送功能。发送邮件需要配置邮件服务器属性信息,配置邮件接收地址,使用SMTP认证获得会话(Session),构建邮件体(MimeMessage),发送邮件。具体编码如下:
2.1.2编码实现
发送邮件需要两个类:一个是SMTP用户身份认证类(James在默认情况下,是需要SMTP身份认证的);另一个就是我们的邮件发送类,为简单起见,我们直接将邮件的相关信息,如:标题、内容、发送者、接收者等信息直接写在类中,运行main()函数即发送。当然,你同样可以为自己的邮件发送系统构造一个邮件发送介面,通过Servlet将相关参数传递至后台进行处理与发送。其主要代码也就是此main()函数中的内容,故不赘述。请各位按需修改,demo代码是截图,不便之处请谅解,可以到我的git获取代码。
SendMail.java发送邮件代码:
值得一提的是,本程序已经实现了带附件邮件的发送功能,如果要发送带附件的邮件,则只需要将附件的路径传到fileAttachment变量中就可以了。邮件发送成功后,程序将在后台打印出“发送成功”,这样我们就完成了邮件发送功能。那么,我们应该如何检验服务器是否确实收到我们发送的测试邮件呢?Javamail可以发送邮件,当然也能接收邮件啦,下面让我们一起使用Javamail编写邮件接收功能来检验吧。
2.2使用Javamail接收邮件
2.2.1业务描述
在上一节,我们已经向James的accidentaly用户发送了一封测试邮件,我们应该如何使用Javamail来收取这封邮件呢?
为读取邮件,必须首先设置服务器属性(Properties),获取一个会话(Session),然后获取并连接邮箱所在的存储器(Store对象),打开该用户的邮箱(Folder),获取所希望阅读的消息,最后关闭目录和连接。
下面的程序实现了接收accidentaly@dascomyun邮箱中所有邮件,并将发送人和主题打印出来
程序运行成功后,将会把accidentaly用户的邮件从James服务器中取出,并将此邮箱中所有邮件的发件人、主题打印在后台。若要打印该邮件的内容等信息,则只要将message[i]对象中的邮件内容等信息读取出来就可以了。
注:鉴于邮件的存储结构(将在第五章介绍),读取邮件附件是一个比较复杂的过程,因为邮件的文本内容和附件信息都是保存在BodyPart对象中的,BodyPart用于标识类型的标记不明确,造成对附件的判断较为复杂。对于附件的操作本人将在今后的改进版本中加以介绍。
3 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中或配置到系统环境变量中。
3.1 用Mailet做一个Hello的例子
3.1.1 业务描述
我们要实现当外部发送给James服务器中名字含accidentaly的邮箱时,服务器在这封邮件的主题前加入“Hello”,并在服务器后台输出“Received a piece of Email”。如前所述,Mailet包括匹配器Matcher和Mailet两种接口,现在就让我们用Mailet API实现这两个接口吧。
匹配器BizMatcher.java
BizMaillet.java
3.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代码:
前半部分是用于配置Mailet包所在位置,后半部分是用于配置Matcher包所在位置,我们把我们刚编写的Mailet和Matcher所在位置配置进去就可以了。配置后的结果如下:
Xml代码
这样就完成了包的配置。我们都知道,Mailet的工作过程是:首先由Matcher来匹配所接收到的邮件,然后提交给相应的Mailet处理,但是哪个匹配器对应哪个Mailet呢?我们还需要配置Mailet的对应关系。同样在config.xml中找到下面的代码,并在这段代码下面加入我们自己的Mailet:
Xml代码
<mailet match="All" class="PostmasterAlias" />
Xml代码
<mailet match="BizMatcher" class="BizMaillet" />
这样就完成了我们自定义的Mailet的配置部署工作了。重启James服务器,则此Mailet即可生效。
3.1.4 测试Mailet
前面我们已经完成了Mailet的编码和部署工作,现在就让我们来测试一下我们的Mailet是否生效吧。首先,需要在James服务器上新建一个名称含accidentaly的用户。前面已介绍过新建用户的方法了,在此就不重复叙述了。
使用adduser accidentaly 881213命令新建一个accidentaly用户。
使用上面所谈及的“使用Javamail向James的邮箱帐户发送邮件”来向accidentaly@dascomyun发送一封邮件(当然,你同样可以使用Foxmail或Outlook向此地址发送邮件),邮件发送成功后,James服务器后台将输出“Receive a piece of email”。运行效果如下图所示:
4 常用Javamail API简介
核心JavaMail API可以分为两部分,一部分由七个类组成:Session、Message、Address、Authenticator、Transport、Store及Folder,它们都来自Javamail API顶级包(但开发者需要使用的具体子类可能在javax.mail.internet包内)。可以用这些类完成大量常见的电子邮件任务,包括发送消息、检索消息、删除消息、认证、回复消息、转发消息、管理附件、处理基于HTML文件格式的消息以及搜索或过滤邮件列表,这类任务主要属于MTA范畴。下图描绘了Javamail邮件收发过程。
下面给出这七个核心类的简单介绍,以使读者能对Javamail框架有一个大体了解:
4.1 javax.mail.Session
Session类定义了一个基本邮件会话,它是Javamail API最高层入口类,所有其它类都必须经由Session对象才得以生效。Session对象管理配置选项和用于与邮件系统交互的用户认证信息,它使用java.util.Properties对象获取信息,如邮件服务器、用户名、密码及整个应用程序中共享的其它信息。
Session类的构造器是私有的,它不能被继承,也不能使用new语句来创建实例,但它提供了两个表态方法getInstance和getDefaultInstance来获取Session实例,前者创建一个独立的会话,否则获取缺省的共享会话。
API明细:/javamail-1.4.1/docs/javadocs/javax/mail/Session.html
4.2 javax.mail.Message
获得Session对象后,可以开始继续创建要发送的邮件消息,这由Message类来完成,Message实现了Part接口,它表示一个邮件消息,包含一系列属性(attribute)和一个消息内容(content)。消息属性标识了消息地址信息,定义了消息内容的结构(包括内容类型);消息内容使用DataHandler对象包装实际数据。当邮件消息位于目录(folder)中时,系统还使用一个标志位集合来描述它的状态。
Message是抽象类,实际使用时必需用一个子类代替以表示具体的邮件格式。比如说,Javamail API提供了MimeMessage(位于javax.mail.internet.MimeMessage包)类,该类扩展自Message,实现了RFC822和MIME标准。Message的子类通常通过字节流构建其实例,相应的,它们也可以生成字节流来传输自身。
API明细:/javamail-1.4.1/docs/javadocs/javax/mail/Message.html
4.3 javax.mail.Address
Address类表示电子邮件地址,它是一个抽象类。其子类(最经常使用的子类是javax.mail.internet.InternetAddress)提供具体实现,且通常可串行化。
在创建了Session和Message,并设置了消息内容后,可以用Address确定邮件消息的发送者和接收者地址。
API明细:/javamail-1.4.1/docs/javadocs/javax/mail/Address.html
4.4 javax.mail.Authenticator
Authenticator代表一个可以为网络连接获取认证信息的对象,它通常通过提示用户输入用户名和密码来收集认证信息,使连接可以访问受保护的资源。对于Javamail API来说,这些资源就是邮件服务器。Javamail Authenticator在javax.mail包中,它和java.net中同名的类Authenticator不同。
要使用Authenticator,必须先创建一个它的子类实例,并且在会话对象创建时为会话注册该Authenticator对象。在需要认证的时候,就会通知Authenticator。程序可以弹出窗口,也可以从配置文件中(虽然没有加密是不安全的)读取用户名和密码,并使用它们作为构造函数参数创建一个PasswordAuthentication对象返回给调用程序。
API明细:/javamail-1.4.1/docs/javadocs/javax/mail/Authenticator.html
4.5 javax.mail.Transport
消息发送的最后步骤是使用Transport类。该类使用指定协议发送消息(通常是SMTP)。Transport是抽象类,它的工作方式与Session有些类似,可以通过静态方法或实例方法发送消息。Transport继承自Service类,而后者提供了很多通用方法,如命名传输、连接服务器、监听传输事件等等。
API明细:/javamail-1.4.1/docs/javadocs/javax/mail/Transport.html
4.6 javax.mail.Store
Store是一个抽象类,它模拟了消息存储器及其内部目录(Folder)访问协议,以存储和读取消息,其子类提供具体实现。
Store定义的存储器包括一个分层的目录体系,消息存储在目录内,。客户程序可以通过获取一个实现了数据库访问协议的Store对象来访问消息存储器,绝大多数存储器要求用户在访问前提供认证信息,connect方法执行了该认证过程。
Store store = session.getStore("pop3");//指定协议
store.connect(host,usename,password);//
API明细:/javamail-1.4.1/docs/javadocs/javax/mail/Store.html
4.7 javax.mail.Folder
Folder是一个抽象类,用于分级组织邮件,其子类提供针对具体协议的实现。Folder代表的目录可以容纳消息或子目录,存储在目录内的消息被顺序计数(从1开始到消息总数),该顺序被称为“邮箱顺序”,通常基于邮件消息到达目录的顺序。邮件顺序的变动将改变消息的序列号,这种情况仅发生在客户程序调用Expunge方法擦除目录内设置了Flags.Flag.DELETED标志位的消息时。执行擦除操作后,目录内消息将重新编号。
客户程序可以通过消息序列号或直接通过相应的Message对象应用目录中的消息,由于消息序列号在会话中很可能改变,因此应尽可能保存Message对象而非序列号来反复引用对象。
连接到Store之后,接下来可以获取一个文件夹(Folder)。该文件夹必须先使用open()方法打开,然后才能读取里面的消息:
Folder folder = store.getDefaultFolder();
//或 : Folder folder = store.getFolder("inbox");
folder.open(Folder.READ_WRITE);
Message message[] = folder.getMessage();
open()方法指定了要打开的文件夹及打开方式(如Folder.READ_WRITE)。 inbox是POP3唯一可以使用的文件夹。如果使用IMAP,还可以用其它的文件夹。获得Message之后,就可以用getContent()获得其内容,或者用writeTo()将内容写入输出流。getContent()方法之能得到消息内容,而writeTo()的输出却包含消息头.读完邮件之后要关闭与Folder和Store的连接:
folder.close(aBoolean);
store.close();
API明细:/javamail-1.4.1/docs/javadocs/javax/mail/Folder.html
5 常用Mailet API简介
Mailet主要包含两个包:org.apache.mailet和org.apache.mailet.dates
5.1 org.apache.mailet
此包主要用于匹配器和Mailet的编写。自定义的Mailet类需要继承org.apache.mailet.GenericMailet,自定义的Matcher类需要继承org.apache.mailet.GenericMatcher或org.apache.mailet.GenericRecipientMatcher。例子详见第四章Mailet快速入门。
API明细:/MailetSDK/javadocs/org/apache/mailet/package-summary.html
5.2 org.apache.mailet.dates
此包主要用于邮件中的日期格式的转换。
API明细:/MailetSDK/javadocs/org/apache/mailet/dates/package-summary.html
码云:https://gitee.com/AccidentalyAcross/projects