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