かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その14 )( Docker で Prometheus+Grafana の環境を構築して Spring Actuator で収集したメトリックスを表示する )

概要

記事一覧はこちらです。

Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その13 )( Remember Me 認証が使えなくなっていたので調査・修正する ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • Docker で Prometheus+Grafana の環境を構築して Spring Actuator で収集したメトリックスを表示させてみます。

参照したサイト・書籍

目次

  1. Prometheus の環境を構築する
    1. prometheus.yml を作成する
    2. docker-compose.yml を作成する
    3. prometheus を起動する
    4. 動作確認
  2. Grafana の環境を構築する
    1. docker-compose.yml に Grafana の設定を追加する
    2. Grafana を起動する
    3. 設定&動作確認
  3. 次回は。。。

手順

Prometheus の環境を構築する

prometheus.yml を作成する

プロジェクトのルートディレクトリの下に docker/prometheus のディレクトリを作成し、その下に prometheus.yml というファイルを新規作成して以下の内容を記述します。

global:
  scrape_interval: 15s # By default, scrape targets every 15 seconds.
  evaluation_interval: 15s # Evaluate rules every 15 seconds.

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'prometheus'
  scrape_interval: 15s
  static_configs:
  - targets: ['localhost:9090']

- job_name: 'spring-actuator'
  metrics_path: '/actuator/prometheus'
  scrape_interval: 15s
  basic_auth:
    username: actuator
    password: xxxxxxxx
  static_configs:
  # Docker で起動した Prometheus からローカルPCで起動している Spring Boot のアプリケーション
  # にアクセスするので、localhost ではなくローカルPCに割り当てられているIPアドレスを設定する
  - targets: ['172.23.136.33:8080']

docker-compose.yml を作成する

プロジェクトのルートディレクトリの下に docker-compose.yml を新規作成して以下の内容を記述します。

version: '3'

services:
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    ports:
    - "9090:9090"
    volumes:
    - ./docker/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml

prometheus を起動する

docker-compose up -d コマンドを実行します。

f:id:ksby:20181104001731p:plain

動作確認

http://localhost:9090/ にアクセスすると http://localhost:9090/graph にリダイレクトされて以下の画面が表示されます。

f:id:ksby:20181104163419p:plain

jvm と入力すると Spring Actuator から収集した jvm が含まれるメトリックスが表示されて、

f:id:ksby:20181104163658p:plain

jvm_memory_used_bytes を選択して「Execute」ボタンをクリックすると以下のように表示されます。

f:id:ksby:20181104164000p:plain

「Graph」タブをクリックするとグラフが表示されます(表示間隔をデフォルトの 1h から 15m に変更しています)。

f:id:ksby:20181104164227p:plain

Grafana の環境を構築する

docker-compose.yml に Grafana の設定を追加する

docker-compose.yml に Grafana の設定を追加します。

version: '3'

services:
  ..........

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
    - "3000:3000"

Grafana を起動する

docker-compose down コマンドを実行した後、docker-compose up -d コマンドを実行します。

f:id:ksby:20181104173129p:plain

設定&動作確認

http://localhost:3000/ にアクセスすると http://localhost:3000/login にリダイレクトされてログイン画面が表示されます。

f:id:ksby:20181104173408p:plain

admin / admin を入力した後「Log In」ボタンをクリックすると、今度はパスワードの変更を求められるのでパスワードを入力して「Save」ボタンをクリックします。

f:id:ksby:20181104173727p:plain

「Home Dashboard」画面が表示されるので「Add data source」をクリックします。

f:id:ksby:20181104173906p:plain

以下の画面の値を入力して「Save & Test」ボタンをクリックします。Prometheus は Docker の別コンテナで起動しているので、「HTTP」-「URL」には localhost 以外の IP アドレスを記述します。

f:id:ksby:20181104174556p:plain

画面左側から「Dashboards」-「Home」を選択します。

f:id:ksby:20181104195840p:plain

「New dashboard」をクリックします。

f:id:ksby:20181104200109p:plain

Panel を追加します。「Graph」をクリックします。

f:id:ksby:20181104200252p:plain

「Panel Title」-「Edit」を選択します。

f:id:ksby:20181104200444p:plain

jvm_memory_used_bytes を入力した後、画面右上の「Back to dashboard」ボタンをクリックします。

f:id:ksby:20181104200701p:plain

Panel 上に jvm_memory_used_bytes のメトリックスのグラフが表示されます。

f:id:ksby:20181104200924p:plain

グラフの表示期間がデフォルトでは Last 6 hours になっているので Last 1 hour に変更し、かつ自動リフレッシュされるようにします。画面右上の「Last 6 hours」と表示されているところをクリックした後、「Custom range」-「Refreshing every」で「5s」を選択して「Apply」ボタンをクリックし、「Quick ranges」で「Last 1 hour」をクリックします。

f:id:ksby:20181104201755p:plain

f:id:ksby:20181104201443p:plain

次回は。。。

Prometheus+Grafana が面白い!! もう少しいろいろ試してみたいので、Prometheus+Grafana で jvm 以外のデータを表示させてみます。

履歴

2018/11/04
初版発行。

Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その13 )( Remember Me 認証が使えなくなっていたので調査・修正する )

概要

記事一覧はこちらです。

Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その12 )( Spring Boot Actuator を導入する ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • 前回の記事の後に build するのを忘れていたので build したところ、Remember Me 認証が使えなくなっていることに気づきました。
    • Docker で Prometheus+Grafana の環境を構築する予定でしたが、先にこの件を調査・修正します。

参照したサイト・書籍

目次

  1. build してみる
  2. Remember Me 認証が使えなくなった原因を調査・修正する
  3. 再度 build してみる

手順

build してみる

clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると、テストが1件失敗して BUILD FAILED のメッセージが出力されます。

f:id:ksby:20181103092916p:plain

失敗したテストで何が起きているのか確認するために、Project Tool Window で src/test を選択した後、コンテキストメニューを表示して「Run with Coverage」-「All Tests」を選択してテストを実行してみると、失敗したテストとエラーメッセージが以下のように出力されて、

f:id:ksby:20181103093424p:plain

エラーが出力された箇所は以下のテストの赤線の部分でした。

f:id:ksby:20181103093714p:plain

どうも Remember Me 認証が動かなくなっているようです。

Tomcat を起動して動作を確認してみます。Tomcat 起動後、http://localhost:8080/ にアクセスして tanaka.taro@sample.com / taro を入力 →「次回から自動的にログインする 」をチェックしてから「ログイン」ボタンをクリックします。

f:id:ksby:20181103094857p:plain

http://localhost:8080/admin/library の URL の画面が表示されるので、ブラウザを一旦終了させます。

f:id:ksby:20181103095044p:plain

ブラウザを起動してアドレスバーに http://localhost:8080/admin/library を入力すると Remember Me 認証が行われて、ログイン不要で画面が表示されるはず。。。なのですがログイン画面が表示されました。やはり Remember Me 認証が使えなくなっています。

f:id:ksby:20181103095202p:plain

Remember Me 認証が使えなくなった原因を調査・修正する

DEBUG ログが出力されるように設定を変更して、テストが失敗した時に何が起きているか見てみると、org.springframework.security.provisioning.InMemoryUserDetailsManager.loadUserByUsername で UsernameNotFoundException が発生していました。

f:id:ksby:20181103100416p:plain

InMemory のユーザだけチェックして、ユーザがいなかった場合に DB のユーザをチェックしていないようです。

stacktrace に出力されているソースの場所を追ってみると、org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices#processAutoLoginCookie で getUserDetailsService().loadUserByUsername(...) を呼び出しており、

f:id:ksby:20181103134715p:plain

getUserDetailsService().loadUserByUsername(...) は実際には org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$UserDetailsServiceDelegator#loadUserByUsername を呼び出していて、

f:id:ksby:20181103135021p:plain

このメソッドでは AuthenticationManagerBuilder のリストから最初に AuthenticationManagerBuilder#getDefaultUserDetailsService で取得できた UserDetailsService の loadUserByUsername を呼び出しています。

AuthenticationManagerBuilder の設定は ksbysample.webapp.lending.config.WebSecurityConfig の中で以下のように行っていますが、

f:id:ksby:20181103135446p:plain

org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder#inMemoryAuthentication を見ると apply メソッドを呼び出していて、

f:id:ksby:20181103135800p:plain

apply メソッドは以下のようになっています。DEBUG モードで確認すると inMemoryAuthentication メソッドが呼び出された時には defaultUserDetailsService には org.springframework.security.provisioning.InMemoryUserDetailsManager がセットされていました。

f:id:ksby:20181103135934p:plain

まとめてみると以下の状況でした。

  • ksbysample.webapp.lending.config.WebSecurityConfig#configAuthentication で以下の順に呼び出しており、
    • auth.authenticationProvider(daoAuhthenticationProvider()).userDetailsService(userDetailsService);
    • auth.inMemoryAuthentication()...
  • 最初の auth.authenticationProvider(daoAuhthenticationProvider()).userDetailsService(userDetailsService); で AuthenticationManagerBuilder の defaultUserDetailsService に userDetailsService がセットされるが、
  • 次の auth.inMemoryAuthentication()... で defaultUserDetailsService に org.springframework.security.provisioning.InMemoryUserDetailsManager がセットされる。
  • Remember Me 認証では defaultUserDetailsService にセットされている UserDetailsService しか使用されない。
  • よって InMemory にセットされたユーザしか使用されず、DB に保存されたユーザが使用されないため、Remember-me cookie が存在しても UsernameNotFoundException が発生する。

AuthenticationManagerBuilder#userDetailsService で userDetailsService をセットした後に auth.inMemoryAuthentication() を呼び出したため、InMemory の UserDetailsServce(InMemoryUserDetailsManager)が Remember Me 認証で使用される UserDetailsService になっていたのが原因でした。 AuthenticationManagerBuilder#userDetailsService を呼び出す前に auth.inMemoryAuthentication() を呼び出すように修正します。

src/main/java/ksbysample/webapp/lending/config/WebSecurityConfig.java を以下のように変更します。

    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
       // AuthenticationManagerBuilder#userDetailsService の後に auth.inMemoryAuthentication() を呼び出すと
        // AuthenticationManagerBuilder の defaultUserDetailsService に
        // org.springframework.security.provisioning.InMemoryUserDetailsManager がセットされて
        // Remember Me 認証で InMemoryUserDetailsManager が使用されて DB のユーザが参照されなくなるので、
        // Remember Me 認証で使用する UserDetailsService を一番最後に呼び出す
        // ※今回の場合には auth.userDetailsService(userDetailsService) が一番最後に呼び出されるようにする
         auth.inMemoryAuthentication()
                .withUser("actuator")
                .password("{noop}xxxxxxxx")
                .roles("ENDPOINT_ADMIN");
        auth.authenticationProvider(daoAuhthenticationProvider())
                .userDetailsService(userDetailsService);
    }
  • auth.inMemoryAuthentication().~auth.authenticationProvider(daoAuhthenticationProvider()).userDetailsService(userDetailsService); の前に移動します。

再度 build してみる

再度 clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると、今度は BUILD SUCCESSFUL が出力されました。

f:id:ksby:20181103145914p:plain

履歴

2018/11/03
初版発行。

Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その12 )( Spring Boot Actuator を導入する )

概要

記事一覧はこちらです。

Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その11 )( HikariCP のコネクションプーリングの情報を JMX で取得できるようにする ) の続きです。

参照したサイト・書籍

目次

  1. build.gradle を変更する
  2. WebSecurityConfig.java を変更して /actuator/** にのみ Basic 認証を設定する
  3. application.properties を変更する
  4. 動作確認
  5. 次回は。。。

手順

build.gradle を変更する

build.gradle の以下の点を変更します。

plugins {
    ..........
    id "de.undercouch.download" version "3.4.3"
    id "com.gorylenko.gradle-git-properties" version "1.5.2"
}

..........

dependencies {
    ..........
    implementation("org.springframework.boot:spring-boot-starter-amqp")
    implementation("org.springframework.boot:spring-boot-starter-actuator")
    runtimeOnly("org.springframework.boot:spring-boot-devtools")
    ..........
    implementation("org.codehaus.janino:janino")
    implementation("io.micrometer:micrometer-registry-prometheus")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    ..........
  • plugins block の以下の点を変更します。
    • id "com.gorylenko.gradle-git-properties" version "1.5.2" を追加します。
  • dependencies block の以下の点を変更します。
    • implementation("org.springframework.boot:spring-boot-starter-actuator") を追加します。
    • spring-boot-devtools は runtimeOnly で記述すべきなのに変更するのを忘れていることに気づきました。implementation("org.springframework.boot:spring-boot-devtools")runtimeOnly("org.springframework.boot:spring-boot-devtools") に変更します。
    • implementation("io.micrometer:micrometer-registry-prometheus") を追加します。

変更後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。

WebSecurityConfig.java を変更して /actuator/** にのみ Basic 認証を設定する

src/main/java/ksbysample/webapp/lending/config/WebSecurityConfig.java の以下の点を変更します。

@Configuration
public class WebSecurityConfig {

    ..........

    @Configuration
    @Order(1)
    public static class ActuatorWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    // Spring Actuator の Endpoint のみ Basic認証を設定する
                    .requestMatcher(EndpointRequest.toAnyEndpoint())
                    .authorizeRequests()
                    .anyRequest().hasRole("ENDPOINT_ADMIN")
                    .and()
                    .httpBasic();
        }

    }

    @Configuration
    public static class FormLoginWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    // 認証の対象外にしたいURLがある場合には、以下のような記述を追加します
                    // 複数URLがある場合はantMatchersメソッドにカンマ区切りで対象URLを複数列挙します
                    // .antMatchers("/country/**").permitAll()
                    .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
                    .antMatchers("/fonts/**").permitAll()
                    .antMatchers("/html/**").permitAll()
                    .antMatchers("/encode").permitAll()
                    .antMatchers("/urllogin").permitAll()
                    .antMatchers("/webapi/**").permitAll()
                    .antMatchers("/springMvcMemo/**").permitAll()
                    .antMatchers("/sessionsample/**").permitAll()
                    .antMatchers("/textareamemo/**").permitAll()
                    .antMatchers("/sample/**").permitAll()
                    .anyRequest().hasAnyRole("USER", "ADMIN", "APPROVER");
            http.formLogin()
                    .loginPage("/")
                    .loginProcessingUrl("/login")
                    .defaultSuccessUrl(DEFAULT_SUCCESS_URL)
                    .usernameParameter("id")
                    .passwordParameter("password")
                    .successHandler(new RoleAwareAuthenticationSuccessHandler())
                    .failureHandler(new ForwardAuthenticationFailureHandler("/"))
                    .permitAll()
                    .and()
                    .logout()
                    .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                    .logoutSuccessUrl("/")
                    .deleteCookies("SESSION")
                    .deleteCookies("remember-me")
                    .invalidateHttpSession(true)
                    .permitAll()
                    .and()
                    .rememberMe()
                    .key(REMEMBERME_KEY)
                    .tokenValiditySeconds(60 * 60 * 24 * 30);
        }

    }

    ..........

    /**
     * @param auth ???
     * @throws Exception
     */
    @SuppressWarnings("PMD.SignatureDeclareThrowsException")
    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
        // 必ず auth.inMemoryAuthentication() を先に書くこと
        auth.inMemoryAuthentication()
                .withUser("actuator")
                .password("{noop}xxxxxxxx")
                .roles("ENDPOINT_ADMIN");
        auth.authenticationProvider(daoAuhthenticationProvider())
                .userDetailsService(userDetailsService);
    }

}
  • @Configuration public static class FormLoginWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { ... } を追加し、既存の configure メソッドをこの中へ移動します。また configure メソッド内の .authenticated().hasAnyRole("USER", "ADMIN", "APPROVER") に変更して actuator 用の Role として使用する ENDPOINT_ADMIN ではログインできないようにします。
  • @Configuration @Order(1) public static class ActuatorWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { ... } を追加し、actuator の endpoint の Basic認証をこのクラスで設定します。
  • configAuthentication bean 内に auth.inMemoryAuthentication().withUser("actuator").password("{noop}xxxxxxxx").roles("ENDPOINT_ADMIN"); を追加します。

application.properties を変更する

src/main/resources/application.properties の以下の点を変更します。

doma.dialect=org.seasar.doma.jdbc.dialect.PostgresDialect

management.endpoints.web.exposure.include=health,info,loggers,prometheus

spring.autoconfigure.exclude=com.integralblue.log4jdbc.spring.Log4jdbcAutoConfiguration
spring.datasource.hikari.jdbc-url=jdbc:postgresql://localhost/ksbylending
..........
  • management.endpoints.web.exposure.include=health,info,loggers,prometheus を追加します。

動作確認

ここまでの設定で /actuator/** には Basic 認証が、それ以外のパスには Form 認証が行われるはずなので、確認してみます。

Tomcat を起動した後、http://localhost:8080/admin/library にアクセスするとログイン画面が表示されます。

f:id:ksby:20181028233415p:plain

tanaka.taro@sample.com / taro を入力して「ログイン」ボタンをクリックすると http://localhost:8080/admin/library が表示されます。

f:id:ksby:20181028233537p:plain

ログインしている状態から http://localhost:8080/actuator/prometheus にアクセスすると、現在ログインしている ID では ENDPOINT_ADMIN の ROLE を持っていないので 403 Forbidden が返ってきます。

f:id:ksby:20181028233843p:plain

画面右上の「ログアウト」リンクをクリックしてログアウトしてから再度 http://localhost:8080/actuator/prometheus にアクセスすると、Basic 認証のダイアログが表示されます。

f:id:ksby:20181028234446p:plain

actuator / xxxxxxxx を入力して「OK」ボタンをクリックすると /actuator/prometheus が出力する情報が表示されます。

f:id:ksby:20181028234603p:plain

次回は。。。

Docker で Prometheus+Grafana の環境を構築して Spring Actuator で収集したメトリックスを表示させてみます。

履歴

2018/10/29
初版発行。
2018/11/20
* Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その13 )( Remember Me 認証が使えなくなっていたので調査・修正する ) の結果を反映して auth.inMemoryAuthentication() を書く位置を修正しました。

Java SE を 8u181 → 8u192 へ、IntelliJ IDEA を 2018.2.4 → 2018.2.5 へ、Git for Windows を 2.18.0 → 2.19.1 へバージョンアップ

Java SE を 8u181 → 8u192 へバージョンアップする

※ksbysample-webapp-lending プロジェクトを開いた状態でバージョンアップしています。

  1. OracleJava SE Downloads を見ると 8u192 がダウンロードできるようになっていましたので、8u192 へバージョンアップします。

  2. jdk-8u192-windows-x64.exe をダウンロードして D:\Java\jdk1.8.0_192 へインストールした後、環境変数 JAVA_HOME のパスを D:\Java\jdk1.8.0_192 へ変更します。

    コマンドプロンプトから java -version を実行し、1.8.0_192 に変更されていることを確認します。

    f:id:ksby:20181021001914p:plain

  3. IntelliJ IDEA を再起動した後、プロジェクトで使用する Java SE を 8u192 へ変更します。

  4. 開いているプロジェクトを閉じて「Welcome to IntelliJ IDEA」ダイアログを表示します。

  5. ダイアログ下部の「Configure」-「Project Defaults」-「Project Structure」を選択します。

    f:id:ksby:20181021002216p:plain

  6. 「Default Project Structure」ダイアログが表示されます。画面左側で「Project Settings」-「Project」を選択後、画面右側の「Project SDK」の「New...」ボタンをクリックし、表示されるメニューから「JDK」を選択します。

    f:id:ksby:20181021002542p:plain

  7. 「Select Home Directory for JDK」ダイアログが表示されます。D:\Java\jdk1.8.0_192 を選択した後、「OK」ボタンをクリックします。

    f:id:ksby:20181021002734p:plain

  8. 「Default Project Structure」ダイアログに戻るので、今度は「Project SDK」の「Edit」ボタンをクリックします。

    f:id:ksby:20181021002936p:plain

  9. 画面左側で「Platform Settings」-「SDKs」が選択された状態になるので、画面右上の入力フィールドで "1.8" → "1.8.0_192" へ変更します。

    f:id:ksby:20181021003214p:plain

  10. 次に中央のリストから「1.8.0_181」を選択した後、リストの上の「-」ボタンをクリックして削除します。

    f:id:ksby:20181021003542p:plain

  11. 「OK」ボタンをクリックして「Default Project Structure」ダイアログを閉じます。

  12. 「Welcome to IntelliJ IDEA」ダイアログに戻ったら、ksbysample-webapp-lending プロジェクトを開きます。

  13. IntelliJ IDEA のメイン画面が開いたら、メニューから「File」-「Project Structure...」を選択します。

  14. 「Project Structure」ダイアログが表示されます。以下の画像の状態になっているので、

    f:id:ksby:20181021003807p:plain

    「Project SDK」と「Project language level」を選択し直します。

    f:id:ksby:20181021004029p:plain

  15. 「OK」ボタンをクリックして「Project Structure」ダイアログを閉じます。

  16. メイン画面に戻ると画面右下に「Indexing...」の表示が出るので、終了するまで待ちます。

    f:id:ksby:20181021004148p:plain

  17. clean タスク実行 → Rebuild Project 実行 → build タスクを実行して、"BUILD SUCCESSFUL" のメッセージが出力されることを確認します。

    f:id:ksby:20181021004745p:plain

  18. Project Tool Window で src/test を選択した後、コンテキストメニューを表示して「Run ‘All Tests’ with Coverage」を選択し、テストが全て成功することを確認します。

    f:id:ksby:20181021005400p:plain

  19. 特に問題は発生しませんでした。8u192 で開発を進めます。

IntelliJ IDEA を 2018.2.4 → 2018.2.5 へバージョンアップする

IntelliJ IDEA の 2018.2.5 がリリースされているのでバージョンアップします。

※ksbysample-webapp-lending プロジェクトを開いた状態でバージョンアップしています。

  1. IntelliJ IDEA のメインメニューから「Help」-「Check for Updates...」を選択します。

  2. IDE and Plugin Updates」ダイアログが表示されます。左下に「Update and Restart」ボタンが表示されていますので、「Update and Restart」ボタンをクリックします。

    f:id:ksby:20181021005711p:plain

  3. Plugin の update も表示されました。このまま「Update and Restart」ボタンをクリックします。

    f:id:ksby:20181021005804p:plain

  4. Patch がダウンロードされて IntelliJ IDEA が再起動します。

  5. IntelliJ IDEA が起動すると画面下部に「Indexing…」のメッセージが表示されますので、終了するまで待機します。

    f:id:ksby:20181021013638p:plain

  6. IntelliJ IDEA のメインメニューから「Help」-「About」を選択し、2018.2.5 へバージョンアップされていることを確認します。

  7. Gradle Tool Window のツリーを見ると「Tasks」の下に「other」しかない状態になっているので、左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。

    f:id:ksby:20181021013842p:plain

  8. clean タスク実行 → Rebuild Project 実行 → build タスクを実行して、"BUILD SUCCESSFUL" のメッセージが出力されることを確認します。

    f:id:ksby:20181021014446p:plain

  9. Project Tool Window で src/test を選択した後、コンテキストメニューを表示して「Run with Coverage」-「All Tests」を選択し、テストが全て成功することを確認します。

    f:id:ksby:20181021015434p:plain

Git for Windows を 2.18.0 → 2.19.1 へバージョンアップする

Git for Windows の 2.19.1 がリリースされていたのでバージョンアップします。

  1. https://git-for-windows.github.io/ の「Download」ボタンをクリックして Git-2.19.1-64-bit.exe をダウンロードします。

  2. Git-2.19.1-64-bit.exe を実行します。

  3. 「Git 2.19.1 Setup」ダイアログが表示されます。[Next >]ボタンをクリックします。

  4. 「Select Components」画面が表示されます。「Git LFS(Large File Support)」だけチェックした状態で [Next >]ボタンをクリックします。

  5. 「Choosing the default editor used by Git」画面が表示されます。「Use Vim (the ubiquitous text editor) as Git's default editor」が選択された状態で [Next >]ボタンをクリックします。

  6. 「Adjusting your PATH environment」画面が表示されます。中央の「Use Git from the Windows Command Prompt」が選択されていることを確認後、[Next >]ボタンをクリックします。

  7. 「Choosing HTTPS transport backend」画面が表示されます。「Use the OpenSSL library」が選択されていることを確認後、[Next >]ボタンをクリックします。

  8. 「Configuring the line ending conversions」画面が表示されます。一番上の「Checkout Windows-style, commit Unix-style line endings」が選択されていることを確認した後、[Next >]ボタンをクリックします。

  9. 「Configuring the terminal emulator to use with Git Bash」画面が表示されます。「Use Windows'default console window」が選択されていることを確認した後、[Next >]ボタンをクリックします。

  10. 「Configuring extra options」画面が表示されます。「Enable file system caching」だけがチェックされていることを確認した後、[Next >]ボタンをクリックします。

  11. 「Configuring experimental options」画面が表示されます(初めて見る画面です)。"experimental" とのことなので、今回は何もチェックせずに [Install]ボタンをクリックします。

  12. インストールが完了すると「Completing the Git Setup Wizard」のメッセージが表示された画面が表示されます。中央の「View Release Notes」のチェックを外した後、「Finish」ボタンをクリックしてインストーラーを終了します。

  13. コマンドプロンプトを起動して git --version を実行し、git のバージョンが git version 2.19.1.windows.1 になっていることを確認します。

    f:id:ksby:20181021021321p:plain

  14. git-cmd.exe を起動して日本語の表示・入力が問題ないかを確認します。

    f:id:ksby:20181021021449p:plain

  15. 特に問題はないようですので、2.19.1 で作業を進めたいと思います。

Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その11 )( HikariCP のコネクションプーリングの情報を JMX で取得できるようにする )

概要

記事一覧はこちらです。

Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その10 )( Tomcat connection Pool → HikariCP に変更する ) の続きです。

参照したサイト・書籍

目次

  1. spring.datasource.hikari.register-mbeans=true の設定がなくても HikariCP が MBean に登録されることを確認する
  2. Pool(HikariPool-1)、PoolConfig(HikariPool-1)で MBean に登録されるようにする
  3. clean タスク実行 → Rebuild Project 実行 → build タスクを実行する
  4. ユニットテスト実行時には MBeanExporter の bean が生成されないのか?
  5. ApplicationConfig.java を変更してユニットテスト実行時には mBeanExporter.addExcludedBean("dataSource"); を呼び出さないようにする
  6. 再度 clean タスク実行 → Rebuild Project 実行 → build タスクを実行する

手順

spring.datasource.hikari.register-mbeans=true の設定がなくても HikariCP が MBean に登録されることを確認する

Spring が起動時に MBean を登録するのであれば spring.datasource.hikari.register-mbeans=true の設定がなくても登録されてしまうのだろうか?と思ったので試してみます。

src/main/resources/application.properties で spring.datasource.hikari.register-mbeans=trueコメントアウトしてから、

spring.autoconfigure.exclude=com.integralblue.log4jdbc.spring.Log4jdbcAutoConfiguration
spring.datasource.hikari.jdbc-url=jdbc:postgresql://localhost/ksbylending
spring.datasource.hikari.username=ksbylending_user
spring.datasource.hikari.password=xxxxxxxx
spring.datasource.hikari.driver-class-name=org.postgresql.Driver
spring.datasource.hikari.leak-detection-threshold=5000
#spring.datasource.hikari.register-mbeans=true
  • com.integralblue.log4jdbc.spring.Log4jdbcAutoConfiguration を無効にしないと dataSource ではなく Pool(HikariPool-1)という名前で MBean に登録されるので、spring.autoconfigure.exclude=com.integralblue.log4jdbc.spring.Log4jdbcAutoConfiguration の設定を一時的に入れています。

IntelliJ IDEA から Tomcat を起動 → ログイン画面(http://localhost:8080/)からログイン → jconsole で確認してみると、dataSource という名前の MBean が登録されていました。spring.datasource.hikari.register-mbeans=true を設定がなくても Spring Boot の AutoConfiguration によって登録されるようです。

f:id:ksby:20181019040547p:plain

spring.datasource.hikari.register-mbeans=trueコメントアウトしていたのは元に戻します。

Pool(HikariPool-1)、PoolConfig(HikariPool-1)で MBean に登録されるようにする

src/main/java/ksbysample/webapp/lending/config/ApplicationConfig.java を以下のように変更します。

@Configuration
public class ApplicationConfig {

    private final ConnectionFactory connectionFactory;

    private final MessageSource messageSource;

    private final MBeanExporter mBeanExporter;

    /**
     * @param connectionFactory {@link ConnectionFactory} bean
     * @param messageSource     {@link MessageSource} bean
     * @param mBeanExporter     {@link MBeanExporter} bean
     */
    public ApplicationConfig(ConnectionFactory connectionFactory
            , MessageSource messageSource
            , MBeanExporter mBeanExporter) {
        this.connectionFactory = connectionFactory;
        this.messageSource = messageSource;
        this.mBeanExporter = mBeanExporter;
    }

    ..........

    /**
     * @return HikariCP の DataSource オブジェクト
     */
    @Bean
    @ConfigurationProperties("spring.datasource.hikari")
    public DataSource dataSource() {
        mBeanExporter.addExcludedBean("dataSource");
        return DataSourceBuilder.create()
                .type(HikariDataSource.class)
                .build();
    }
  • private final MBeanExporter mBeanExporter; を追加し、コンストラクタインジェクションされるようコンストラクタに処理を追加します。
  • dataSource Bean 内に mBeanExporter.addExcludedBean("dataSource"); を追加し、Spring Boot により dataSource という名前で MBean に登録されないようにします。

動作確認してみます。src/main/resources/application.properties は以下の設定になっており、

spring.autoconfigure.exclude=com.integralblue.log4jdbc.spring.Log4jdbcAutoConfiguration
spring.datasource.hikari.jdbc-url=jdbc:postgresql://localhost/ksbylending
spring.datasource.hikari.username=ksbylending_user
spring.datasource.hikari.password=xxxxxxxx
spring.datasource.hikari.driver-class-name=org.postgresql.Driver
spring.datasource.hikari.leak-detection-threshold=5000
spring.datasource.hikari.register-mbeans=true

IntelliJ IDEA から Tomcat を起動 → ログイン画面(http://localhost:8080/)からログイン → jconsole で確認してみると、今度は Pool(HikariPool-1)、PoolConfig(HikariPool-1)で MBean に登録されています。

f:id:ksby:20181019044357p:plain

clean タスク実行 → Rebuild Project 実行 → build タスクを実行する

clean タスク → Rebuild Project → build タスクを実行してみます。

f:id:ksby:20181020091103p:plain .......... f:id:ksby:20181020091206p:plain

checkstyle で2点警告が出たのと、

  • Member name 'mBeanExporter' must match pattern '^[a-z][a-z0-9][a-zA-Z0-9]*$'. [MemberName]
  • Parameter name 'mBeanExporter' must match pattern '^[a-z]([a-z0-9][a-zA-Z0-9]*)?$'. [ParameterName]

テストも大量に失敗しました。。。 Project Tool Window で src/test を選択した後、コンテキストメニューを表示して「Run」-「All Tests」を選択して IntelliJ IDEA からテストを実行してみます。

f:id:ksby:20181020092429p:plain

失敗しているテストを見ると Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.jmx.export.MBeanExporter' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {} というエラーが発生していました。どうもテストだと MBeanExporter の bean が生成されていないようです。

ユニットテスト実行時には MBeanExporter の bean が生成されないのか?

ユニットテストではなく通常 Tomcat を起動する時に生成される MBeanExporter の bean は org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration の mbeanExporter メソッドで生成されますが(このメソッド内に breakpoint を設定して Tomcat を起動すると止まります)、

f:id:ksby:20181020220839p:plain

ただし、例えば src/test/groovy/ksbysample/webapp/lending/SampleHelperTest.groovy のテストを実行した時には、このメソッド内に設定した breakpoint には止まりませんでした。確かにユニットテストの時には MBeanExporter の bean は生成されていないようです。

IntelliJ IDEA の Find in Path ダイアログで spring.jmx.enabled で検索してみると org.springframework.boot.test.context.SpringBootContextLoader#disableJmx というメソッドが見つかりました。

f:id:ksby:20181020220453p:plain

また org.springframework.boot.test.context.SpringBootContextLoader のクラスコメントを見ると @see SpringBootTest という記述があります。

f:id:ksby:20181020221411p:plain

この2つから以下の仕組みになっているようです。

  1. @SpringBootTest アノテーションが付加されたテストが実行されると org.springframework.boot.test.context.SpringBootContextLoader#disableJmx が呼び出される。
  2. spring.jmx.enabled=false が設定されるので、JmxAutoConfiguration クラスに付加されている条件 @ConditionalOnProperty(prefix = "spring.jmx", name = "enabled", havingValue = "true", matchIfMissing = true) と一致せず、mbeanExporter メソッドも実行されない(MBeanExporter の bean は生成されない)。

実際 org.springframework.boot.test.context.SpringBootContextLoader#disableJmx メソッド内に breakpoint を設定してから src/test/groovy/ksbysample/webapp/lending/SampleHelperTest.groovy のテストを実行すると設定した breakpoint で止まりました。

ApplicationConfig.java を変更してユニットテスト実行時には mBeanExporter.addExcludedBean("dataSource"); を呼び出さないようにする

src/main/java/ksbysample/webapp/lending/config/ApplicationConfig.java を以下のように変更します。

@Configuration
public class ApplicationConfig {

    private final ConnectionFactory connectionFactory;

    private final MessageSource messageSource;

    private final MBeanExporter mbeanExporter;

    /**
     * @param connectionFactory {@link ConnectionFactory} bean
     * @param messageSource     {@link MessageSource} bean
     * @param mbeanExporter     {@link MBeanExporter} bean
     */
    public ApplicationConfig(ConnectionFactory connectionFactory
            , MessageSource messageSource
            , @Autowired(required = false) MBeanExporter mbeanExporter) {
        this.connectionFactory = connectionFactory;
        this.messageSource = messageSource;
        this.mbeanExporter = mbeanExporter;
    }

    ..........

    /**
     * @return HikariCP の DataSource オブジェクト
     */
    @Bean
    @ConfigurationProperties("spring.datasource.hikari")
    public DataSource dataSource() {
        if (mbeanExporter != null) {
            mbeanExporter.addExcludedBean("dataSource");
        }
        return DataSourceBuilder.create()
                .type(HikariDataSource.class)
                .build();
    }
  • checkstyle の警告が出ないようにするために private final MBeanExporter mBeanExporter;private final MBeanExporter mbeanExporter; に変更します。
  • コンストラクタの引数を MBeanExporter mbeanExporter@Autowired(required = false) MBeanExporter mbeanExporter に変更します。
  • dataSource メソッドで mbeanExporter.addExcludedBean("dataSource"); を呼び出している部分を if (mbeanExporter != null) { ... } で囲みます。

再度 clean タスク実行 → Rebuild Project 実行 → build タスクを実行する

clean タスク → Rebuild Project → build タスクを実行してみると、今度は BUILD SUCCESSFUL が出力されました。

f:id:ksby:20181020224655p:plain

IntelliJ IDEA から Tomcat を起動 → ログイン画面(http://localhost:8080/)からログイン → jconsole で確認してみると、こちらも先程と同様に Pool(HikariPool-1)、PoolConfig(HikariPool-1)で MBean に登録されていました。

f:id:ksby:20181020224924p:plain

Spring Boot 2.0 から HikariCP がデフォルトになりましたが、MBean が絡むとなんか面倒です。。。

履歴

2018/10/19
初版発行。
2018/10/20
clean タスク実行 → Rebuild Project 実行 → build タスクを実行する 以降の記述を追加しました。

Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その10 )( Tomcat connection Pool → HikariCP に変更する )

概要

記事一覧はこちらです。

Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その9 )( gradle-errorprone-plugin を 0.0.16 → 0.6 にバージョンアップ。。。しようと思いましたが止めました ) の続きです。

参照したサイト・書籍

目次

  1. build.gradle を変更する
  2. application.properties を変更する
  3. logback-spring.xml を変更する
  4. dataSource Bean の実装を変更する
  5. clean タスク → Rebuild Project → build タスクを実行する
  6. JMX への登録状況を確認する

手順

build.gradle を変更する

dependencies block から implementation("org.apache.tomcat:tomcat-jdbc") を削除します。

変更後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。

application.properties を変更する

src/main/resources/application.properties を以下のように変更します。

spring.datasource.hikari.jdbc-url=jdbc:postgresql://localhost/ksbylending
spring.datasource.hikari.username=ksbylending_user
spring.datasource.hikari.password=xxxxxxxx
spring.datasource.hikari.driver-class-name=org.postgresql.Driver
spring.datasource.hikari.leak-detection-threshold=5000
spring.datasource.hikari.register-mbeans=true

src/main/resources/application-product.properties から以下の4行を削除します。

  • spring.datasource.tomcat.initialSize=10
  • spring.datasource.tomcat.maxActive=100
  • spring.datasource.tomcat.maxIdle=100
  • spring.datasource.tomcat.minIdle=10

logback-spring.xml を変更する

Tomcat connection pool の SlowQueryReport interceptor がなくなりましたので、<if condition='"${spring.profiles.active}" == "product" &amp;&amp; "${slowquery.logging.file}" != ""'> ... </if> の設定を削除します。

dataSource Bean の実装を変更する

src/main/java/ksbysample/webapp/lending/config/ApplicationConfig.java の以下の点を変更します。

    @Bean
    @ConfigurationProperties("spring.datasource.hikari")
    public DataSource dataSource() {
        return DataSourceBuilder.create()
                .type(HikariDataSource.class)
                .build();
    }
  • @ConfigurationProperties("spring.datasource.tomcat")@ConfigurationProperties("spring.datasource.hikari") に変更します。
  • .type(org.apache.tomcat.jdbc.pool.DataSource.class).type(HikariDataSource.class) に変更します。

clean タスク → Rebuild Project → build タスクを実行する

clean タスク → Rebuild Project → build タスクを実行すると BUILD SUCCESSFUL が出力されました。

f:id:ksby:20181010224407p:plain

JMX への登録状況を確認する

まずは IntelliJ IDEA から spring.profiles.acive=develop で起動した後、jconsole を起動して確認します。

f:id:ksby:20181010224802p:plain f:id:ksby:20181010225209p:plain f:id:ksby:20181010225240p:plain

次に ksbysample-webapp-lending-2.0.4-RELEASE.jar を作成して、D:\webapps\ksbysample-webapp-lending\bat\webapp_startup.bat から spring.profiles.acive=product で起動した後、jconsole を起動して確認します。

f:id:ksby:20181010225609p:plain f:id:ksby:20181010225740p:plain f:id:ksby:20181010225821p:plain

表示が違いますが、spring.profiles.acive=develop の時は implementation("com.integralblue:log4jdbc-spring-boot-starter:1.0.2") が適用されているためと思われます。src/main/resources/application.properties に spring.autoconfigure.exclude=com.integralblue.log4jdbc.spring.Log4jdbcAutoConfiguration を追加してから jconsole で見てみます。

f:id:ksby:20181010234216p:plain

今度は spring.profiles.acive=product で起動した時と同じ表示になりました。

D:\webapps\ksbysample-webapp-lending\bat\webapp_startup.bat から spring.profiles.acive=product で起動した時に本当にコネクションプーリングをしているのかを PostgreSQL の管理ツール pgAdmin4 で見てみると、ログイン画面からログインした直後に 10 セッション接続していることが確認できました。

f:id:ksby:20181011003013p:plain

また、Spring Boot + npm + Geb で入力フォームを作ってテストする ( その75 )( コネクションプーリング用ライブラリを Tomcat connection pool → HikariCP に切り替える ) の時は @EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING) を付けないと jar から起動した時に org.springframework.jmx.export.UnableToRegisterMBeanException: Unable to register MBean [HikariDataSource (HikariPool-1)] with key 'dataSource'; ... の例外が発生しましたが、今回は付けなくても例外は発生しませんでした。例外が発生する/しないの違いがよく分かりませんでした。。。

履歴

2018/10/11
初版発行。

Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その9 )( gradle-errorprone-plugin を 0.0.16 → 0.6 にバージョンアップ。。。しようと思いましたが止めました )

概要

記事一覧はこちらです。

Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その8 )( 一旦動作確認し、動作しない点があれば修正する ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • gradle-errorprone-plugin の 0.6 がリリースされていて、設定方法も大きく変わっていました。0.0.16 → 0.6 にバージョンアップしてみます。。。が、出来なかったという内容です。

参照したサイト・書籍

目次

  1. build.gradle を変更する
  2. build タスクを実行してみる

手順

build.gradle を変更する

build.gradle の以下の点を変更します。

plugins {
    id "java"
    id "eclipse"
    id "idea"
    id "org.springframework.boot" version "2.0.4.RELEASE"
    id "io.spring.dependency-management" version "1.0.6.RELEASE"
    id "groovy"
    id "checkstyle"
    id "com.github.spotbugs" version "1.6.4"
    id "pmd"
    id "net.ltgt.errorprone" version "0.6"
    id "de.undercouch.download" version "3.4.3"
}

..........

[compileJava, compileTestGroovy, compileTestJava]*.options*.encoding = "UTF-8"
[compileJava, compileTestGroovy, compileTestJava]*.options*.compilerArgs << "-Xlint:all,-options,-processing,-path"
tasks.withType(JavaCompile).configureEach {
    options.errorprone.errorproneArgs << "-Xep:RemoveUnusedImports:WARN"
    options.errorprone.errorproneArgs << "-Xep:InsecureCryptoUsage:OFF"
    options.errorprone.errorproneArgs << "-Xep:ParameterName:OFF"
}

..........

dependencies {
    ..........

    // for Error Prone ( http://errorprone.info/ )
    errorprone("com.google.errorprone:error_prone_core:${errorproneVersion}")
    compileOnly("com.google.errorprone:error_prone_annotations:${errorproneVersion}")
    // JDK 8 support
    errorproneJavac("com.google.errorprone:javac:9+181-r4173-1")

    ..........
}
  • id "net.ltgt.errorprone" version "0.0.16"id "net.ltgt.errorprone" version "0.6" に変更します。
  • [compileJava, compileTestGroovy, compileTestJava]*.options*.compilerArgs = [ ... ] で compile と error-prone のオプションを指定していたのを [compileJava, compileTestGroovy, compileTestJava]*.options*.compilerArgs << "-Xlint:all,-options,-processing,-path"tasks.withType(JavaCompile).configureEach { ... } に分けます。
  • dependencies block の以下の点を変更します。
    • errorproneJavac("com.google.errorprone:javac:9+181-r4173-1") を追加します。

変更後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。

build タスクを実行してみる

clean タスク実行 → Rebuild Project 実行 → build タスクを実行してみます。

f:id:ksby:20181006065051p:plain f:id:ksby:20181006065156p:plain

compileTestJava タスクでエラーが 34個出て build は失敗しました。エラーの内容を見ると、テストコードで Lombok を使用して自動生成させている Getter/Setter を errorproneJavac が認識できていないようです。options.errorprone.disableWarningsInGeneratedCode = true とかを指定してもエラーはなくなりませんでした。テストコードで Lombok を使用しないように変更すればエラーはなくなるのですが、そうまでして 0.6 にバージョンアップするか迷います。。。

https://github.com/tbroyer/gradle-errorprone-plugin には Error Prone requires at least a JDK 9 compiler. という記述があり JDK 9 以上が本来想定されている環境のようですので、今回は gradle-errorprone-plugin のバージョンアップは止めることにします。JDK を 8 → 11 に変える時に再度試そうと思います。

履歴

2018/10/06
初版発行。