かんがるーさんの日記

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

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その55 )( PMD を 5.8.1 → 6.4.0 へバージョンアップする )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その54 )( webpack を 3.8.1 → 4.9.1 へバージョンアップする ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • PMD を 5.8.1 → 6.4.0 へバージョンアップします。2~3回に分ける予定です。
    • PMD 6.0.0 から Java 9 に対応しており(6.4.0 で Java 10 に対応)、Rule が8種類の Category に分類されて独自の Rulesets の書き方も変わっています。

参照したサイト・書籍

目次

  1. build.gradle を変更する
  2. 変更の方針を決める
  3. category/java/bestpractices.xml
    1. Unsupported build listener: class org.gradle.api.internal.project.ant.AntLoggingAdapterUnsupported build listener: class org.apache.tools.ant.AntClassLoader
    2. This analysis could be faster, please consider using Incremental Analysis: https://pmd.github.io/pmd-6.4.0/pmd_userdocs_getting_started.html#incremental-analysis
  4. category/java/codestyle.xml
    1. Variables that are final and static should be all capitals, '...' is not all capitals.
    2. Parameter '...' is not assigned and could be declared final
    3. Avoid excessively long variable names like ...
    4. Local variable '...' could be declared final
    5. Each class should declare at least one constructor
    6. Avoid variables with short names like ...
    7. Avoid the use of value in annotations when its the only element
    8. Avoid unnecessary constructors - the compiler will generate these for you
    9. The utility class name '...' doesn't match '[A-Z][a-zA-Z0-9]+(Utils?|Helper)'
    10. Use explicit scoping instead of the default package private level
    11. To avoid mistakes add a comment at the beginning of the ... field if you want a default access modifier
    12. A method should have only one exit point, and that should be the last statement in the method
  5. category/java/design.xml
    1. Removed misconfigured rule: LoosePackageCoupling cause: No packages or classes specified
    2. All methods are static. Consider using a utility class instead. Alternatively, you could add a private constructor or make the class abstract to silence this warning.
    3. The method '...' has a NCSS line count of ....
    4. Potential violation of Law of Demeter (method chain calls)
    5. This class has too many methods, consider refactoring it.
    6. The class '...' is suspected to be a Data Class
    7. Avoid throwing raw exception types.
    8. Rather than using a lot of String arguments, consider using a container object for those values.
    9. The method '...' has a cyclomatic complexity of ....
  6. ここまでやってみた感想

手順

build.gradle を変更する

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

pmd {
    toolVersion = "6.4.0"
    sourceSets = [project.sourceSets.main]
    ignoreFailures = true
    consoleOutput = true
    ruleSetFiles = rootProject.files("/config/pmd/pmd-project-rulesets.xml")
    ruleSets = []
}
  • toolVersion = "5.8.1"toolVersion = "6.4.0" に変更します。

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

clean タスク実行 → Rebuild Project 実行 → build タスクを実行してみますが、相変わらず大量にエラーが出ています。。。

f:id:ksby:20180602142545p:plain

変更の方針を決める

PMD Release Notes - 15-December-2017 - 6.0.0PMD Making RulesetsJava Rules を見ると、Rule の Category が一新されていて1から見直した方が良さそうに思えたので、以下の方針で変更します。

  • pmd-project-rulesets.xml は1から作り直します。build タスク実行で出力されたエラーメッセージを元に修正はしません。
  • pmd-project-rulesets.xml には <rule ref="category/java/bestpractices.xml" /> のフォーマットで Category の xml ファイルを1つずつ記述して、build タスクを実行して結果を見て調整します。
  • 現在 exclude している Rule は、基本的に同じように exclude します。

config/pmd/pmd-project-rulesets.xml を以下の内容に書き換えます。

<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="mybraces"
         xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
    <description>project rulesets</description>

    <!--
        rulesets の種類・説明は 以下の URL 参照
        https://github.com/pmd/pmd/tree/master/pmd-java/src/main/resources/category/java
        https://github.com/pmd/pmd/tree/master/pmd-java/src/main/resources/rulesets/java
        https://pmd.github.io/pmd-6.4.0/pmd_rules_java.html
        ※"pmd-6.4.0" の部分は適用しているバージョンに変更すること。
    -->
    <rule ref="category/java/bestpractices.xml"/>
    ..........
</ruleset>

<rule ref="category/java/bestpractices.xml"/> の部分に1つずつ category を記述して調整していきます。

category/java/bestpractices.xml

PMD のルール違反は出ませんでしたが、それ以外で以下のメッセージが出力されました。

:pmdMain
Unsupported build listener: class org.gradle.api.internal.project.ant.AntLoggingAdapter
Unsupported build listener: class org.apache.tools.ant.AntClassLoader
This analysis could be faster, please consider using Incremental Analysis: https://pmd.github.io/pmd-6.4.0/pmd_userdocs_getting_started.html#incremental-analysis
This analysis could be faster, please consider using Incremental Analysis: https://pmd.github.io/pmd-6.4.0/pmd_userdocs_getting_started.html#incremental-analysis

Unsupported build listener: class org.gradle.api.internal.project.ant.AntLoggingAdapterUnsupported build listener: class org.apache.tools.ant.AntClassLoader

コマンドラインから gradlew cleangradlew --debug pmdMain を実行してみたところ、以下のログが出力されました。

16:06:18.934 [WARN] [org.gradle.api.internal.project.ant.AntLoggingAdapter] Unsupported build listener: class org.gradle.api.internal.project.ant.AntLoggingAdapter
16:06:18.935 [WARN] [org.gradle.api.internal.project.ant.AntLoggingAdapter] Unsupported build listener: class org.apache.tools.ant.AntClassLoader

Unsupported build listener で調べてみると Gradle - Method details - addBuildListener) から Interface BuildListener が見つかりました。AntLoggingAdapter と AntClassLoader のクラスで BuildListener インターフェースが実装されていないのが原因のようです。

WARN のログなので、出力されたままにすることにします。

This analysis could be faster, please consider using Incremental Analysis: https://pmd.github.io/pmd-6.4.0/pmd_userdocs_getting_started.html#incremental-analysis

https://pmd.github.io/pmd-6.4.0/pmd_userdocs_getting_started.html#incremental-analysis のリンク先のページを見ると、なぜか Gradle だけリンクになっていませんね。。。

他にも調べてみると Revert "Support PMD's analysis cache (#2223)" が見つかりました。おそらく原因はこれだと思うのですが、まだ解決していないようです。対策がないようですので、出力されたままにします。

category/java/codestyle.xml

設定を追加して build タスクを実行すると 307 PMD rule violations were found. と出力されました。メッセージは以下の12種類でした。

  • Variables that are final and static should be all capitals, '...' is not all capitals.
  • Parameter '...' is not assigned and could be declared final
  • Avoid excessively long variable names like ...
  • Local variable '...' could be declared final
  • Each class should declare at least one constructor
  • Avoid variables with short names like ...
  • Avoid the use of value in annotations when its the only element
  • Avoid unnecessary constructors - the compiler will generate these for you
  • The utility class name '...' doesn't match '[A-Z][a-zA-Z0-9]+(Utils?|Helper)'
  • Use explicit scoping instead of the default package private level
  • To avoid mistakes add a comment at the beginning of the ... field if you want a default access modifier
  • A method should have only one exit point, and that should be the last statement in the method

Variables that are final and static should be all capitals, '...' is not all capitals.

VariableNamingConventions のルール違反のメッセージです。前も exclude していたので、今回も exclude します。

Parameter '...' is not assigned and could be declared final

MethodArgumentCouldBeFinal のルール違反のメッセージです。前も exclude していたので、今回も exclude します。

Avoid excessively long variable names like ...

LongVariable のルール違反のメッセージです。前も exclude していたので、今回も exclude します。

Local variable '...' could be declared final

LocalVariableCouldBeFinal のルール違反のメッセージです。前も exclude していたので、今回も exclude します。

Each class should declare at least one constructor

AtLeastOneConstructor のルール違反のメッセージです。前は何もしていませんでしたが、今のところ対応する必要性を感じなかったので exclude します。

Avoid variables with short names like ...

ShortVariable のルール違反のメッセージです。前も exclude していたので、今回も exclude します。

Avoid the use of value in annotations when its the only element

UnnecessaryAnnotationValueElement のルール違反のメッセージです。PMD 6.2.0 から新規に追加された Rule です。指摘された箇所のアノテーションは削除しようがなかったので、exclude します。

Avoid unnecessary constructors - the compiler will generate these for you

UnnecessaryConstructor のルール違反のメッセージです。

メッセージが出ているのは src/main/java/ksbysample/webapp/bootnpmgeb/config/DomaConfig.java で、ルール違反を指摘されているコンストラクタを削除しても特に問題なかったので、ソースを修正してメッセージが出ないようにします。

The utility class name '...' doesn't match '[A-Z][a-zA-Z0-9]+(Utils?|Helper)'

ClassNamingConventions のルール違反のメッセージです。

メッセージが出ているのは以下の2つのクラスですが、static のフィールド・メソッドしかないと出ているようです。

  • src/main/java/ksbysample/webapp/bootnpmgeb/constants/UrlConst.java
  • src/main/java/ksbysample/webapp/bootnpmgeb/util/validator/EmailValidator.java

個人的にクラス名を変更したくなかったので、exclude することにします。

Use explicit scoping instead of the default package private level

DefaultPackage のルール違反のメッセージです。

メッセージが出ているのは Doma 2 の Entity クラスで対応しようがなかったので、exclude することにします。

To avoid mistakes add a comment at the beginning of the ... field if you want a default access modifier

CommentDefaultAccessModifier のルール違反のメッセージです。

メッセージが出ているのは Doma 2 の Entity クラスで、DomaGen で生成した時にコメントがついていないのが原因なので(確か COMMENT ON COLUMN ... IS '...'; でカラムにコメントを付けるとコメントに出るはず)、exclude することにします。

A method should have only one exit point, and that should be the last statement in the method

OnlyOneReturn のルール違反のメッセージです。

個人的にはメソッド内に複数 return を入れても構わないと思っているので、exclude します。

以上で category/java/codestyle.xml は以下のような定義になりました。

    <rule ref="category/java/codestyle.xml">
        <exclude name="VariableNamingConventions"/>
        <exclude name="MethodArgumentCouldBeFinal"/>
        <exclude name="LongVariable"/>
        <exclude name="LocalVariableCouldBeFinal"/>
        <exclude name="AtLeastOneConstructor"/>
        <exclude name="ShortVariable"/>
        <exclude name="UnnecessaryAnnotationValueElement"/>
        <exclude name="ClassNamingConventions"/>
        <exclude name="DefaultPackage"/>
        <exclude name="CommentDefaultAccessModifier"/>
        <exclude name="OnlyOneReturn"/>
    </rule>

category/java/design.xml

設定を追加して build タスクを実行すると 95 PMD rule violations were found. と出力されました。メッセージは以下の9種類でした。

  • Removed misconfigured rule: LoosePackageCoupling cause: No packages or classes specified
  • All methods are static. Consider using a utility class instead. Alternatively, you could add a private constructor or make the class abstract to silence this warning.
  • The method '...' has a NCSS line count of ....
  • Potential violation of Law of Demeter (method chain calls)
  • This class has too many methods, consider refactoring it.
  • The class '...' is suspected to be a Data Class
  • Avoid throwing raw exception types.
  • Rather than using a lot of String arguments, consider using a container object for those values.
  • The method '...' has a cyclomatic complexity of ....

Removed misconfigured rule: LoosePackageCoupling cause: No packages or classes specified

LoosePackageCoupling の設定がないというメッセージのようです。

リンク先の説明を読みましたが、今は必要そうに思えなかったので exclude することにします。

All methods are static. Consider using a utility class instead. Alternatively, you could add a private constructor or make the class abstract to silence this warning.

UseUtilityClass のルール違反のメッセージです。

ClassNamingConventions を exclude したのと同じ理由で、こちらも exclude します。

The method '...' has a NCSS line count of ....

NcssCount のルール違反のメッセージです。PMD 6.0.0 から新規に追加された Rule です。

実はこのルールが何を指摘しているのかよく分かりませんでした。。。 対応方法もよく分からないので exclude します。

Potential violation of Law of Demeter (method chain calls)

LawOfDemeter のルール違反のメッセージです。

リンク先の説明を読みましたが、有効性がよく分からなかったので exclude します。

This class has too many methods, consider refactoring it.

TooManyMethods のルール違反のメッセージです。

このルールは残しておきたいので、メッセージが表示された以下のクラスに @SuppressWarnings("PMD.TooManyMethods") を付けることにします。

  • src/main/java/ksbysample/webapp/bootnpmgeb/aspect/logging/RequestAndResponseLogger.java

The class '...' is suspected to be a Data Class

DataClass のルール違反のメッセージです。PMD 6.0.0 から新規に追加された Rule です。

メッセージが出ていたのが Doma 2 の Entity クラスだったのですが対応のしようがないのと、このルールの必要性も感じなかったので、exclude します。

Avoid throwing raw exception types.

AvoidThrowingRawExceptionTypes のルール違反のメッセージです。

エラーが明確に分かる例外クラスではなく RuntimeException を throw していたところでメッセージが出ていました。確かにその通りなので、今回はメッセージが出ている以下のクラスに @SuppressWarnings("PMD.AvoidThrowingRawExceptionTypes") を付けることにします。

  • src/main/java/ksbysample/webapp/bootnpmgeb/helper/freemarker/FreeMarkerHelper.java
  • src/main/java/ksbysample/webapp/bootnpmgeb/values/ValuesHelper.java
  • src/main/java/ksbysample/webapp/bootnpmgeb/values/validation/ValuesEnumValidator.java

Rather than using a lot of String arguments, consider using a container object for those values.

UseObjectForClearerAPI のルール違反のメッセージです。

メソッドの引数が多い場合に Parameter クラスを新規作成してそちらでも渡せるようにした方がよい、というルールのようです。今のところ必要性を感じなかったので exclude します。

The method '...' has a cyclomatic complexity of ....

CyclomaticComplexity のルール違反のメッセージです。

1メソッド内の行数(処理量?)が多いと出るメッセージで有効だと思うのですが、今回は exclude します。

以上で category/java/codestyle.xml は以下のような定義になりました。

    <rule ref="category/java/design.xml">
        <exclude name="LoosePackageCoupling"/>
        <exclude name="UseUtilityClass"/>
        <exclude name="NcssCount"/>
        <exclude name="LawOfDemeter"/>
        <exclude name="DataClass"/>
        <exclude name="UseObjectForClearerAPI"/>
        <exclude name="CyclomaticComplexity"/>
    </rule>

ここまでやってみた感想

今回は Rule の exclude 有無を1つずつ見直していますが、単純にバージョンアップするなら以下の方針で構わないと思います。

  • build.gradle の記述については特に変更する必要はありません。
  • 独自の Rulesets は書き方が変わっているので書き直しが必要。https://github.com/pmd/pmd/tree/master/pmd-java/src/main/resources/category/java の category の一覧を見て、xml ファイルに <rule ref="category/java/bestpractices.xml"/> のフォーマットで全て列挙します。
  • その後に build タスクを実行して、メッセージが出力された Rule を全て exclude します。
  • 6 以降に追加された Rule は exclude されたままにせず確認して追加するのか exclude したままにするのか判断します。

履歴

2018/06/03
初版発行。