SendGridのメール送信状況をJava&WebSocketでモニタリングする

SendGridサポートエンジニアの菊田(@kikutaro_)です。

先週はJava Day Tokyo 2017JJUG CCC 2017 SpringとJavaの大きなイベント2つに参加しました。今年はJava SE 9とJava EE 8がリリース予定ということもあって、新しい機能に関するセッションも多く、とても楽しかったです。

学んだことは実際に手を動かしてみるのが一番なので、本日はJavaの新しい機能を使って、SendGridのメール送信状況をモニタリングする仕組みを作ってみたいと思います。

実は少し前のブログでもJavaの新しい機能を利用したエントリを書いていて、そのときはJava SE 9で入るJShellを使いました。対話的にSendGridのJavaライブラリを利用する方法を紹介していますが、これはどんなJavaライブラリでも同様のことができて、手軽に動作検証ができるので、是非ご確認ください。

JShellからSendGridでメール送信する

今回はJava EE 8の新しい機能を使ってみたいと思います。Java SE同様に様々な追加機能がありますが、その中からJSF(JavaServer Faces)におけるWebSocket対応の新機能を試してみます。

WebSocketはクライアントとサーバ間で双方向通信をするためのプロトコルです。現在最新のJava EE 7でもWebSocket対応は行われていますが、Java EEのWebプレゼンテーション層を構築するJSFでは、PrimeFacesなどのサードパーティライブラリを利用しないと扱いにくいものでした。しかし、Java EE 8では、JSFで新たに<f:websocket>タグ定義が追加されて、WebSocketが簡単に利用できるようになっています。今回の挙動確認はGlassFish 5のPromoted Buildを利用しました。

SendGridではメールの送信状況をWebhookで外部にPOSTするEvent Webhook機能があるので、これと組み合わせることで、複数の人がリアルタイムにメール送信状況をモニタリングできるようになります。下図のようなイメージです。

SendGrid Event Webhook with WebSocket on Java EE
もちろんSendGrid標準の機能でも、Activityの画面でメール送信状況を確認することができます。ただし、Activityの表示は1週間までなので、長期的にログを蓄積する意味でもEvent Webhookの利用は非常に重要です。

また、Event Webhookを利用して簡単にモニタリングする1つの手段として、RequestBinのようなサービスを利用することもできます。ただし、RequestBinではブラウザをリロードをしないと最新情報が取得できないので、WebSocketやHTTP/2のServer-Sent Eventsなどでプッシュ式に配信するほうが便利です。

簡単なプログラムですが、実行した結果は以下の通りです。

Demo SendGrid Event Webhook with WebSocket on Java EE

ここでは実際のメール送信ではなく、Event Webhookの設定画面から利用できる接続テストツール(Integration Testing Tool)を利用しています。3つの異なるブラウザ(右上がChrome、真ん中がEdge、右下がFirefox)でリロードすることなくイベント情報が表示されていることが確認できます。便利ですよね。

コードはGitHubにアップしていますが、1点だけ注意があります。メインとなるコードは次のような定義ですが、Java EEをご存知の方々からは少し物議を醸しそうなコードかもしれません。

@Path("webhook")
@Named
@ApplicationScoped
public class MonitoringBean implements Serializable {
    
    //WebSocket
    @Inject
    @Push(channel = "sendgrid")
    private PushContext push;


    //SendGridのEvent Webhookを受信
    @POST
    public void post(String body) {
        Gson gson = new Gson();
        Event[] events = gson.fromJson(body, Event[].class);
        monitor(events);
    }

    public void startMonitor(){
        push.send("start monitor");
    }
 
    //WebSocketでクライアントへSendGridのEvent情報を表示加工してプッシュする
    public void monitor(Event[] events) {
        SimpleDateFormat df = new SimpleDateFormat("YYYY/MM/dd HH:mm:ss");
        Arrays.stream(events).forEach(e -> 
                push.send(
                        df.format(e.retriveTimestamp()) + "|" +
                        e.getEmail() + "|" +
                        e.getEvent() + "|" +
                        e.getSg_message_id()));
    }
}

というのも、今回のサンプルでは、SendGridから飛んでくるPOSTはJavaでREST APIを組むためのJAX-RSを利用し、そこからJSFのWebSocketへ繋げるため、JSFとJAX-RSを同梱したコードとなっているためです。この辺り、どうあるべきか、は是非、@kikutaro_までご意見いただけると嬉しいです。

今回はJava EEでWebSocketを利用する例でしたが、WebSocketはプログラミング言語に依存するものではないため、PHPやRubyなどでも同様のことが可能です。もう既に「そういうの作って実践してるよ!」という方もいらっしゃれば是非事例として情報発信していただければと思います!

なお、今回のコードは以下サイトを参考とさせていただいています。