@kikutaro_さんがSendGriderになられたのを祝って(?)、自分もSendGridをCloud Foundryで使ってみます。
Pivotal Web ServicesではMarketplaceにSendGridサービスが用意されており、SendGridのアカウントを作らなくてもcf create-service
でアカウントを払い出すことができます。
ただし、残念なことに現状Send Gridのサービスインスタンスから渡されるクレデンシャル情報は
hostname
username
password
であり、API Keyが渡ってこないため、PWSのSendGridサービスではWeb API V2とSMTP APIのみ利用可能です。Web API V3は利用できません。 (PWSのSendGridサービスを使わず、普通にAPI Keyを払い出せばV3 APIももちろん使えます。)
V2 APIやSMTP APIでも十分なケースは多いのと思うので、それぞれをJavaアプリ(主にSpring Boot)で使う方法を紹介します。
Web API V2を使う
まずは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
の中に
{
"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
を取りに行くのが基本的な使い方です。
次に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クライアントライブラリを追加します。
<dependency>
<groupId>com.sendgrid</groupId>
<artifactId>sendgrid-java</artifactId>
<version>2.2.2</version>
</dependency>
HelloSendgridApplication
クラスにSendGridを使うコードを追記します。
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
を作成します。
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
}
しばらくするとメールが無事送信されました。簡単ですね。
次にSpring Boot限定の設定方法を紹介します。Spring Bootでは自動で環境変数VCAP_SERVICES
をパースし次のようなフラットなプロパティとして扱えるようになっています。
demo-sendgrid
サービスインスタンスのusername
、password
はvcap.services.demo-sendgrid.credentials.username
、vcap.services.demo-sendgrid.credentials.password
というプロパティで取得可能です。
したがって、SendGrid
インスタンスの作成は次のようにシンプルになります。
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を使う
次にSMTP APIを使ってみます。この場合はSendGridクライアントライブラリは不要でJavaMail API(をラップしたSpringのMainSender
)を使います。
com.sendgrid:sendgrid-java:2.2.2
の<dependency>
を削除して、次の<dependency>
を追加します。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
MailSender
を使うようにアプリケーションを修正します。
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
に次の設定を行ってください。
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
にローカル用の設定を書いておけば環境ごとの設定切り替えが自動で行われて便利です。
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を利用すると環境変数VCAP_SERVICESから接続情報を取得してデータアクセス必要なオブジェクト(RDBMSの場合はDataSource
、Redisの場合はRedisConnectionFactory
)を環境に依存せずに簡単に定義できます。SMTPも対応しています。
次の<dependency>
を追加します。
<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
クラスに行います。
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
が含まれているかどうか等です。
後はビルドしてcf push
./mvnw clean package -DskipTests=true
cf push
Web API V3を使う
折角なのでV3 APIを使う方法も紹介します。こちらはAPI Keyが必要なので、もはやPWSのSendGridサービスインスタンスは不要です。 アプリケーションんからアンバインドします。
cf unbind-service hello-sendgrid demo-sendgrid
spring-boot-starter-mail
やspring-cloud-*-connector
の<dependency>
も不要で、代わりにV3 API用のクライアントライブラリを追加します。
<dependency>
<groupId>com.sendgrid</groupId>
<artifactId>sendgrid-java</artifactId>
<version>3.1.0</version>
</dependency>
V3 API用のクライアントライブラリを使うようにアプリケーションを修正します。API Keyは環境変数SENDGRID_API_KEY
に渡すことにします。
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は公式サイトか代理店サイトでアカウントを作成して取得してください。
追記
@making あ、ちなみにAPI Keyですが、Pivotal経由でもmanageからSendGridの管理画面いけるんで、そこで手にはいりますよー pic.twitter.com/xm9vJL4eOS
— キクタロー (@kikutaro_) October 16, 2016
manifest.yml
を次のように変更します。
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資料を見て色々試してください。 拙著「はじめてのSpring Boot [改訂版] 」の4章でも学べます。