---
title: SendGrid x Spring BootをPivotal Web Services(Cloud Foundry)で試す
tags: ["Cloud Foundry", "Pivotal Web Services", "SendGrid", "SMTP", "Spring Boot", "Java"]
categories: ["Service", "PaaS", "CloudFoundry"]
date: 2016-10-15T19:27:10Z
updated: 2016-10-16T08:22:16Z
---

[@kikutaro_](https://twitter.com/kikutaro_)さんがSendGriderになられたのを祝って(?)、自分もSendGridをCloud Foundryで使ってみます。

[Pivotal Web Services](https://run.pivotal.io/)ではMarketplaceに[SendGridサービス](https://docs.run.pivotal.io/marketplace/services/sendgrid.html)が用意されており、SendGridのアカウントを作らなくても`cf create-service`でアカウントを払い出すことができます。

ただし、残念なことに現状Send Gridのサービスインスタンスから渡されるクレデンシャル情報は

* `hostname`
* `username`
* `password`

であり、API Keyが渡ってこないため、PWSのSendGridサービスでは[Web API V2](https://sendgrid.com/docs/Integrate/Code_Examples/v2_Mail/index.html)と[SMTP API](https://sendgrid.com/docs/Integrate/Code_Examples/SMTP_API_Header_Examples/index.html)のみ利用可能です。[Web API V3](https://sendgrid.com/docs/Integrate/Code_Examples/v3_Mail/index.html)は利用できません。
(PWSのSendGridサービスを使わず、普通にAPI Keyを払い出せばV3 APIももちろん使えます。)

V2 APIやSMTP APIでも十分なケースは多いのと思うので、それぞれをJavaアプリ(主にSpring Boot)で使う方法を紹介します。

### [Web API V2](https://sendgrid.com/docs/Integrate/Code_Examples/v2_Mail/index.html)を使う

まずはSendGridサービスインスタンスを作成する。`cf marketplace`で`sendgrid`サービスのプランを確認すると、`free`プランが無償で使えることがわかリマす。

```
$ cf marketplace | grep sendgrid
sendgrid         free, bronze*, silver*                                                               Email Delivery. Simplified.
```

`sendgrid`サービスの`free`プランのサービスインスタンスを`demo-sendgrid`という名前で作成します。

```
cf create-service sendgrid free demo-sendgrid
```

後でこのサービスインスタンスをアプリケーションにバインドしますが、バインドするとアプリケーションには環境変数`VCAP_SERVICES`の中に

``` json
{
  "sendgrid": [
   {
    "credentials": {
     "hostname": "smtp.sendgrid.net",
     "password": "zqaT47p6zdx30890",
     "username": "e6g8xCyB29"
    },
    "label": "sendgrid",
    "name": "demo-sendgrid",
    "plan": "free",
    "provider": null,
    "syslog_drain_url": null,
    "tags": [
     "iPhone",
     "Web-based",
     "Email",
     "smtp",
     "Analytics",
     "Mac",
     "Developer Tools",
     "Retail",
     "Inventory management",
     "iPad",
     "BI \u0026 Analytics",
     "Email Delivery",
     "Communication"
    ],
    "volume_mounts": []
   }
  ]
 }
```

というJSON形式で接続情報が渡ります。このJSONをパースして`credentials`を取りに行くのが基本的な使い方です。

<iframe src="//www.slideshare.net/slideshow/embed_code/key/zQuFUBNeHbGyHV?startSlide=67" width="595" height="485" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe> <div style="margin-bottom:5px"> <strong> <a href="//www.slideshare.net/makingx/cloud-foundry-hackt-hacktk" title="今すぐ始めるCloud Foundry #hackt #hackt_k" target="_blank">今すぐ始めるCloud Foundry #hackt #hackt_k</a> </strong> from <strong><a target="_blank" href="//www.slideshare.net/makingx">Toshiaki Maki</a></strong> </div>

次にSpring Bootの雛形を作成します。

```
$ curl start.spring.io/starter.tgz \
       -d artifactId=hello-sendgrid \
       -d baseDir=hello-sendgrid \
       -d dependencies=web \
       -d applicationName=HelloSendgridApplication | tar -xzvf -
```

`pom.xml`にV2 API用のJavaクライアントライブラリを追加します。

``` xml
		<dependency>
			<groupId>com.sendgrid</groupId>
			<artifactId>sendgrid-java</artifactId>
			<version>2.2.2</version>
		</dependency>
```

`HelloSendgridApplication`クラスにSendGridを使うコードを追記します。

``` java
package com.example;

import java.io.IOException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sendgrid.SendGrid;
import com.sendgrid.SendGridException;

@SpringBootApplication
@RestController
public class HelloSendgridApplication {

	public static void main(String[] args) {
		SpringApplication.run(HelloSendgridApplication.class, args);
	}

	@Autowired
	SendGrid sendGrid;

	@GetMapping
	SendGrid.Response send(@RequestParam String message) throws SendGridException {
		SendGrid.Email email = new SendGrid.Email()
				.addTo("makingx@example.com")
				.setFrom("other@example.com")
				.setSubject("Hello World")
				.setText(message);
		return sendGrid.send(email);
	}

	@Bean
	SendGrid sendGrid(ObjectMapper objectMapper) throws IOException {
		JsonNode credentials = objectMapper
				.readValue(System.getenv("VCAP_SERVICES"), JsonNode.class).get("sendgrid")
				.get(0).get("credentials");
		String username = credentials.get("username").asText();
		String password = credentials.get("password").asText();
		return new SendGrid(username, password);
	}

}
```


今回はCloud Foundry独自の方法を使わず、素直にJacksonで`VCAP_SERVICES`をパースして、`demo-sendgrid`サービスインスタンスの`username`と`password`を取得しました。この方法はSpring BootだけでなくどのJavaアプリでも使えます。


このアプリケーションをデプロイするための`manifest.yml`を作成します。

``` yaml
applications:
- name: hello-sendgrid
  memory: 256m
  buildpack: java_buildpack
  path: ./target/hello-sendgrid-0.0.1-SNAPSHOT.jar
  services:
  - demo-sendgrid
```

後はビルドして`cf push`

```
./mvnw clean package -DskipTests=true
cf push
```

何か送ってみます。

```
$ curl "https://hello-sendgrid.cfapps.io?message=💩" | jq .

{
  "code": 200,
  "message": "{\"message\":\"success\"}",
  "status": true
}
```

しばらくするとメールが無事送信されました。簡単ですね。

![image](https://qiita-image-store.s3.amazonaws.com/0/1852/5afdc77e-29ce-aa5e-c683-810937e2717e.png)

次にSpring Boot限定の設定方法を紹介します。Spring Bootでは自動で環境変数`VCAP_SERVICES`をパースし次のようなフラットなプロパティとして扱えるようになっています。

![image](https://qiita-image-store.s3.amazonaws.com/0/1852/20a0b813-c874-d220-124b-c9d1279d3a79.png)

`demo-sendgrid`サービスインスタンスの`username`、`password`は`vcap.services.demo-sendgrid.credentials.username`、`vcap.services.demo-sendgrid.credentials.password`というプロパティで取得可能です。

したがって、`SendGrid`インスタンスの作成は次のようにシンプルになります。


``` java
package com.example;

import java.io.IOException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.sendgrid.SendGrid;
import com.sendgrid.SendGridException;

@SpringBootApplication
@RestController
public class HelloSendgridApplication {

	public static void main(String[] args) {
		SpringApplication.run(HelloSendgridApplication.class, args);
	}

	@Autowired
	SendGrid sendGrid;

	@GetMapping
	SendGrid.Response send(@RequestParam String message) throws SendGridException {
		SendGrid.Email email = new SendGrid.Email()
				.addTo("makingx@example.com")
				.setFrom("other@example.com")
				.setSubject("Hello World")
				.setText(message);
		return sendGrid.send(email);
	}

	@Bean
	SendGrid sendGrid(
			@Value("${vcap.services.demo-sendgrid.credentials.username}") String username,
			@Value("${vcap.services.demo-sendgrid.credentials.password}") String password)
			throws IOException {
		return new SendGrid(username, password);
	}

}
```

後はビルドして`cf push`

```
./mvnw clean package -DskipTests=true
cf push
```

### [SMTP API](https://sendgrid.com/docs/Integrate/Code_Examples/SMTP_API_Header_Examples/index.html)を使う

次にSMTP APIを使ってみます。この場合はSendGridクライアントライブラリは不要でJavaMail API(をラップしたSpringの`MainSender`)を使います。

`com.sendgrid:sendgrid-java:2.2.2`の`<dependency>`を削除して、次の`<dependency>`を追加します。

``` xml
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-mail</artifactId>
		</dependency>
```

`MailSender`を使うようにアプリケーションを修正します。

``` java
package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class HelloSendgridApplication {

	public static void main(String[] args) {
		SpringApplication.run(HelloSendgridApplication.class, args);
	}

	@Autowired
	MailSender mailSender;

	@GetMapping
	void send(@RequestParam String message) {
		SimpleMailMessage email = new SimpleMailMessage();
		email.setTo("makingx@example.com");
		email.setFrom("other@example.com");
		email.setSubject("Hello World");
		email.setText(message);
		mailSender.send(email);
	}
}
```

Spring BootのSMTP AutoConfigureを使うには次のプロパティを定義すれば良いです。

* `spring.mail.host`
* `spring.mail.username`
* `spring.mail.password`

`application-cloud.properties`に次の設定を行ってください。

``` properties
spring.mail.host=${vcap.services.demo-sendgrid.credentials.hostname}
spring.mail.username=${vcap.services.demo-sendgrid.credentials.username}
spring.mail.password=${vcap.services.demo-sendgrid.credentials.password}
```

`VCAP_SERVICES`の値を設定しています。Cloud FoundryにSpring Bootアプリケーションをデプロイすると`cloud`プロファイルが適用されるため、`application-cloud.properties`に書いたプロパティはCloud Foundryにデプロイされた時だけ有効になります。ローカル環境で試す時は`application.properties`にローカル用の設定を書いておけば環境ごとの設定切り替えが自動で行われて便利です。

``` properties
spring.mail.host=stmp.local
spring.mail.username=test@local
spring.mail.password=test
```

後はビルドして`cf push`

```
./mvnw clean package -DskipTests=true
cf push
```

#### Spring Cloud Connectorを使う

[Spring Cloud Connector](http://cloud.spring.io/spring-cloud-connectors/)を利用すると環境変数VCAP_SERVICESから接続情報を取得してデータアクセス必要なオブジェクト（RDBMSの場合は`DataSource`、Redisの場合は`RedisConnectionFactory`）を環境に依存せずに簡単に定義できます。[SMTPも対応しています](http://cloud.spring.io/spring-cloud-connectors/spring-cloud-spring-service-connector.html#_smtp)。

次の`<dependency>`を追加します。

``` xml
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-spring-service-connector</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-cloudfoundry-connector</artifactId>
		</dependency>
```

`MailSender`をクラウド情報から自動で生成する定義を`CloudConfig`クラスに行います。

``` java
package com.example;

import org.springframework.cloud.config.java.AbstractCloudConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.mail.MailSender;

@Profile("cloud")
@Configuration
public class CloudConfig extends AbstractCloudConfig {
    @Bean
	MailSender mailSender() {
		return connectionFactory().service(MailSender.class);
	}
}
```

`application-cloud.properties`は削除してください。SMTPサービスインスタンスが一つしかバインドされていない場合は、サービスインスタンス名の指定は不要です。
ちなみにバインドされたサービスインスタンスがSMTPかどうかの判定は[`tag`に`smtp`が含まれているかどうか等](http://cloud.spring.io/spring-cloud-connectors/spring-cloud-cloud-foundry-connector.html#_smtp)です。

後はビルドして`cf push`

```
./mvnw clean package -DskipTests=true
cf push
```


### [Web API V3](https://sendgrid.com/docs/Integrate/Code_Examples/v3_Mail/index.html)を使う

折角なのでV3 APIを使う方法も紹介します。こちらはAPI Keyが必要なので、もはやPWSのSendGridサービスインスタンスは不要です。
アプリケーションんからアンバインドします。

```
cf unbind-service hello-sendgrid demo-sendgrid
```

`spring-boot-starter-mail`や`spring-cloud-*-connector`の`<dependency>`も不要で、代わりにV3 API用のクライアントライブラリを追加します。


``` xml
		<dependency>
			<groupId>com.sendgrid</groupId>
			<artifactId>sendgrid-java</artifactId>
			<version>3.1.0</version>
		</dependency>
```

V3 API用のクライアントライブラリを使うようにアプリケーションを修正します。API Keyは環境変数`SENDGRID_API_KEY`に渡すことにします。

``` java
package com.example;

import java.io.IOException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.sendgrid.*;

@SpringBootApplication
@RestController
public class HelloSendgridApplication {

	public static void main(String[] args) {
		SpringApplication.run(HelloSendgridApplication.class, args);
	}

	@Autowired
	SendGrid sendGrid;

	@GetMapping
	Response send(@RequestParam String message) throws IOException {
		Email from = new Email("other@example.com");
		String subject = "Hello World";
		Email to = new Email("makingx@example.com");
		Content content = new Content("text/plain", message);
		Mail mail = new Mail(from, subject, to, content);
		Request request = new Request();
		request.method = Method.POST;
		request.endpoint = "mail/send";
		request.body = mail.build();
		return sendGrid.api(request);
	}

	@Bean
	SendGrid sendGrid() {
		return new SendGrid(System.getenv("SENDGRID_API_KEY"));
	}
}
```

API Keyは[公式サイト](https://sendgrid.com/)か[代理店サイト](https://sendgrid.kke.co.jp/)でアカウントを作成して取得してください。

**追記**

<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="ja" dir="ltr"><a href="https://twitter.com/making">@making</a> あ、ちなみにAPI Keyですが、Pivotal経由でもmanageからSendGridの管理画面いけるんで、そこで手にはいりますよー <a href="https://t.co/xm9vJL4eOS">pic.twitter.com/xm9vJL4eOS</a></p>&mdash; キクタロー (@kikutaro_) <a href="https://twitter.com/kikutaro_/status/787501154053742593">October 16, 2016</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

`manifest.yml`を次のように変更します。

``` yaml
applications:
- name: hello-sendgrid
  memory: 256m
  buildpack: java_buildpack
  path: ./target/hello-sendgrid-0.0.1-SNAPSHOT.jar
  env:
    SENDGRID_API_KEY: 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
```

後はビルドして`cf push`

```
./mvnw clean package -DskipTests=true
cf push
```

---

☝️のアプリをベースにSendGridで色々遊んでみてください。
(ちなみにPivotal Web Servicesからの通知メールにもSendGridが使われています。)

また、Cloud Foundryをもう少し触りたくなったら[Workshop資料](https://github.com/Pivotal-Japan/cf-workshop)を見て色々試してください。
拙著「[はじめてのSpring Boot [改訂版] ](http://bit.ly/hajiboot2)」の4章でも学べます。

<a href="http://www.amazon.co.jp/exec/obidos/ASIN/4777519694/ikam-22/ref=nosim/" name="amazletlink" target="_blank"><img src="http://ecx.images-amazon.com/images/I/51MBlgJ0pTL._SL160_.jpg" alt="はじめてのSpring Boot―スプリング・フレームワークで簡単Javaアプリ開発 (I・O BOOKS)" style="border: none;" /></a>
