概要
Eメールを送信するJavaアプリケーションのサンプルコードです。JavaMail、Commons Email、Spring Boot Starter mailの3つのライブラリを試しました。
環境
- Windows7 (64bit)
- Java 1.8.0_65
- JavaMail 1.5.4
- Spring Boot 1.3.0
- spring-boot-starter-mail 1.3.0
- Commons Email 1.4
参考
JavaMail
準備
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.5.4</version>
</dependency>
properties
mail.user=
mail.host=
mail.from=
mail.mime.address.strict=true
mail.store.protocol=imap
mail.transport.protocol=smtp
mail.debug=false # debug出力
An SMTP protocol provider for the JavaMail API that provides access to an SMTP server.
mail.smtp.user=
mail.smtp.host=
mail.smtp.port=
mail.smtp.from=
mail.smtp.connectiontimeout=0 # コネクション確立までのタイムアウト時間(ミリ秒)
mail.smtp.timeout=0 # SMTPサーバとの通信(read)のタイムアウト時間(ミリ秒)
mail.smtp.writetimeout=0 # ソケットの書き込みのタイムアウト時間(ミリ秒)
mail.smtp.auth=false # authコマンドでのユーザー認証を行うか
mail.smtp.ssl.enable=false #
mail.smtp.starttls.enable=false #
mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory # ssl
mail.smtp.socketFactory.fallback=false # ssl
mail.smtp.socketFactory.port= # ssl
サンプルコード
import java.io.UnsupportedEncodingException;
import java.util.Properties;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class JavaMailSample {
private static final Logger log = LoggerFactory.getLogger(JavaMailSample.class);
public static void main(String[] args) {
JavaMailSample mailSend = new JavaMailSample();
mailSend.send("JavaMail テストメール", "テストメールの本文");
}
public void send(String subject, String content) {
final String to = "xxx.yyy.zzz@example.com";
final String from = "*****.*****.*****@gmail.com";
// Google account mail address
final String username = "*****.*****.*****@gmail.com";
// Google App password
final String password = "***************";
//final String charset = "ISO-2022-JP";
final String charset = "UTF-8";
final String encoding = "base64";
// for gmail
String host = "smtp.gmail.com";
String port = "587";
String starttls = "true";
// for local
//String host = "localhost";
//String port = "2525";
//String starttls = "false";
Properties props = new Properties();
props.put("mail.smtp.host", host);
props.put("mail.smtp.port", port);
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", starttls);
props.put("mail.smtp.connectiontimeout", "10000");
props.put("mail.smtp.timeout", "10000");
props.put("mail.debug", "true");
Session session = Session.getInstance(props,
new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
try {
MimeMessage message = new MimeMessage(session);
// Set From:
message.setFrom(new InternetAddress(from, "Watanabe Shin"));
// Set ReplyTo:
message.setReplyTo(new Address[]{new InternetAddress(from)});
// Set To:
message.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
message.setSubject(subject, charset);
message.setText(content, charset);
message.setHeader("Content-Transfer-Encoding", encoding);
Transport.send(message);
} catch (MessagingException e) {
throw new RuntimeException(e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}
Commons Email
準備
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-email</artifactId>
<version>1.4</version>
</dependency>
サンプルコード
package com.example.sbmail.mail;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.mail.DefaultAuthenticator;
import org.apache.commons.mail.Email;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.SimpleEmail;
import org.springframework.stereotype.Component;
@Component
public class CommonsMailSample {
private String to = "xxx.yyy.zzz@example.com";
private String from = "*****.*****.*****@gmail.com";
// Google account mail address
private String username = "*****.*****.*****@gmail.com";
// Google app password
private String password = "**********";
//private String charset = "ISO-2022-JP";
private String charset = "UTF-8";
private String encoding = "base64";
// for gmail
private String host = "smtp.gmail.com";
private int port = 587;
private boolean starttls = true;
// for local
//private String host = "localhost";
//private int port = 2525;
//private boolean starttls = false;
private Map<String, String> headers = new HashMap<String, String>(){
private static final long serialVersionUID = 1L;
{
put("Content-Transfer-Encoding", encoding);
}
};
public static void main(String[] args) throws IOException {
CommonsMailSample sendMail = new CommonsMailSample();
sendMail.send("Commons Email テストメール", "テストメールの本文");
}
public void send(String subject, String content) {
Email email = new SimpleEmail();
try {
email.setHostName(host);
email.setSmtpPort(port);
email.setCharset(charset);
email.setHeaders(headers);
email.setAuthenticator(new DefaultAuthenticator(username, password));
email.setStartTLSEnabled(starttls);
email.setFrom(from);
email.addTo(to);
email.setSubject(subject);
email.setMsg(content);
email.setDebug(true);
email.send();
} catch (EmailException e) {
e.printStackTrace();
}
}
}
spring-boot-starter-mail
準備
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>1.3.0.RELEASE</version>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
</dependency>
properties
spring:
# Email (MailProperties)
mail:
default-encoding: UTF-8
protocol: smtp
#host: localhost
#port: 2525
host: smtp.gmail.com
port: 587
jndi-name: mail/Session
password: ********** # Google App password
username: *****.*****.*****@gmail.com # Google account mail address
properties:
mail:
smtp:
auth: true
starttls:
#enable: false
enable: true
socketFactory:
#port: 2525
port: 587
class: javax.net.ssl.SSLSocketFactory
fallback: false
debug: true
test-connection: false
MailSenderAutoConfiguration.java
サンプルコード
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Component;
@Component
public class SpringBootMailSample {
private static final Logger log = LoggerFactory.getLogger(SpringBootMailSample.class);
private final JavaMailSender javaMailSender;
@Autowired
SpringBootMailSample(JavaMailSender javaMailSender) {
this.javaMailSender = javaMailSender;
}
public SimpleMailMessage send(String subject, String content) {
SimpleMailMessage mailMessage = new SimpleMailMessage();
mailMessage.setTo("xxx.yyy.zzz@example.com");
mailMessage.setReplyTo("*****.*****.*****@gmail.com");
mailMessage.setFrom("*****.*****.*****@gmail.com");
mailMessage.setSubject(subject);
mailMessage.setText(content);
javaMailSender.send(mailMessage);
return mailMessage;
}
}
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
@Component
public class SpringBootMailSample2 {
private static final Logger log = LoggerFactory.getLogger(SpringBootMailSample2.class);
private final JavaMailSender javaMailSender;
@Autowired
public SpringBootMailSample2(JavaMailSender javaMailSender) {
this.javaMailSender = javaMailSender;
}
public void send(String subject, String content) {
try {
MimeMessage mail = javaMailSender.createMimeMessage();
mail.setHeader("Content-Transfer-Encoding", "base64");
MimeMessageHelper helper = new MimeMessageHelper(mail, false);
helper.setTo("xxx.yyy.zzz@example.com");
helper.setReplyTo("*****.*****.*****@gmail.com");
helper.setFrom("*****.*****.*****@gmail.com");
helper.setSubject(subject);
helper.setText(content);
javaMailSender.send(mail);
} catch (MessagingException e) {
e.printStackTrace();
}
}
}
開発用SMTPサーバー
ローカル環境でメールの送信テストを行いたい場合に使用する開発用SMTPサーバーを幾つか試用しました。
FakeSMTP
Dummy SMTP server with GUI for testing emails in applications easily.
Javaで開発されているので実行環境を選びません。受信したメールをeml形式のファイルとして保存することができます。
日本語(ISO-2020-JP,UTF-8)で書かれたメールにも対応しています。
ビルド
ソースコードからビルドしてjarファイルを生成します。(ビルド済みのjarを取得して使用することもできます)
$ git clone https://github.com/Nilhcem/FakeSMTP.git
> cd FaceSMTP
> mvn package -Dmaven.test.skip
実行
-o
で指定したディレクトリに受信したメールがemlファイルとして保存されます。
> java -jar fakeSMTP-2.1-SNAPSHOT.jar -p 2525 -a 127.0.0.1 -o d:\mail\message
ファイルに書き出す必要がない場合は代わりに-m
オプションを付けます。
> java -jar fakeSMTP-2.1-SNAPSHOT.jar -p 2525 -a 127.0.0.1 -m
上記のオプションで実行するとGUIが起動します。
メールをクリックするとメールクライアントが立ち上がりメール本文を確認することが出来ます。
バックグラウンドモードで実行したい場合は-b
、-s
オプションを付けます。
> java -jar fakeSMTP-2.1-SNAPSHOT.jar -p 2525 -a 127.0.0.1 -b -s -o d:\mail\message
smtp4dev
Windows 7/Vista/XP/2003/2010 compatible dummy SMTP server. Sits in the system tray and does not deliver the received messages. The received messages can be quickly viewed, saved and the source/structure inspected. Useful for testing/debugging software that generates email.
Windowsのネイティブアプリケーションです。smtp4dev.exeという実行ファイルのみでインストール不要です。最終リリースが2011年となっているのが気になりますが今のところ問題なく利用できました。
日本語にも対応しています。(ISO-2022-JPの場合、GUI上では件名が文字化けしましたがメールクライアントでは正常に表示されます。)
Viewボタンをクリックするとメールクライアントでメールを表示することができます。図はありませんがInspectボタンをクリックするとメールヘッダーやソースを確認することができます。
option画面
MailDev
MailDev is a simple way to test your projects' emails during development with an easy to use web interface that runs on your machine.
インストールと実行にはNodejs,npmが必要です。ISO-2022-JPの場合は文字化けを起こしますが、UTF-8は問題ありませんでした。
インストール
> npm install -g maildev
実行
> maildev -s 2525 -w 8080 --ip 127.0.0.1
ブラウザベースのGUIが付属しています。
SubEtha
SubEtha SMTP is a Java library for receiving SMTP mail
上記のsmtpサーバーとは用途が違い、Unit Testで使用する組み込み型smtpサーバーです。こちらは未だ試用していませんが便利そうだったので記載だけしておきます。
メモ
テンプレートエンジン
mustache.java
<dependency>
<groupId>com.github.spullara.mustache.java</groupId>
<artifactId>compiler</artifactId>
<version>0.9.1</version>
</dependency>
Handlebars.java
<dependency>
<groupId>com.github.jknack</groupId>
<artifactId>handlebars</artifactId>
<version>4.0.2</version>
</dependency>
Gmail
Googleアカウントの2段階認証
smtpサーバーにsmtp.gmail.comを使用してメール送信を行う場合、指定するGoogleアカウントが2段階認証プロセスを適用していると下記のエラーメッセージを出力してメール送信に失敗します。
この場合はそのGoogleアカウントでアプリパスワードを取得し、そのパスワードを使用することでメール送信ができるようになります。
javax.mail.AuthenticationFailedException: 534-5.7.9 Application-specific password required. Learn more at
534 5.7.9 https://support.google.com/accounts/answer/185833 63sm19755563pfq.92 - gsmtp
アプリパスワードを取得する
Googleアカウント情報ページの"Googleへのログイン"メニューを選択し"アプリ パスワード"をクリックします。
アプリパスワードの用途をメニューから選ぶか、任意の名前を付けてパスワードを作成します。
図の通り(モザイクをかけています)パスワードが作成されるので、このパスワードをgoogleアカウントのパスワードとして使用します。
作成したパスワードはいつでも取り消すことができます。
メール送信に使用するポート
25
SMTP
465
SMTPS (Simple Mail Transfer Protocol Secure)
ポート465はSMTPSに割り当てられていたが、STARTTLSの制定により割り当ては無効となった。
587
MSA (Mail Submission Agent)
MUA-MSA間の投稿(Submission)ではポート587が使われることが多い。
STARTTLS
STARTTLS(スタート・ティーエルエス)は、平文の通信プロトコルを暗号化通信に拡張する方法のひとつ。
STARTTLSは、IMAPやPOP3に対してはRFC 2595、SMTPに対してはRFC 3207、FTPに対してはRFC 4217、XMPPに対してはRFC 6120の5節、LDAPに対してはRFC 4511の4.14節、NNTPに対してはRFC 4642で規定する。
参考になったstackoverflow
spring.mail.properties.mail.smtp.ssl.enable = true