1. 簡介
Apache James(Java Apache Mail Enterprise Server)是Apache組織的子項目之一,完全采用純Java技術開發,實現了SMTP、POP3與NNTP等多種郵件相關協議。
James也是一個郵件應用平臺,可以通過Match與Mailet擴充其功能,如Mail2SMS、Mail2Fax等。James提供了比較完善的配置方案,尤其是關于郵件內容存儲和用戶信息存儲部分,可以選擇在文件、數據庫或其他介質中保存。
James性能穩定、可配置性強,還是開源項目,所有源代碼不存在版權問題,因此,James在項目中的應用日益廣泛,現在已經有3.0版本,不過下載后發現,里面內容太過龐大,因此還是選擇精簡且穩定的2.3.2版本做介紹。
2. 安裝與配置
2.1 Windows環境下
第一步:安裝JDK,此處略。
第二步:下載James,并解壓。
點擊下載(推薦此鏈接,其余版本可能出錯。)
第三步:直接運行或需要配置JAVA_HOME。
這時,可以嘗試直接雙擊/james/bin/run.bat,若啟動無誤,將提示如下:
Using PHOENIX_HOME: C:/james
Using PHOENIX_TMPDIR: C:/james/temp
Using JAVA_HOME:
Phoenix 4.0.1
James 2.1
Remote Manager Service started plain:4555
POP3 Service started plain:110
SMTP Service started plain:25
NNTP Service Disabled
Fetch POP Disabled
要關閉 James 服務,請使用 Ctrl + C
啟動前請確保您的JDK環境變量如JAVA_HOME等已經設置好;James 啟動時,其SMTP 服務默認在 25 端口啟動,POP3 服務默認在 110 端口啟動, NNTP 服務默認在 119 端口啟動, 請確保這些端口未被占用。
如果這幾個端口已經占用的話,打開run.bat是會一閃而過的,請在james的文件路徑apps/james/SAR-INF下打開config.xml文件,這個文件是服務器的配置文件,用notepad++或editplus等編輯器打開。CTRL+F找到pop3server這個標簽:把110改成其他端口
<pop3server enabled="true">
<!-- POP3協議端口 -->
<port>110</port>
<handler>
helloName autodetect="true">myMailServer</helloName>
<connectiontimeout>120000</connectiontimeout>
</handler>
</pop3server>
同理,把下面smtpserver和nntpserver的端口也改掉。
我們修改完這個幾個端口后,應該就可以順利啟動James服務了。
PS:若使用其他版本的james,可能會報
”caused by: java.io.IOException: 文件名、目錄名或卷標語法不正確?!?
2.2 linux環境下
第一步:安裝jdk,配置環境變量,此處略。
第二步:下載,并解壓。
1. wget http://mirror.bjtu.edu.cn/apache//james/server/apache-james-2.3.2.tar.gz
2. tar -zxvf apache-james-2.3.2.tar.gz
3. ln -s james-2.3.2 mailserver
第三步:運行。必須先運行一下,才能配置。
1. cd james-2.3.2/
2. chmod +x bin/*.sh
3. vi bin/run.sh #在第一行加入export JAVA_HOME=/opt/java(此處為你的JAVA_HOME)
4. bin/run.sh
運行成功后如下:
[root@dev6 james-2.3.2]# sh bin/run.sh
Using PHOENIX_HOME: /opt/james-2.3.2
Using PHOENIX_TMPDIR: /opt/james-2.3.2/temp
Using JAVA_HOME: /opt/java
Running Phoenix:
Phoenix 4.2
James Mail Server 2.3.2
Remote Manager Service started plain:4555
POP3 Service started plain:110
SMTP Service started plain:25
NNTP Service started plain:119
FetchMail Disabled
3. 配置james
默認配置啟動James服務,只能給內網發送郵件,我們的要求是可以給外網的其他郵箱發郵件,比如163,qq,sina等郵箱發送郵件,那么我們必須修改James默認配置,接下來我們就來看看如何修改
還是打開config.xml文件,找到postmaster標簽:
<postmaster>Postmaster@localhost</postmaster>
……
<servernames autodetect="true" autodetectIP="true">
<servername>localhost</servername>
</servernames>
把localhost該成你自己想要的郵箱域名, autodetect和autodetectIP設置為“false”,這里localhost假設改成 cp.com 如果開了一個帳號 cp ,那么他的郵件地址就是 cp@cp.com(注意兩個localhost都要改),改完如下:
<postmaster>Postmaster@cp.com</postmaster>
<servernames autodetect="false" autodetectIP="false">
<servername>cp.com</servername>
</servernames>
修改理由:
1.autodetect設為true的話會自動偵測你的主機名,設成false會用你指定的servername
2.autodetectIP設為true會為你的servername加上IP,然而并不需要
3.servername改為你的server名字,如clararun.com
4.在C:\WINDOWS\System32\drivers\etc\host文件中添加127.0.0.1 cp.com
(此處就相當于注冊了一個偽域名,之后可以用此郵箱向本地和外面的其他郵箱發郵件,但是接收不到外面郵箱發來的郵件,因為沒有注冊。)
實際上我把這個配置文件中所有的localhost都改成了我的域名;把所有的autodetect屬性,修改為false,autodetectIP也設為false;查找所有myMailServer,替換為域名
找到
<mailet match="RemoteAddrNotInNetwork=127.0.0.1" class="ToProcessor">
<processor> relay-denied </processor>
<notice>550 - Requested action not taken: relaying denied</notice>
</mailet>
把這一整段都注釋掉。
找到下面這一段
<!-- <authRequired>true</authRequired> -->
去掉它的注釋。
找到dnsserver標簽:
<dnsserver>
<servers>
<!--Enter ip address of your DNS server, one IP address per server -->
<!-- element. -->
<!--
<server>127.0.0.1</server>
-->
</servers>
<!-- Change autodiscover to false if you would like to turn off autodiscovery -->
<!-- and set the DNS servers manually in the <servers> section -->
<autodiscover>true</autodiscover>
<authoritative>false</authoritative>
<!-- Maximum number of entries to maintain in the DNS cache -->
<maxcachesize>50000</maxcachesize>
</dnsserver>
在標簽下加入:
<server>49.123.92.128</server> <!--你的IP地址-->
<server>202.197.96.16</server> <!--第一個DNS地址-->
<server>202.197.96.1</server> <!--第二個DNS地址-->
上面的三個IP要根據你的電腦情況來填寫,第一個是你電腦的IP地址,也就是服務器地址,第二和第三個都是DNS地址,這三個地址都可以通過在cmd輸入命令ipconfig中查看得到。
這樣就算配置完成了,重新啟動一下服務器。
4. 創建郵件賬號
打開cmd,輸入telnet localhost 4555,會提示你輸入login id和password,這個id和password可以在config.xml中修改,CTRL+F查找password,把login和password的值換掉,默認是root和root。
<account login="root" password="!changeme!"/>
創建新用戶的命令是adduser username password
例如
adduser cp cp
adduser jack jack
輸入命令listusers可以查看所有用戶。
下面是一些命令的含義:
5. 使用郵件客戶端收發郵件
這里介紹用Foxmail客戶端來進行郵件的收發。
填寫E-mail地址和密碼,地址是cp@cp.com,密碼就是剛才用命令添加的cp。
收件服務器和發件服務器都是你自己的ip地址,其他均為默認。
同理,我們再添加rose賬戶,就可以愉快地背著jack和rose通信了~
至此,郵件服務器的搭建和客戶端的測試已經完成了。
文章參考
6. 用JavaMail測試James
為了確保我們的設置正常運行,我們將編寫一組快速發送消息和列出收件箱內容的類,模擬典型電子郵件客戶端的基本功能。
import java.io.IOException;
import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
public class MailClient extends Authenticator{
public static final int SHOW_MESSAGES = 1;
public static final int CLEAR_MESSAGES = 2;
public static final int SHOW_AND_CLEAR =
SHOW_MESSAGES + CLEAR_MESSAGES;
protected String from;
protected Session session;
protected PasswordAuthentication authentication;
public MailClient(String user, String host)
{
this(user, host, false);
}
public MailClient(String user, String host, boolean debug)
{
from = user + '@' + host;
authentication = new PasswordAuthentication(user, user); //由于創建的user和password一樣,所以傳兩個user就OK
Properties props = new Properties();
props.put("mail.user", user);
props.put("mail.host", host);
props.put("mail.debug", debug ? "true" : "false");
props.put("mail.store.protocol", "pop3");
props.put("mail.transport.protocol", "smtp");
session = Session.getInstance(props, this);
}
public PasswordAuthentication getPasswordAuthentication()
{
return authentication;
}
public void sendMessage(
String to, String cc, String subject, String content)
throws MessagingException
{
System.out.println("SENDING message from " + from + " to " + to);
System.out.println();
MimeMessage msg = new MimeMessage(session);
msg.addRecipients(Message.RecipientType.TO, to); //添加收件人
msg.addRecipients(Message.RecipientType.CC, cc); //添加轉發人
msg.setSubject(subject);
msg.setText(content);
Transport.send(msg);
}
public void checkInbox(int mode)
throws MessagingException, IOException
{
if (mode == 0) return;
boolean show = (mode & SHOW_MESSAGES) > 0;
boolean clear = (mode & CLEAR_MESSAGES) > 0;
String action =
(show ? "Show" : "") +
(show && clear ? " and " : "") +
(clear ? "Clear" : "");
System.out.println(action + " INBOX for " + from);
Store store = session.getStore();
store.connect();
Folder root = store.getDefaultFolder();
Folder inbox = root.getFolder("inbox");
inbox.open(Folder.READ_WRITE);
Message[] msgs = inbox.getMessages();
if (msgs.length == 0 && show)
{
System.out.println("No messages in inbox");
}
for (int i = 0; i < msgs.length; i++)
{
MimeMessage msg = (MimeMessage)msgs[i];
if (show)
{
System.out.println(" From: " + msg.getFrom()[0]);
System.out.println(" Subject: " + msg.getSubject());
System.out.println(" Content: " + msg.getContent());
}
if (clear)
{
msg.setFlag(Flags.Flag.DELETED, true);
}
}
inbox.close(true);
store.close();
System.out.println();
}
}
這個mail client主要是為了讓我們發送消息和顯示或刪除給定用戶的服務器上可用的郵件列表。
此類還實現了Authenticator接口,可以很容易地檢索電子郵件時管理登錄過程。
我創建了兩個構造函數,其中之一顯式設置JavaMail調試標志。這將打印客戶端/服務器協議交互到控制臺,以便您可以看到發生了什么。較短的構造函數將此標志關閉。另外兩個參數是用戶名和主機。通過暗示,電子郵件地址可以從用戶和主機派生。我們創建一個PasswordAuthentication可以通過接口getPasswordAuthentication()指定的方法返回的對象Authenticator。
該checkInbox()方法執行更多的工作,因為它需要列出消息,并且可選地擦除它們。也可以根據您用于模式參數的標志來清除消息而不查看它們。要實際獲取消息,我們需要Store通過我們的會話對象獲取引用,連接到服務器,然后打開收件箱文件夾。在我們引用該文件夾之后,我們可以遍歷消息并顯示或刪除它們。
下面是mail client的測試類:
public class MailClientTest {
public static void main(String[] args)
throws Exception
{
// CREATE CLIENT INSTANCES
MailClient cp = new MailClient("cp", "cp.com");
MailClient rose = new MailClient("rose", "cp.com");
MailClient jack = new MailClient("jack", "cp.com");
String c1 = ContentModes.content1;
String c2 = ContentModes.content2;
// CLEAR EVERYBODY'S INBOX
cp.checkInbox(MailClient.CLEAR_MESSAGES);
rose.checkInbox(MailClient.CLEAR_MESSAGES);
jack.checkInbox(MailClient.CLEAR_MESSAGES);
Thread.sleep(500); // Let the server catch up
// SEND A COUPLE OF MESSAGES TO BLUE (FROM RED AND GREEN)
cp.sendMessage(
"rose@cp.com,502966686@qq.com",
"",
"Testing rose from cp",
"Dear" + "\n" +
"This is a test message." +"\n"+
"Good day! ");
rose.sendMessage(
"cp@cp.com",
"502966686@qq.com",
"Testing cp from rose",
"Dear" + "\n" +
"This is a test message." +"\n"+
"Good day! ");
rose.sendMessage(
"cp@cp.com",
"502966686@qq.com",
"New Situation!",
c1);
Thread.sleep(500); // Let the server catch up
// LIST MESSAGES FOR BLUE (EXPECT MESSAGES FROM RED AND GREEN)
// jack.checkInbox(MailClient.SHOW_AND_CLEAR);
cp.checkInbox(MailClient.SHOW_MESSAGES);
rose.checkInbox(MailClient.SHOW_AND_CLEAR);
}
}
7. Matchers
james帶來了許多標準匹配器即matcher。每個都實現了Matcher API,如下所示,并提供了現有MTA以及其他有用的擴展通用的功能。界面相當簡單; 它包括一對生命周期方法,init()和destroy(),和一對記賬方法,getMatcherInfo()和getMatcherConfig(),還有main方法--match(),它用來操作Mail對象。該Mail引用提供對容器狀態,郵件消息和元數據的訪問以進行處理。
public interface Matcher
{
void init(MatcherConfig config);
void destroy();
String getMatcherInfo();
MatcherConfig getMatcherConfig();
Collection match(Mail mail);
}
以下是寫好的Matcher接口。
8. Mailet
許多james的功能是通過Mailet API實現的,習慣于用servlet的人會對此很熟悉。
與Matcher API一樣,Mailet接口提供了init()和destroy()兩個生命周期的方法。兩種有返回值的方法。第一個, getMailetInfo(),返回一個String對象,其中包含了與mailet關聯的作者,版本和版權等信息。第二個,getMailetConfig(),用來返回最近的mailet配置信息。
public interface Mailet
{
void init(MailetConfig config);
void destroy();
String getMailetInfo();
MailetConfig getMailetConfig();
void service(Mail mail);
}
主進程在services()方法中進行,帶有一個Mail對象參數。此對象提供對容器狀態,郵件消息和要進行處理的元數據的附加訪問。
以下是已經實現的mailet接口。
9. Mailet和Matcher的簡單嘗試
Mailet API是一個用來創建郵件處理程序的簡單的API,它被配置在郵件服務器端執行,分匹配器Matcher和Mailet的接口兩種,匹配器根據特定的條件匹配郵件消息,并觸發相應的Mailet。
Mailet的簡單可編程接口可以用來做一些郵件處理,比如反垃圾郵件,檢查郵件病毒以及郵件博客等等,利用移動設備可發送email的功能,可以做到手機通過mail發送信息到郵件服務器交給Mailet處理,形成移動博客的模型。
import javax.mail.MessagingException;
import org.apache.mailet.GenericRecipientMatcher;
import org.apache.mailet.MailAddress;
import org.apache.mailet.MatcherConfig;
public class MatcherTest1 extends GenericRecipientMatcher {
public boolean matchRecipient(MailAddress recipient)
throws MessagingException {
if(recipient.getUser().indexOf("rose")!= -1){
System.out.println(recipient.getHost());
System.out.println(recipient.getUser());
return true ;
}
return false;
}
}
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import org.apache.mailet.GenericMailet;
import org.apache.mailet.Mail;
public class MailetTest1 extends GenericMailet {
public void service(Mail mail) throws MessagingException {
MimeMessage mmp ;
mmp = (MimeMessage)mail.getMessage();
mmp.setSubject("This is emergency!"+mmp.getSubject());
mmp.setFileName(getMailetName()+"aha!");
System.out.println("Received a piece of Email.");
}
}
接下來是配置部署。
Mailet跟Servlet一樣,是服務器端程序,是不能直接在客戶端運行的,必須要部署到服務器端方可生效。部署具體步驟如下:
1、 將我們編寫的Matcher和Mailet打包成jar文件;
2、 在\james-2.3.1\apps\james\SAR-INF目錄下新建一個lib文件夾;
3、 將打包好的jar文件復制到剛剛新建的lib文件夾下;
4、 打開config.xml配置文件,找到以下這段代碼:
<mailetpackages>
<mailetpackage>org.apache.james.transport.mailets</mailetpackage>
<mailetpackage>org.apache.james.transport.mailets.smime</mailetpackage></mailetpackages>
<matcherpackages>
<matcherpackage>org.apache.james.transport.matchers</matcherpackage>
<matcherpackage>org.apache.james.transport.matchers.smime</matcherpackage></matcherpackages>
前半部分是用于配置Mailet包所在位置,后半部分是用于配置Matcher包所在位置,我們把我們剛編寫的Mailet和Matcher所在位置配置進去就可以了。配置后的結果如下:
<mailetpackages>
<mailetpackage>com.primeton.mailet.test</mailetpackage>
<mailetpackage>org.apache.james.transport.mailets</mailetpackage>
<mailetpackage>org.apache.james.transport.mailets.smime</mailetpackage>
</mailetpackages>
<matcherpackages>
<matcherpackage>com.primeton.mailet.test</matcherpackage>
<matcherpackage>org.apache.james.transport.matchers</matcherpackage>
<matcherpackage>org.apache.james.transport.matchers.smime</matcherpackage>
</matcherpackages>
這樣就完成了包的配置。我們都知道,Mailet的工作過程是:首先由Matcher來匹配所接收到的郵件,然后提交給相應的Mailet處理,但是哪個匹配器對應哪個Mailet呢?我們還需要配置Mailet的對應關系。同樣在config.xml中找到下面的代碼:
<mailet match="All" class="PostmasterAlias"/>
在這段代碼下面加入我們自己的Mailet:
<mailet match="All" class="PostmasterAlias"/>
<mailet match="MatcherTest1" class="MailetTest1"/>
這樣就完成了我們自定義的Mailet的配置部署工作了。重啟James服務器,則此Mailet即可生效。
10.總結
至此,james server的筆記結束了,它是一個很不錯的開源郵箱項目,希望能幫到大家,謝謝。