JavaアプリケーションからEメールを送信するサンプルコード

# 概要

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] (http://commons.apache.org/proper/commons-email/) 1.4

**参考**

* [Testing mail code in Spring Boot application] (http://www.javacodegeeks.com/2014/09/testing-mail-code-in-spring-boot-application.html)
* [Sending email] (https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-email.html)
* [E-mail送信(SMTP)] (https://github.com/terasolunaorg/guideline/blob/master/source/ArchitectureInDetail/Email.rst)


## [JavaMail] (http://www.oracle.com/technetwork/java/javamail/index.html)

### 準備

```xml:pom.xml
<dependency>
  <groupId>com.sun.mail</groupId>
  <artifactId>javax.mail</artifactId>
  <version>1.5.4</version>
</dependency>
```

### properties

[Package javax.mail] (https://docs.oracle.com/javaee/7/api/javax/mail/package-summary.html)

```text
mail.user=
mail.host=
mail.from=
mail.mime.address.strict=true
mail.store.protocol=imap
mail.transport.protocol=smtp
mail.debug=false                # debug出力
```

[Package com.sun.mail.smtp] (https://javamail.java.net/nonav/docs/api/com/sun/mail/smtp/package-summary.html)

> An SMTP protocol provider for the JavaMail API that provides access to an SMTP server.

```text
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
```

* [JavaMailを使う時はちゃんとタイムアウト値を設定しよう] (http://hyperash.hatenablog.com/entry/20041213/p1)
* [JavaMail mail.smtp.ssl.enable is not working] (http://stackoverflow.com/questions/2043018/javamail-mail-smtp-ssl-enable-is-not-working)


### サンプルコード

```java
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] (https://commons.apache.org/proper/commons-email/)

* [Users Guide] (https://commons.apache.org/proper/commons-email/userguide.html)

### 準備

```xml:pom.xml
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-email</artifactId>
  <version>1.4</version>
</dependency>
```

### サンプルコード

```java
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] (https://github.com/spring-projects/spring-boot/tree/master/spring-boot-starters/spring-boot-starter-mail)

### 準備

```xml:pom.xml
<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

[MailProperties.java] (https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailProperties.java)

```yaml:application.yml
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] (https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailSenderAutoConfiguration.java)


### サンプルコード

```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;
  }

}
```

```java
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] (https://github.com/Nilhcem/FakeSMTP)

> Dummy SMTP server with GUI for testing emails in applications easily.

Javaで開発されているので実行環境を選びません。受信したメールをeml形式のファイルとして保存することができます。
日本語(ISO-2020-JP,UTF-8)で書かれたメールにも対応しています。

#### ビルド

ソースコードからビルドしてjarファイルを生成します。(ビルド済みのjarを取得して使用することもできます)

```bash
$ git clone https://github.com/Nilhcem/FakeSMTP.git
```

```bat
> cd FaceSMTP
> mvn package -Dmaven.test.skip
```

#### 実行

`-o`で指定したディレクトリに受信したメールがemlファイルとして保存されます。

```bat
> java -jar fakeSMTP-2.1-SNAPSHOT.jar -p 2525 -a 127.0.0.1 -o d:\mail\message
```

ファイルに書き出す必要がない場合は代わりに`-m`オプションを付けます。

```bat
> java -jar fakeSMTP-2.1-SNAPSHOT.jar -p 2525 -a 127.0.0.1 -m
```

上記のオプションで実行するとGUIが起動します。

![FaceSMTPServer.png](https://qiita-image-store.s3.amazonaws.com/0/22772/4ef14584-a78d-3128-39b5-9f557adb5873.png)

メールをクリックするとメールクライアントが立ち上がりメール本文を確認することが出来ます。

![FaceSMTPServer1.png](https://qiita-image-store.s3.amazonaws.com/0/22772/c266b746-725e-718b-4ec6-1f45079f6040.png)

バックグラウンドモードで実行したい場合は`-b`、`-s`オプションを付けます。

```
> java -jar fakeSMTP-2.1-SNAPSHOT.jar -p 2525 -a 127.0.0.1 -b -s -o d:\mail\message
```


### [smtp4dev] (https://smtp4dev.codeplex.com/)

> 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上では件名が文字化けしましたがメールクライアントでは正常に表示されます。)

![smtp4dev0.png](https://qiita-image-store.s3.amazonaws.com/0/22772/4825b16b-f955-7c88-162b-854db755fa19.png)

Viewボタンをクリックするとメールクライアントでメールを表示することができます。図はありませんがInspectボタンをクリックするとメールヘッダーやソースを確認することができます。

![mailtest0.png](https://qiita-image-store.s3.amazonaws.com/0/22772/cfad8369-4912-b74a-de89-2edf5472c05a.png)

option画面

![smtp4dev1.png](https://qiita-image-store.s3.amazonaws.com/0/22772/6c7d7f9e-e1a4-543f-8775-dc4ee710b763.png)


### [MailDev] (http://djfarrelly.github.io/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は問題ありませんでした。

#### インストール
```bat
> npm install -g maildev
```

#### 実行

```bat
> maildev -s 2525 -w 8080 --ip 127.0.0.1
```

ブラウザベースのGUIが付属しています。

![maildev0.png](https://qiita-image-store.s3.amazonaws.com/0/22772/c7800f66-01cd-435f-5315-296ae6133280.png)


### [SubEtha] (https://github.com/voodoodyne/subethasmtp)

> SubEtha SMTP is a Java library for receiving SMTP mail

上記のsmtpサーバーとは用途が違い、Unit Testで使用する組み込み型smtpサーバーです。こちらは未だ試用していませんが便利そうだったので記載だけしておきます。

* [ユニットテスト用のメールサーバsubethasmtpがあることを知りました] (http://qiita.com/tacchang001/items/79097b68cc9e22a1f401)
* [SubEthaMailのwiserで仮想SMTPを立ててjavaのメール送信をユニットテストする！] (http://www.bunkei-programmer.net/entry/2015/01/10/012647)



## メモ

### テンプレートエンジン

* [The Apache Velocity Project] (http://velocity.apache.org/)
* [Apache FreeMarker] (http://freemarker.incubator.apache.org/)
* [mustache.java] (https://github.com/spullara/mustache.java)
* [Handlebars.java] (http://jknack.github.io/handlebars.java/)

**mustache.java**

```xml:pom.xml
<dependency>
  <groupId>com.github.spullara.mustache.java</groupId>
  <artifactId>compiler</artifactId>
  <version>0.9.1</version>
</dependency>
```

**Handlebars.java**

```xml:pom.xml
<dependency>
  <groupId>com.github.jknack</groupId>
  <artifactId>handlebars</artifactId>
  <version>4.0.2</version>
</dependency>
```

* [commons-emailとhandlerbars.javaでメール送信] (http://qiita.com/n_slender/items/9cd77a37935e2e390789)



### Gmail

#### Googleアカウントの2段階認証

smtpサーバーにsmtp.gmail.comを使用してメール送信を行う場合、指定するGoogleアカウントが2段階認証プロセスを適用していると下記のエラーメッセージを出力してメール送信に失敗します。
この場合はそのGoogleアカウントでアプリパスワードを取得し、そのパスワードを使用することでメール送信ができるようになります。

```text
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へのログイン"メニューを選択し"アプリ パスワード"をクリックします。

![g01.png](https://qiita-image-store.s3.amazonaws.com/0/22772/e079c456-33e1-113f-06d1-6570d138d842.png)

アプリパスワードの用途をメニューから選ぶか、任意の名前を付けてパスワードを作成します。

![g02.png](https://qiita-image-store.s3.amazonaws.com/0/22772/ca9520cf-4176-038e-c28a-095fd0a4a8dd.png)

図の通り(モザイクをかけています)パスワードが作成されるので、このパスワードをgoogleアカウントのパスワードとして使用します。

![g03.png](https://qiita-image-store.s3.amazonaws.com/0/22772/7d4f87b1-456a-9b3e-00f9-8a8b132a3486.png)

作成したパスワードはいつでも取り消すことができます。

![g04.png](https://qiita-image-store.s3.amazonaws.com/0/22772/6ef5af69-66c2-799c-02c1-7314b8586734.png)


#### メール送信に使用するポート

**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で規定する。
> 
> [STARTTLS - Wikipedia] (https://ja.wikipedia.org/wiki/STARTTLS)


### 参考になったstackoverflow

* [Spring Boot 1.2.5.RELEASE - Sending E-mail via Gmail SMTP] (http://stackoverflow.com/questions/31721298/spring-boot-1-2-5-release-sending-e-mail-via-gmail-smtp)

```
spring.mail.properties.mail.smtp.ssl.enable = true
```

* [Unable to Send Rich Text Email - Thymeleaf + Spring 4] (http://stackoverflow.com/questions/30986717/unable-to-send-rich-text-email-thymeleaf-spring-4)

* [What is the difference between ports 465 and 587?] (http://stackoverflow.com/questions/15796530/what-is-the-difference-between-ports-465-and-587)