Gradle で Multi-project を作成する ( 番外編 )( Spring Actuator を利用してアプリ起動時にメールサーバに接続できない場合には起動を中断させる )
概要
記事一覧はこちらです。
本編とは全く関係ありません。。。
何となく思いついたことで Spring Actuator を入れると DB サーバやメールサーバの UP/DOWN を検知できますが、起動時にサーバに接続できなかったら起動を中断させることができたりするのかな?、と思って調べた時の内容です。
結論としては HealthChecker の実装に依存しますが、メールサーバは可能でした。
参照したサイト・書籍
- Programmatically shut down Spring Boot application
https://stackoverflow.com/questions/22944144/programmatically-shut-down-spring-boot-application
目次
- 動作確認のための demo プロジェクトを作成する
- Spring Actuator でメールサーバをチェックするよう設定する
- メールサーバを起動せずに demo アプリを起動してみる
- 起動時にメールサーバに接続できなかったら起動を中断するための CustomMailHealthIndicator クラスを作成する
- 動作確認
手順
動作確認のための demo プロジェクトを作成する
IntelliJ IDEA から Spring Initializr を利用して demo プロジェクトを作成します。
※DevTools、Web、Mail、Actuator をチェックします。
IntelliJ IDEA のメイン画面が表示されたら、lombok の @Slf4j
アノテーションを使用したいので build.gradle の dependencies に lombok の依存関係を追加します。
plugins { id 'org.springframework.boot' version '2.1.4.RELEASE' id 'java' } apply plugin: 'io.spring.dependency-management' group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '11' configurations { // annotationProcessor と testAnnotationProcessor、compileOnly と testCompileOnly を併記不要にする testAnnotationProcessor.extendsFrom annotationProcessor testImplementation.extendsFrom compileOnly } repositories { mavenCentral() } dependencies { def lombokVersion = "1.18.6" implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'org.springframework.boot:spring-boot-starter-mail' implementation 'org.springframework.boot:spring-boot-starter-web' runtimeOnly 'org.springframework.boot:spring-boot-devtools' testImplementation 'org.springframework.boot:spring-boot-starter-test' // for lombok // testAnnotationProcessor、testCompileOnly を併記しなくてよいよう configurations で設定している annotationProcessor("org.projectlombok:lombok:${lombokVersion}") compileOnly("org.projectlombok:lombok:${lombokVersion}") }
変更後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
Spring Actuator でメールサーバをチェックするよう設定する
Spring Actuator でメールサーバの health チェックを行うために application.properties に spring.mail.host=localhost
の設定を追加します。
spring.mail.host=localhost
メールサーバを起動せずに demo アプリを起動してみる
メールサーバを起動しない状態で demo アプリを起動すると、Spring Actuator の MailHealthIndicator クラスから警告ログが出力されますがアプリは終了せず起動したままです。
Health チェックの画面では mail が黄色アイコンで表示されています。
起動時にメールサーバに接続できなかったら起動を中断するための CustomMailHealthIndicator クラスを作成する
警告ログを出力している org.springframework.boot.actuate.mail.MailHealthIndicator クラスを見ると以下のように実装されています。
実際に health チェックしているのが doHealthCheck メソッドで、this.mailSender.testConnection();
で接続してみて例外が throw されなければ builder.up();
を呼び出してステータスを UP に変更する、という実装でした。
MailHealthIndicator クラスには @Component
等のアノテーションが付与されていないので Bean を定義しているクラスを探したところ、org.springframework.boot.actuate.autoconfigure.mail.MailHealthIndicatorAutoConfiguration クラスで定義されていました。
mailHealthIndicator という名前の Bean が存在しない時だけ MailHealthIndicatorAutoConfiguration クラスで Bean を生成しています。
MailHealthIndicator クラスを継承して CustomMailHealthIndicator クラスを作成し、一番最初の health チェックで例外が throw されたら Web アプリを強制終了させるようにしてみます。src/main/java/com/example/demo/CustomMailHealthIndicator.java クラスを新規作成した後、以下の内容を記述します。
package com.example.demo; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ExitCodeGenerator; import org.springframework.boot.SpringApplication; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.mail.MailHealthIndicator; import org.springframework.context.ApplicationContext; import org.springframework.mail.javamail.JavaMailSenderImpl; import java.util.concurrent.atomic.AtomicBoolean; @Slf4j @Component("mailHealthIndicator") public class CustomMailHealthIndicator extends MailHealthIndicator { private final ApplicationContext context; private final AtomicBoolean isFirstTime = new AtomicBoolean(true); public CustomMailHealthIndicator(JavaMailSenderImpl mailSender, ApplicationContext context) { super(mailSender); this.context = context; } @Override protected void doHealthCheck(Health.Builder builder) throws Exception { try { super.doHealthCheck(builder); } catch (Exception e) { // 一番最初の health チェックでメールサーバに接続できなかった時にはアプリを強制終了させる if (isFirstTime.get()) { log.error("メールサーバが起動していないので強制終了します", e); int exitCode = SpringApplication.exit(context, (ExitCodeGenerator) () -> 1); System.exit(exitCode); } throw e; } finally { isFirstTime.compareAndExchange(true, false); } } }
動作確認
先程と同様にメールサーバを起動しない状態で demo アプリを起動すると、最後に Process finished with exit code 1
のメッセージが出力されてアプリが終了しました。
メールサーバ(smtp4dev)を起動してから demo アプリを起動すると、アプリは終了せずに起動したままとなり、
メールサーバを終了させると、health チェックのエラーログが出力されますがアプリは終了せずに起動したままでした。
想定通りの動きです。
履歴
2019/04/27
初版発行。