Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その2 )( Gradle を 3.5 → 4.10 にバージョンアップする )
概要
記事一覧はこちらです。
Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その1 )( 概要 ) の続きです。
- 今回の手順で確認できるのは以下の内容です。
参照したサイト・書籍
目次
手順
2.0.x ブランチの作成
master から 2.0.x ブランチを、2.0.x から feature/132-issue ブランチを作成します。
gradlew wrapper --gradle-version=4.10
コマンドを実行する
build.gradle の wrapper タスクの記述を以下のように変更します。
wrapper {
gradleVersion = "4.10"
distributionType = Wrapper.DistributionType.ALL
}
task wrapper(type: Wrapper) { gradleVersion = '3.5' }
→wrapper { gradleVersion = "4.10" ... }
に変更します。
gradle/wrapper/gradle-wrapper.properties を以下のように変更します。
#Tue May 30 00:33:27 JST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-bin.zip
gradle-3.5-bin.zip
→gradle-4.10-bin.zip
に変更します。
コマンドプロンプトから gradlew wrapper --gradle-version=4.10
コマンドを実行します。最後に「'_CONSOLE' は、内部コマンドまたは外部コマンド、操作可能なプログラムまたはバッチ ファイルとして認識されていません。」というメッセージが出ますが、無視して構いません。
gradlew --version
コマンドを実行して 4.10 にバージョンアップしていることを確認します。
build.gradle を修正する
Spring Boot + npm + Geb で入力フォームを作ってテストする ( その53 )( Gradle を 3.5 → 4.6 へバージョンアップする ) の時の内容を参考に、tasks.withType(FindBugs) { ... }
の中に記述していた doFirst { ... }
の記述を削除し、以下のように変更します。
tasks.withType(FindBugs) { reports { xml.enabled = false html.enabled = true } }
変更後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
動作確認
clean タスク実行 → Rebuild Project 実行 → build タスクを実行して、"BUILD SUCCESSFUL" のメッセージが出力されることを確認します。
履歴
2018/08/29
初版発行。
Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その1 )( 概要 )
概要
記事一覧はこちらです。
- 「Spring Boot で書籍の貸出状況確認・貸出申請する Web アプリケーションを作る」で作成した Web アプリケーション ( ksbysample-webapp-lending ) の Spring Boot のバージョンを 1.5.4 → 2.0.8 へバージョンアップします。
- 進め方は以下の方針とします。
- Git のブランチは 2.0.x を作成して、そちらで作業します。Spring Boot のバージョンと合わせます。
- 最初に Gradle のバージョンを 3.5 → 4.x へバージョンアップします。
- 次に Spring Boot のバージョン番号を 2.0.8 にします。
- Spring Initializr で 2.0.x のプロジェクトを作成して、修正した方がよさそうな点があれば反映します。
- Spring IO Platform は 9 April 2019 に EOL を迎えるので、Spring Boot の BOM を利用するように変更します。
- ライブラリは最新バージョンにアップデートします。
- build.gradle の記述についても、プラグインの書き方を
apply plugin: ...
から plugins block に変更したり、それ意外にも build.gradle の書き方で変更した方が良い点を変更します。
- プロジェクトを build し直してエラーが出る点があれば修正し、まずはここまでで動くようにします。
- その後で 2.0 系ではこう書くべきという点があるか確認し、変更した方がよいところを修正します。
- 以下の対応もする予定です。
2.0 の Release Notes、Migration Guide はこちらです。
Spring Boot 2.0 Release Notes
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes
Spring Boot 2.0 Migration Guide
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Migration-Guide
履歴
2018/08/29
初版発行。
2018/10/16
* Spring Boot 2.0 Migration Guide のリンクを追加しました。
2019/02/06
* Spring Boot のバージョンアップ後のバージョンを 2.0.4 → 2.0.8 に修正しました。
Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( 大目次 )
- その1 ( 概要 )
- その2 ( Gradle を 3.5 → 4.10 にバージョンアップする )
- その3 ( build.gradle を変更する )
- その4 ( AbstractJsonpResponseBodyAdvice を削除し、失敗しているテストを成功させる )
- その5 ( checkstyle を 7.8.1 → 8.12 に、PMD を 5.8.1 → 6.7.0 にバージョンアップする )
- その6 ( FindBugs 3.0.1 → SpotBugs 3.1.7 に切り替える )
- その7 ( src/main/resources/static の下の css や js ファイルにアクセスできない原因とは? )
- その8 ( 一旦動作確認し、動作しない点があれば修正する )
- その9 ( gradle-errorprone-plugin を 0.0.16 → 0.6 にバージョンアップ。。。しようと思いましたが止めました )
- その10 ( Tomcat connection Pool → HikariCP に変更する )
- その11 ( HikariCP のコネクションプーリングの情報を JMX で取得できるようにする )
- その12 ( Spring Boot Actuator を導入する )
- その13 ( Remember Me 認証が使えなくなっていたので調査・修正する )
- その14 ( Docker で Prometheus+Grafana の環境を構築して Spring Actuator で収集したメトリックスを表示する )
- その15 ( Prometheus+Grafana メモ書き )
- その16 ( Gradle を 4.10 → 4.10.2 へ、Spring Boot を 2.0.4 → 2.0.6 へバージョンアップする )
- その17 ( Spock を 1.1-groovy-2.4 → 1.2-groovy-2.5 へバージョンアップする )
- その18 ( Docker で Redis の環境を構築する(単体サーバ構成)+Spring Actuator の Endpoint の Basic 認証ではセッション情報を生成しないようにする )
- その19 ( Docker で Redis の環境を構築する2(Redis Cluster 構成))
- その20 ( Docker で Redis の環境を構築する3(Redis を 5.0.1 → 5.0.2 にバージョンアップする+.env の環境変数を使用するよう変更する))
- その21 ( Docker で RabbitMQ の環境を構築する )
- その22 ( Docker で RabbitMQ の環境を構築する2(RabbitMQ の Clustering 構成) )
- その23 ( Docker Network メモ書き+Prometheus の HTTP API でデータを削除する )
- 番外編 ( gradle-docker-compose-plugin で test の前に自動で Docker コンテナを起動してみる )
- 番外編 ( docker volume メモ書き )
- その24 ( Docker で PostreSQL+pgAdmin4+ Flyway の環境を構築する )
- その25 ( Docker で PostreSQL+pgAdmin4+ Flyway の環境を構築する2 )
- その26 ( Gradle を 4.10.2 → 4.10.3 へ、Spring Boot を 2.0.6 → 2.0.7 へバージョンアップする。。。が Spring Security の bug のため Spring Boot は 2.0.6 へ戻す )
- その27 ( ProviderManager#getProviders が DaoAuthenticationProvider を3つ返す原因を調査する )
- その28 ( Docker で SMTPサーバ+Webmailクライアント環境を構築する )
- その29 ( build.gradle の dependencies から不要な記述を削除する )
- その30 ( Redis のクライアントライブラリを Jedis → Lettuce に変更する )
- その31 ( Spring Actuator の Basic 認証用ユーザの認証成功時には AuthenticationSuccessEvent イベントが発生しないようにする+いろいろ調整する )
- その32 ( Spring Boot を 2.0.6 → 2.0.8 へバージョンアップする )
- その33 ( PC の IP アドレスが変更された時に修正するファイルを最小限にする )
- その34 ( Docker で複数の Tomcat を起動して動作確認する )
- その35 ( Docker で起動しているサーバの TimeZone を Asia/Tokyo に変更する )
- 番外編 ( docker logs メモ書き )
- その36 ( Windows のサービスから起動して動作確認する )
- 感想
Spring Boot + Spring Integration でいろいろ試してみる ( その25 )( Docker Compose でサーバを構築する、SMTP+POP3サーバ編 )
概要
記事一覧はこちらです。
Spring Integration のアプリケーションで使用するサーバを Docker Compose で構築します。
- SMTPサーバ+POP3サーバを構築します。
- Dockerイメージは tvial/docker-mailserver を使用します。選定理由は以下の通りです。
- SMTPサーバは認証なし、SSLなし、ポート番号は25番を使用。
- POP3サーバは認証あり、SSLなし、ポート番号は110番を使用。
- 認証に使用するパスワードは、サーバ側で PLAIN TEXT で保存します。
参照したサイト・書籍
Spring Integration Reference Manual - 22. Mail Support
https://docs.spring.io/spring-integration/docs/5.0.7.RELEASE/reference/html/mail.htmltvial/docker-mailserver(Docker Hub のページ)
https://hub.docker.com/r/tvial/docker-mailserver/tomav/docker-mailserver(GitHub のページ)
https://github.com/tomav/docker-mailservertomav/docker-mailserver - Wiki
https://github.com/tomav/docker-mailserver/wiki- docker-mailserver の設定方法は Wiki に記載されています。
Docker for Windows で postgres コンテナの Volume マウントを安全にする
https://qiita.com/megmogmog1965/items/e7cd4500006c3b6b1894
目次
- ksbysample-eipapp-dockerserver プロジェクトを作成する
- SMTP+POP3 サーバを構築する
- SMTP でメールを送信するサンプルを作成する
- POP3 でメールを受信するサンプルを作成する
手順
ksbysample-eipapp-dockerserver プロジェクトを作成する
Spring Initializr で作成します。
生成された build.gradle は以下のものですが、
buildscript { ext { springBootVersion = '2.0.4.RELEASE' } repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'org.springframework.boot' apply plugin: 'io.spring.dependency-management' group = 'ksbysample' version = '0.0.1-SNAPSHOT' sourceCompatibility = 1.8 repositories { mavenCentral() } dependencies { compile('org.springframework.boot:spring-boot-starter-integration') testCompile('org.springframework.boot:spring-boot-starter-test') }
dependencies block を以下のように変更します。
dependencies { implementation('org.springframework.boot:spring-boot-starter-integration') implementation("org.springframework.boot:spring-boot-starter-mail") implementation('org.springframework.integration:spring-integration-mail') implementation("org.apache.commons:commons-lang3") testImplementation('org.springframework.boot:spring-boot-starter-test') }
compile
→implementation
、testCompile
→testImplementation
に変更します。- Spring Integration の Mail Support の機能を使用するので、以下の行を追加します。
implementation('org.springframework.integration:spring-integration-mail')
- メールを送信するのに JavaMailSender Bean を使用したいので、以下の行を追加します。
implementation("org.springframework.boot:spring-boot-starter-mail")
- StringUtils を使用したいので、以下の行を追加します。
implementation("org.apache.commons:commons-lang3")
build.gradle を変更後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
また @SpringBootApplication
アノテーションが記述されているファイル名が長いので、KsbysampleEipappDockerserverApplication.java → Application.java に変更します。
SMTP+POP3 サーバを構築する
docker-mailserver をカスタマイズする設定ファイルを配置するディレクトリを作成する
プロジェクトの直下に docker/mail-server/config ディレクトリを作成します。
docker-compose.yml を作成する
プロジェクトのルートディレクトリ直下に docker-compose.yml を新規作成し、以下の内容を記述します。
version: '3' services: # docker-mailserver # https://hub.docker.com/r/tvial/docker-mailserver/ # https://github.com/tomav/docker-mailserver # # 起動した docker-mailserver のコンテナ(mail-server) にアクセスする場合には以下のコマンドを実行する # docker exec -it mail-server /bin/sh # # アカウントのパスワードを SHA512 で作成する場合には、コンテナにアクセスして以下のようにコマンドを実行して生成する # doveadm pw -s SHA512-CRYPT -u [メールアドレス(例:tanaka@mail.example.com)] -p [パスワード] # mail-server: image: tvial/docker-mailserver:latest container_name: mail-server hostname: mail domainname: example.com ports: - "25:25" - "110:110" volumes: - ./docker/mail-server/config/:/tmp/docker-mailserver/ environment: # debug したい場合には以下の行のコメントアウトを解除する # - DMS_DEBUG=1 - ENABLE_SPAMASSASSIN=0 - ENABLE_CLAMAV=0 - ENABLE_FETCHMAIL=0 - ENABLE_FAIL2BAN=0 - ENABLE_POSTGREY=0 - ENABLE_POP3=1 cap_add: - NET_ADMIN - SYS_PTRACE restart: always
- コンテナ名は
mail-server
。 - メールのドメイン名は
mail.example.com
。 - https://github.com/tomav/docker-mailserver の docker-compose.yml のサンプルから主に以下の点を変更しています。
- PORT は 25番(SMTP)、110番(POP3)のみ記述しています。
Note: Port 25 is only for receiving email from other mailservers and not for submitting email. You need to use port 465 or 587 for this.
と記述されていますが、465番、587番は SSL, TLS 用のポートなので、今回は SSL なしのサンプルのため 25番を使用します。 - サンプルでは volumes に
maildata:/var/mail
,mailstate:/var/mail-state
が記載されていますが、Windows PC 上のディレクトリにマウントしないことにしたので記述していません。マウントする場合にはdocker volume create
コマンドで maildata, mailstate という名前のボリュームを作成して、サンプルのように記述すればよいはず。 - 開発環境用としてシンプルに SMTP, POP3 を使用したいだけなので、spamassasin, clamav, fetchmail, fail2ban, postgrey は OFF にします。
- POP3 を使用するので
ENABLE_POP3=1
を追加します。
- PORT は 25番(SMTP)、110番(POP3)のみ記述しています。
Postfix をカスタマイズするための postfix-main.cf を作成する
docker/mail-server/config の下に postfix-main.cf を新規作成し、以下の内容を記述します。
mydestination = smtpd_recipient_restrictions =
- mydestination と virtual_mailbox_domains に同じドメインが設定されていると
warning: do not list domain mail.example.com in BOTH mydestination and virtual_mailbox_domains
というログが出るので、mydestination を空にします。 - 既存の smtpd_recipient_restrictions の設定のままではメール送信時に
Helo command rejected: need fully-qualified hostname
というエラーが出るため、空にします。
Dovecot をカスタマイズするための dovecot.cf を作成する
docker/mail-server/config の下に dovecot.cf を新規作成し、以下の内容を記述します。
disable_plaintext_auth = no ssl = no # debug したい場合には以下の行のコメントアウトを解除する # auth_verbose = yes # auth_debug = yes
- SSL をオフにして、かつ PLAIN TEXT のパスワード送信を受け付けるようにするために
disable_plaintext_auth = no
、ssl = no
の設定を入れます。この2つの設定がないと POP3 の認証時にPlaintext authentication disallowed on non-secure (SSL/TLS) connections.
というエラーが発生します。
ユーザを登録するための postfix-accounts.cf を作成する
tanaka@mail.example.com、suzuki@mail.example.com の2ユーザを作成します。docker/mail-server/config の下に postfix-accounts.cf を新規作成し、以下の内容を記述します。
tanaka@mail.example.com|{PLAIN}xxxxxxxx suzuki@mail.example.com|{PLAIN}yyyyyyyy
ここまでの作業で docker ディレクトリは以下の構成になります。
サーバを起動する
コマンドプロンプトを起動し docker-compose.yml のあるディレクトリへ移動して docker-compose up -d
コマンドを実行して起動します。
IntelliJ IDEA の docker plugin を見ると mail-server コンテナが起動していることが確認できます。
動作確認
telnet で接続して SMTP, POP3 コマンドを実行すれば動作確認できるのですが、Windows には標準では telnet コマンドがありません。「Windows の機能の有効化または無効化」で「Telnet クライアント」がインストール可能ですが、インストールしてもなぜかうまく接続できませんでした。Git for Windows の中を見ても telnet コマンドはありませんでした。
Windows 10 では WSL(Windows Subsystem for Linux)という機能で Ubuntu 18.04 がインストールできますので、Ubuntu から telnet で接続して動作確認します(おそらく Windows で telnet を使うのはこれが一番使いやすいです)。WSL や Ubuntu 18.04 のインストール方法はここでは書きません。
まずは telnet localhost 25
で接続してメールを送信します。
コマンドプロンプトで docker exec -it mail-server /bin/sh
コマンドを実行して mail-server コンテナに接続し、/var/mail の下の tanaka@mail.example.com にメールが届いていることを確認します。
最後に telnet localhost 110
で接続して POP3 でメールを受信できることを確認します。
SMTP でメールを送信するサンプルを作成する
Spring Framework のスケジューリング機能を利用して一定時間毎にメールを送信したいので、src/main/java/ksbysample/eipapp/dockerserver/Application.java に @EnableScheduling
アノテーションを付与します。
package ksbysample.eipapp.dockerserver; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @EnableScheduling public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
src/main/resources/application.properties に以下の内容を記述します。
spring.mail.host=localhost spring.mail.port=25 # メール送信処理を debug したい場合には以下の行のコメントアウトを解除する # spring.mail.properties.mail.debug=true
src/main/java/ksbysample/eipapp/dockerserver の下に flow パッケージを新規作成し、その下に MailFlowConfig.java を新規作成して、以下の内容を記述します。
package ksbysample.eipapp.dockerserver.flow; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.integration.dsl.IntegrationFlow; import org.springframework.integration.handler.LoggingHandler; import org.springframework.integration.mail.MailHeaders; import org.springframework.integration.mail.MailSendingMessageHandler; import org.springframework.integration.support.StringObjectMapBuilder; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.support.MessageBuilder; import org.springframework.scheduling.annotation.Scheduled; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; @Configuration public class MailFlowConfig { /**************************************** * メール送信処理のサンプル * ****************************************/ private final JavaMailSender mailSender; private final AtomicInteger count; public MailFlowConfig(JavaMailSender mailSender) { this.mailSender = mailSender; this.count = new AtomicInteger(0); } @Bean public MailSendingMessageHandler mailSendingMessageHandler() { return new MailSendingMessageHandler(this.mailSender); } @Autowired private MailSendingMessageHandler mailSendingMessageHandler; /** * 受信したメッセージを元にメールを送信する * * @return {@IntegrationFlow} オブジェクト */ @Bean public IntegrationFlow sendMailFlow() { return f -> f // メッセージ送信に時間がかかるので(1件あたり約10秒)、スレッドを生成してメール送信は別スレッドに処理させる .channel(c -> c.executor(Executors.newCachedThreadPool())) .filter(Message.class, m -> m.getHeaders().containsKey(MailHeaders.FROM) && m.getHeaders().containsKey(MailHeaders.TO) && m.getHeaders().containsKey(MailHeaders.SUBJECT)) .log(LoggingHandler.Level.WARN, m -> String.format("★★★ メール送信: %s", m.getPayload())) .wireTap(sf -> sf.handle(mailSendingMessageHandler)) .log(LoggingHandler.Level.WARN, m -> String.format("◇◇◇ メール送信: %s", m.getPayload())); } /** * sendMailFlow へ5秒おきに MailHeaders.* のヘッダーをセットした Message を送信する */ @Scheduled(initialDelay = 5000, fixedDelay = 5000) public void sendMessageTask() { Map<String, Object> headers = new StringObjectMapBuilder() .put(MailHeaders.FROM, "sample@test.co.jp") .put(MailHeaders.TO, "tanaka@mail.example.com,suzuki@mail.example.com") .put(MailHeaders.SUBJECT, "これはテストです") .get(); MessageHeaders messageHeaders = new MessageHeaders(headers); Message<String> message = MessageBuilder.createMessage(String.format("count = %d", this.count.incrementAndGet()) , messageHeaders); sendMailFlow().getInputChannel().send(message); } }
動作確認してみます。最初に docker-compose up -d
でメールサーバを起動し、メールが何も届いていないことを確認します。
次に作成したアプリケーションを実行してメールを送信します。
メールサーバを見るとメールが届いていることが確認できます。
POP3 でメールを受信するサンプルを作成する
src/main/java/ksbysample/eipapp/dockerserver/flow/MailFlowConfig.java に POP3 のメール受信の処理を追加します。
/**************************************** * メール受信処理のサンプル * ****************************************/ /** * Pop3MailReceiver {@link MailFlowConfig#pop3MessageSource()} に記述せず Bean で定義する必要がある * Bean にしないと受信したメッセージをサーバから削除してくれない * * @return {@Pop3MailReceiver} オブジェクト */ @Bean public Pop3MailReceiver pop3MailReceiver() { Pop3MailReceiver pop3MailReceiver = new Pop3MailReceiver("localhost", "tanaka@mail.example.com", "xxxxxxxx"); pop3MailReceiver.setShouldDeleteMessages(true); Properties javaMailProperties = new Properties(); // debug したい場合には以下のコメントアウトを解除する // javaMailProperties.put("mail.debug", "true"); pop3MailReceiver.setJavaMailProperties(javaMailProperties); return pop3MailReceiver; } @Bean public MailReceivingMessageSource pop3MessageSource() { return new MailReceivingMessageSource(pop3MailReceiver()); } /** * 5秒おきにメールを受信してみる * * @return */ @Bean public IntegrationFlow mailRecvByPop3Flow() { return IntegrationFlows.from(pop3MessageSource() // 5秒おきに最大100件受信する , c -> c.poller(Pollers.fixedDelay(5000).maxMessagesPerPoll(100))) .<MimeMessage>log(LoggingHandler.Level.ERROR, m -> { try { return String.format("◎◎◎ メール受信: %s" , StringUtils.chomp((String) m.getPayload().getContent())); } catch (IOException | MessagingException e) { throw new RuntimeException(e); } }) .get(); }
メールサーバを再起動してから(送信されたメールがクリアされます)、アプリケーションを実行するとメールの送信と受信が行われます。
履歴
2018/08/28
初版発行。
IntelliJ IDEA を 2018.2.1 → 2018.2.2 へバージョンアップ
IntelliJ IDEA を 2018.2.1 → 2018.2.2 へバージョンアップする
IntelliJ IDEA の 2018.2.2 がリリースされているのでバージョンアップします。
- IntelliJ IDEA 2018.2.2 is released!
https://blog.jetbrains.com/idea/2018/08/intellij-idea-2018-2-2-is-released/
※ksbysample-webapp-lending プロジェクトを開いた状態でバージョンアップしています。
IntelliJ IDEA のメインメニューから「Help」-「Check for Updates...」を選択します。
「IDE and Plugin Updates」ダイアログが表示されます。左下に「Update and Restart」ボタンが表示されていますので、「Update and Restart」ボタンをクリックします。
Plugin の update も表示されました。このまま「Update and Restart」ボタンをクリックします。
Patch がダウンロードされて IntelliJ IDEA が再起動します。
IntelliJ IDEA が起動すると画面下部に「Indexing…」のメッセージが表示されますので、終了するまで待機します。
IntelliJ IDEA のメインメニューから「Help」-「About」を選択し、2018.2.2 へバージョンアップされていることを確認します。
Gradle Tool Window のツリーを見ると「Tasks」の下に「other」しかない状態になっているので、左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
clean タスク実行 → Rebuild Project 実行 → build タスクを実行して、"BUILD SUCCESSFUL" のメッセージが出力されることを確認します。
Project Tool Window で src/test を選択した後、コンテキストメニューを表示して「Run with Coverage」-「All Tests」を選択し、テストが全て成功することを確認します。
Spring Boot + Spring Integration でいろいろ試してみる ( その24 )( MessageSource からの Message 送信有無を制御する )
概要
記事一覧はこちらです。
org.springframework.integration.core.MessageSource からデータ取得して処理する処理を任意に stop したり start したりする方法を調べました。そのメモ書きです。
参照したサイト・書籍
- Is there a way to start the file:inbound-channel-adapter through code?
https://stackoverflow.com/questions/26708039/is-there-a-way-to-start-the-fileinbound-channel-adapter-through-code
目次
- ksbysample-eipapp-messagesource-controll プロジェクトを作成する
- まずは 1秒毎に hello を出力する処理を書いてみる
- MessageSource から null を返せば何も処理されないのか?
- MessageSource から Message を返すか否かを 5秒毎に切り替えてみる
- poller を stop、start して制御してみる
手順
ksbysample-eipapp-messagesource-controll プロジェクトを作成する
今回は Spring Initializr で作成します。
生成された build.gradle は以下のものですが、
buildscript { ext { springBootVersion = '2.0.4.RELEASE' } repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'org.springframework.boot' apply plugin: 'io.spring.dependency-management' group = 'ksbysample' version = '0.0.1-SNAPSHOT' sourceCompatibility = 1.8 repositories { mavenCentral() } dependencies { compile('org.springframework.boot:spring-boot-starter-integration') testCompile('org.springframework.boot:spring-boot-starter-test') }
lombok の @Slf4j を使いたいので dependencies block を以下のように変更します。Spring Initializr で生成された Gradle プロジェクトの Gradle は 4.8.1 だったので、記述も少し変更しました。
dependencies { def lombokVersion = "1.18.2" implementation('org.springframework.boot:spring-boot-starter-integration') testImplementation('org.springframework.boot:spring-boot-starter-test') // for lombok annotationProcessor("org.projectlombok:lombok:${lombokVersion}") compileOnly("org.projectlombok:lombok:${lombokVersion}") }
build.gradle を変更後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
@SpringBootApplication が付与されているクラス名が長かったので、短く Application
クラスに変更します。
まずは 1秒毎に hello を出力する処理を書いてみる
以下の仕様で実装します。
- MessageSource は "hello" の文字列が payload にセットされたメッセージを返す。
- IntegrationFlow を返す Bean の処理は、1秒毎に MessageSource からメッセージを取得してログに出力する。
src/main/java/ksbysample/eipapp/mscontroll の下に FlowConfig.java を新規作成し、以下の内容を記述します。
package ksbysample.eipapp.mscontroll; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.integration.core.MessageSource; import org.springframework.integration.dsl.IntegrationFlow; import org.springframework.integration.dsl.IntegrationFlows; import org.springframework.integration.dsl.Pollers; import org.springframework.integration.support.MessageBuilder; @Slf4j @Configuration public class FlowConfig { @Bean public MessageSource<String> helloMessageSource() { return () -> MessageBuilder.withPayload("hello").build(); } @Bean public IntegrationFlow helloFlow() { return IntegrationFlows.from(helloMessageSource() , e -> e.poller(Pollers.fixedDelay(1000))) .handle((p, h) -> { log.info("★★★ " + p); return null; }) .get(); } }
実行すると以下のようにログが出力されます。
MessageSource から null を返せば何も処理されないのか?
MessageSource から Message を返さずに null を返すと処理は何も起きない気がするので、試してみます。helloMessageSource
メソッドを以下のように変更します。
@Bean public MessageSource<String> helloMessageSource() { // return () -> MessageBuilder.withPayload("hello").build(); return () -> null; }
実行してみると Started Application in 1.981 seconds が出力された後は何も出力されませんでした。
MessageSource から Message を返すか否かを 5秒毎に切り替えてみる
以下の仕様で実装し直してみます。
- MessageSource は boolean 型のフィールド変数を見て Message を返すか否かを切り替えられるようにする。
- フィールド変数の値は @Scheduled アノテーションを付与したメソッドで 5秒毎に変更する。
まずは @Scheduled アノテーションが使用できるよう src/main/java/ksbysample/eipapp/mscontroll/Application.java を以下のように変更します。
package ksbysample.eipapp.mscontroll; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @EnableScheduling public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
@EnableScheduling
を追加します。
src/main/java/ksbysample/eipapp/mscontroll/FlowConfig.java を以下のように変更します。
package ksbysample.eipapp.mscontroll; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.integration.core.MessageSource; import org.springframework.integration.dsl.IntegrationFlow; import org.springframework.integration.dsl.IntegrationFlows; import org.springframework.integration.dsl.Pollers; import org.springframework.integration.support.MessageBuilder; import org.springframework.scheduling.annotation.Scheduled; @Slf4j @Configuration public class FlowConfig { private boolean messageSourceControlFlg = true; @Bean public MessageSource<String> helloMessageSource() { return () -> messageSourceControlFlg ? MessageBuilder.withPayload("hello").build() : null; } @Bean public IntegrationFlow helloFlow() { return IntegrationFlows.from(helloMessageSource() , e -> e.poller(Pollers.fixedDelay(1000))) .handle((p, h) -> { log.info("★★★ " + p); return null; }) .get(); } @Scheduled(initialDelay = 5000, fixedDelay = 5000) public void messageSourceControlFlgChanger() { messageSourceControlFlg = !messageSourceControlFlg; log.warn(String.valueOf(messageSourceControlFlg)); } }
private boolean messageSourceControlFlg = true;
を追加します。- helloMessageSource Bean の処理を
return () -> MessageBuilder.withPayload("hello").build();
→return () -> messageSourceControlFlg ? MessageBuilder.withPayload("hello").build() : null;
に変更します。 - messageSourceControlFlgChanger メソッドを追加します。@Scheduled アノテーションを付与して 5秒毎に実行されるようにします。
実行すると 5秒間 hello のログが出力されて、true → false に変わると5秒間何も起きず、false → true に変わると再び hello が出力されるようになりました。やりたかったことは実現できました。
poller を stop、start して制御してみる
MessageSource ではなく、poller の方の処理を stop, start する方法もあります。
Control Bus で制御する
Spring Integration には Control Bus という Component の動作を制御するための機能が用意されており、これを利用することで poller の動作を制御できます。
src/main/java/ksbysample/eipapp/mscontroll/FlowConfig.java を以下のように変更します。
package ksbysample.eipapp.mscontroll; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.integration.core.MessageSource; import org.springframework.integration.dsl.IntegrationFlow; import org.springframework.integration.dsl.IntegrationFlowDefinition; import org.springframework.integration.dsl.IntegrationFlows; import org.springframework.integration.dsl.Pollers; import org.springframework.integration.support.MessageBuilder; import org.springframework.messaging.Message; import org.springframework.scheduling.annotation.Scheduled; @Slf4j @Configuration public class FlowConfig { private boolean messageSourceControlFlg = false; @Bean public MessageSource<String> helloMessageSource() { return () -> MessageBuilder.withPayload("hello").build(); } @Bean public IntegrationFlow helloFlow() { return IntegrationFlows.from(helloMessageSource() // poller に .id("xxx") を付与すると、Control Bus に "@xxx.stop()", "@xxx.start()" の SpEL のメッセージ // を送信することで poller の処理を停止・開始することができるようになる , e -> e.poller(Pollers.fixedDelay(1000)) .autoStartup(messageSourceControlFlg) .id("helloPollingAdapter")) .handle((p, h) -> { log.info("★★★ " + p); return null; }) .get(); } @Bean public IntegrationFlow controlBus() { return IntegrationFlowDefinition::controlBus; } @Scheduled(initialDelay = 5000, fixedDelay = 5000) public void messageSourceControlFlgChanger() { messageSourceControlFlg = !messageSourceControlFlg; String payload = messageSourceControlFlg ? "@helloPollingAdapter.start()" : "@helloPollingAdapter.stop()"; Message operation = MessageBuilder.withPayload(payload).build(); controlBus().getInputChannel().send(operation); log.warn(payload); } }
- messageSourceControlFlg の初期値を true → false に変更します。
- helloMessageSource メソッド内の処理を
return () -> messageSourceControlFlg ? MessageBuilder.withPayload("hello").build() : null;
→return () -> MessageBuilder.withPayload("hello").build();
に変更します。 - helloFlow メソッド内の処理で、
e -> e.poller(...)
の後に.autoStartup(messageSourceControlFlg).id("helloPollingAdapter")
を追加します。 - controlBus Bean を追加します。
- messageSourceControlFlgChanger メソッド内の処理を Control Bus にメッセージを送信して停止・再開する処理に変更します。
実際に動作させてみると、アプリが起動して 5秒経過した後に started helloPollingAdapter
のログが出力されて hello の文字が出力されはじめ、5秒後に stopped helloPollingAdapter
のログが出て hello の文字が出なくなります。
org.springframework.integration.endpoint.AbstractEndpoint#stop, start で制御する
実は e -> e.poller(...)
の後に追加した .id("helloPollingAdapter")
の名前で Bean が生成されているので、その Bean を取得して AbstractEndpoint#stop, AbstractEndpoint#start メソッドを呼び出すことで制御することもできます。
src/main/java/ksbysample/eipapp/mscontroll/FlowConfig.java を以下のように変更します。
package ksbysample.eipapp.mscontroll; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.integration.core.MessageSource; import org.springframework.integration.dsl.IntegrationFlow; import org.springframework.integration.dsl.IntegrationFlows; import org.springframework.integration.dsl.Pollers; import org.springframework.integration.endpoint.SourcePollingChannelAdapter; import org.springframework.integration.support.MessageBuilder; import org.springframework.scheduling.annotation.Scheduled; @Slf4j @Configuration public class FlowConfig { private final ApplicationContext context; public FlowConfig(ApplicationContext context) { this.context = context; } @Bean public MessageSource<String> helloMessageSource() { return () -> MessageBuilder.withPayload("hello").build(); } @Bean public IntegrationFlow helloFlow() { return IntegrationFlows.from(helloMessageSource() , e -> e.poller(Pollers.fixedDelay(1000)) .autoStartup(false) .id("helloPollingAdapter")) .handle((p, h) -> { log.info("★★★ " + p); return null; }) .get(); } @Scheduled(initialDelay = 5000, fixedDelay = 5000) public void messageSourceControlFlgChanger() { SourcePollingChannelAdapter adapter = (SourcePollingChannelAdapter) context.getBean("helloPollingAdapter"); if (adapter.isRunning()) { adapter.stop(); } else { adapter.start(); } } }
private boolean messageSourceControlFlg = false;
を削除します。private final ApplicationContext context;
を追加し、コンストランクタで DI するようにします。- helloFlow メソッド内の処理で、
.autoStartup(messageSourceControlFlg)
→.autoStartup(false)
に変更します。 - controlBus Bean を削除します。
- messageSourceControlFlgChanger メソッド内の処理を helloPollingAdapter Bean を取得して AbstractEndpoint#stop, AbstractEndpoint#start メソッドを呼び出して停止・再開する処理に変更します。
実行すると Control Bus の時と同じ動作になります。
履歴
2018/08/21
初版発行。
2018/08/22
* poller を stop、start して制御してみる を追加した。
Windows 7 PC → Windows 10 PC へ移行する ( 最後にメモ書き )
記事一覧はこちらです。
使用している Windows 7 PC と Windows 10 PC は物理的なディスプレイのサイズがほとんど同じで、ディスプレイ上に表示されている内容も同じだったのですが、解像度を見ると前者は 1280 x 800 で、後者は 1920 x 1200 となっており、Windows 10 PC では 150% に拡大されていました。「高DPIデバイス」というものらしいです。
解像度が高いならもっと表示できるようになっても構わないなと思い、150% → 100% にしてみたらさすがに文字が小さすぎて見えず。125% だとちょうど良さそうだったのでこれにしたのですが、今度は Q-Dir 等のソフトで文字がにじむ現象が見られました。調べたら古いソフトで高DPIに対応していないとこうなるらしい。ソフトの exe のプロパティで設定をするときれいに表示させるようにできるので、いくつか設定しました。画面が広く表示されるようになったのは嬉しいのですが、少し面倒そうです。
これまで Google IME を利用していたのですが、拡大率を 125% に変更したら変換候補のウィンドウが入力中の文字を隠すように表示されるようになって Microsoft IME に切り替えました。Microsoft IME にすることはもうないだろうな、と思っていたので意外です。まさかこんな理由で切り替えることになるとは。。。 Microsoft IME を使ってみると、変換候補も結構賢くなっていて Google IME とそう変わらない感じです。
画像の編集にペイントを使用していましたが、高DPIに対応していなくて使い物になりません(画像を貼り付けても小さくなります)。代替ソフトをいろいろ調べてみましたが paint.net にしました。
IntelliJ IDEA の Grep Console Plugin が便利です。特に WARN のログが気づきにくいのですが、色が変わって表示されるので簡単に分かるようになりました。
IntelliJ IDEA にインストール済の Plugin を見ていたら Struts 1.x を見つけました。こんなプラグインもあったんだとか、使っていない Plugin(見てると結構ありそう)を整理したら IntelliJ IDEA が軽快に動作するようになるのかなとか思いましたが、他にもやりたいことが山積みなので今のままですね。
Windows 10 になって、やっと Docker for Windows を入れることが出来ました。IntelliJ IDEA でも Docker サポートされているし、今後はサーバは Docker で環境作って楽ができるはず! でも最近の流れだと Docker なんて使えて当たり前で、Kubernetes とかを覚えていきたいところです。まあ、マイペースで行きましょう。
次は ksbysample-webapp-lending の Spring Boot 1.5 → 2.0 バージョンアップを行います(たぶん)。