Spring Boot 1.4.x の Web アプリを 1.5.x へバージョンアップする ( その11 )( build.gradle への PMD の導入 )
概要
- 今回の手順で確認できるのは以下の内容です。
- Spring Boot 1.3.x の Web アプリを 1.4.x へバージョンアップする ( その8 )( build.gradle への checkstyle, findbugs の導入+CheckStyle-IDEA, FindBugs-IDEA Plugin の導入 ) で checkstyle と FindBugs を導入しましたが、PMD は導入していなかったので導入してみます。
参照したサイト・書籍
Gradle で PMD による静的解析を実行する
http://maku77.github.io/gradle/pmd/pmd.htmlmychaelstyle/build.gradle - Example Gradle build Java with FindBugs and PMD and CPD
https://gist.github.com/mychaelstyle/9826322How to find PMD Rulesets names in Gradle >2.0
https://stackoverflow.com/questions/25584501/how-to-find-pmd-rulesets-names-in-gradle-2-0pmd/pmd - pmd/pmd-java/src/main/resources/rulesets/java/
https://github.com/pmd/pmd/tree/master/pmd-java/src/main/resources/rulesets/javaHow can getters/setters be ignored in the PMD CommentRequired rule?
https://stackoverflow.com/questions/30869538/how-can-getters-setters-be-ignored-in-the-pmd-commentrequired-ruleHow to make a new rule set
https://pmd.github.io/pmd-5.7.0/customizing/howtomakearuleset.html#@SuppressWarnings more than one rule not working
https://stackoverflow.com/questions/22855796/suppresswarnings-more-than-one-rule-not-working
目次
- build.gradle を変更する
- build タスクを実行する
- RuleSets を1つずつ適用して出力されるメッセージを確認し、適用するか否か判断する
- java-basic
- java-braces
- java-clone
- java-codesize
- java-comments
- java-controversial
- java-coupling
- java-design
- java-empty
- java-finalizers
- java-imports
- java-logging-jakarta-commons
- java-logging-java
- java-naming
- java-optimizations
- java-strictexception
- java-strings
- java-sunsecure
- java-typeresolution
- java-unnecessary
- java-unusedcode
- pmd-project-rulesets.xml を作成し、build.gradle を変更する
- 再び build タスクを実行する
- 続きます。。。
手順
build.gradle を変更する
build.gradle を リンク先のその1の内容 に変更します。
変更後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
build タスクを実行する
clean タスク → Rebuild Project → build タスク の順に実行します。
build タスクが実行されるといろいろダウンロードされます。また
Removed misconfigured rule: LoosePackageCoupling cause: No packages or classes specified
というメッセージも出力されていました。その後 PMD のチェック処理が実行されて
1761 PMD rule violations were found.
というメッセージが出力されました。checkstyle, findbugs で警告・エラーが出なくなっているので大丈夫なのでは?と思っていましたが、かなり大量にメッセージが出ますね。。。そんなに大量に指摘されても全て対応することはできないので、RuleSet を1つずつ確認して、どのようなメッセージが出るのか、またその RuleSet を適用すべきか、を判断します。
RuleSet を1つずつ適用して出力されるメッセージを確認し、適用するか否か判断する
build.gradle の ruleSets に指定する RuleSet を1つだけにして clean タスク → Rebuild Project → build タスク の順に実行し、以下の方針で判断します。
- Web で PMD を設定している build.gradle を調べてみると
java-basic
,java-braces
の2つを適用している例がよく見られたので、この2つは必ず適用することにします。ただしメッセージが出力されるのか否かは確認します。 - それ以外は出力されるメッセージと PMD Rulesets index: Current Rulesets のマニュアルでチェックされる内容を確認して、適用するか否かを判断します。
また適用の方法については、最終的には build.gradle の ruleSets に指定する方法ではなく、別の xml ファイルで細かく条件を指定する方法に変更します。作成する xml ファイルは /config/pmd/pmd-project-rulesets.xml とし、以下の内容のファイルに RuleSet を追加していきます。
<?xml version="1.0"?> <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>custom rulesets</description> .....(ここに RuleSet を記述します)..... </ruleset>
java-basic
java-basic は何もメッセージは出ませんでした。これはそのまま適用します。
<rule ref="rulesets/java/basic.xml"/>
java-braces
java-braces は何もメッセージは出ませんでした。これはそのまま適用します。
<rule ref="rulesets/java/braces.xml"/>
java-clone
java-clone は何もメッセージは出ませんでした。
Clone Implementation でチェック内容を確認すると適用しても問題なさそうだったので、そのまま適用することにします。
<rule ref="rulesets/java/clone.xml"/>
java-codesize
java-codesize は 2 PMD rule violations were found.
という結果でした。メッセージの種類は1種類で、以下のメッセージでした。
This class has too many methods, consider refactoring it.
Code Size でチェック内容を確認した上で、以下のルールで適用することにします。
- TooManyMethods(
This class has too many methods, consider refactoring it.
)は指摘されてもすぐに修正できなさそうなので、外します。 - それ以外はそのまま適用します。
<rule ref="rulesets/java/codesize.xml"> <exclude name="TooManyMethods"/> </rule>
java-comments
java-comments は 479 PMD rule violations were found.
という結果でした。メッセージの種類は8種類で、以下のメッセージでした。
headerCommentRequirement Required
fieldCommentRequirement Required
Comment is too large: Line too long
publicMethodCommentRequirement Required
protectedMethodCommentRequirement Required
To avoid mistakes add a comment at the beginning of the ... field if you want a default access modifier
Comment is too large: Too many lines
enumCommentRequirement Required
Comments でチェック内容を確認した上で、以下のルールで適用することにします。
- CommentRequired の headerCommentRequirement(
headerCommentRequirement Required
)だけ指摘して欲しいと思ったので(checkstyle でクラスのコメントがないのをチェックできていないことに気付いたので)、これだけ適用します。
<rule ref="rulesets/java/comments.xml/CommentRequired"> <properties> <property name="fieldCommentRequirement" value="Ignored"/> <property name="publicMethodCommentRequirement" value="Ignored"/> <property name="protectedMethodCommentRequirement" value="Ignored"/> <property name="enumCommentRequirement" value="Ignored"/> </properties> </rule>
java-controversial
java-controversial は 169 PMD rule violations were found.
という結果でした。メッセージの種類は12種類で、以下のメッセージでした。
Each class should declare at least one constructor
Avoid unnecessary constructors - the compiler will generate these for you
It is a good practice to call super() in a constructor
Use explicit scoping instead of the default package private level
Found 'DD'-anomaly for variable ...
Found 'DU'-anomaly for variable ...
If you run in Java5 or newer and have concurrent access, you should use the ConcurrentHashMap implementation
A method should have only one exit point, and that should be the last statement in the method
Found 'UR'-anomaly for variable ...
Avoid using Literals in Conditional Statements
This statement may have some unnecessary parentheses
Assigning an Object to null is a code smell. Consider refactoring.
Controversial でチェック内容を確認しましたが、この RuleSet に必要性を感じなかったので、適用しないことにします。
java-coupling
java-coupling は 247 PMD rule violations were found.
という結果でした。メッセージの種類は3種類で、以下のメッセージでした。またこの RuleSets を適用すると Removed misconfigured rule: LoosePackageCoupling cause: No packages or classes specified
のメッセージも表示されました。
Potential violation of Law of Demeter (method chain calls)
Potential violation of Law of Demeter (object not created locally)
Potential violation of Law of Demeter (static property access)
Coupling でチェック内容を確認しましたが、この RuleSet に必要性を感じなかったので、適用しないことにします。
java-design
java-design は 55 PMD rule violations were found.
という結果でした。メッセージの種類は11種類で、以下のメッセージでした。
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.
Document empty method body
Consider using varargs for methods or constructors which take an array the last parameter.
Consider simply returning the value vs storing it in local variable '...'
Document empty constructor
Private field '...' could be made final; it is only initialized in the declaration or constructor.
Avoid autogenerated methods to access private fields and methods of inner / outer classes
New exception is thrown in catch block, original stack trace may be lost
A class which only has private constructors should be final
Class cannot be instantiated and does not provide any static methods or fields
Deeply nested if..then statements are hard to read
Design でチェック内容を確認した上で、以下のルールで適用することにします。
UseUtilityClass
(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.
)は Application.java や Spring Framework の @Component アノテーションを付加していて Helper クラスにしているものが検知されてしまうので、外します。UncommentedEmptyMethodBody
(Document empty method body
)は @Aspect を付加したクラスの @Pointcut アノテーションを付加したメソッドが検知されていたので、外します。MissingStaticMethodInNonInstantiatableClass
(Class cannot be instantiated and does not provide any static methods or fields
)は ValuesHelper クラスが検知されていて修正のしようがないので、外します。- それ以外はそのまま適用します。
<rule ref="rulesets/java/design.xml"> <exclude name="UseUtilityClass"/> <exclude name="UncommentedEmptyMethodBody"/> <exclude name="MissingStaticMethodInNonInstantiatableClass"/> </rule>
java-empty
java-empty は何もメッセージは出ませんでした。
Empty Code でチェック内容を確認すると指摘してもらいたい点が多かったので、そのまま適用することにします。
<rule ref="rulesets/java/empty.xml"/>
java-finalizers
java-finalizers は何もメッセージは出ませんでした。
Finalizer でチェック内容を確認すると適用しても問題なさそうだったので、そのまま適用することにします。
<rule ref="rulesets/java/finalizers.xml"/>
java-imports
java-design は 55 PMD rule violations were found.
という結果でした。メッセージの種類は1種類で、以下のメッセージでした。
Unnecessary use of fully qualified name ... due to existing import ...
Import Statements でチェック内容を確認すると適用しても問題なさそうだったので、そのまま適用することにします。
<rule ref="rulesets/java/imports.xml"/>
java-logging-jakarta-commons
java-logging-jakarta-commons は 2 PMD rule violations were found.
という結果でした。メッセージの種類は1種類で、以下のメッセージでした。
There is log block not surrounded by if
Jakarta Commons Logging でチェック内容を確認すると指摘して欲しい点が記載されているのですが、試してみると期待通り動作しないようです。
There is log block not surrounded by if
が出力されたところは確かにlogger.info(...)
しか記述しておらずif (logger.isInfoEnabled()) { ... }
は記述していなかったのですが、他にlogger.info(...)
しか記述していないところでメッセージが出ていない箇所があります。- GuardDebugLogging の rule は検知して欲しいと思ったのですが、
logger.debug(...)
だけ記述してもメッセージが何も表示されませんでした。
org.slf4j.Logger を使用しているからでしょうか。。。? 動作していないようなので、外すことにします。
java-logging-java
java-logging-java は 7 PMD rule violations were found.
という結果でした。メッセージの種類は1種類で、以下のメッセージでした。
The Logger variable declaration does not contain the static and final modifiers
Java Logging でチェック内容を確認すると適用しても問題なさそうだったので、そのまま適用することにします。
<rule ref="rulesets/java/logging-java.xml"/>
java-naming
java-naming は 175 PMD rule violations were found.
という結果でした。メッセージの種類は1種類で、以下のメッセージでした。
Variables that are final and static should be all capitals, ... is not all capitals.
Avoid excessively long variable names like ...
Avoid variables with short names like ...
Only variables that are final should contain underscores (except for underscores in standard prefix/suffix), ... is not final.
The field name indicates a constant but its modifiers do not
Variables should start with a lowercase character, ... starts with uppercase character.
Avoid short class names like ...
Naming でチェック内容を確認した上で、以下のルールで適用することにします。
- 以下の rule は必要性を感じなかったので外します。
- ShortVariable(
Avoid variables with short names like ...
) - LongVariable(
Avoid excessively long variable names like ...
) - ShortMethodName
- ShortClassName(
Avoid short class names like ...
)
- ShortVariable(
- それ以外はそのまま適用します。
<rule ref="rulesets/java/naming.xml"> <exclude name="ShortVariable"/> <exclude name="LongVariable"/> <exclude name="ShortMethodName"/> <exclude name="ShortClassName"/> </rule>
java-optimizations
java-optimizations は 527 PMD rule violations were found.
という結果でした。メッセージの種類は6種類で、以下のメッセージでした。
Parameter ... is not assigned and could be declared final
Local variable ... could be declared final
Avoid declaring a variable if it is unreferenced before a possible exit point.
Avoid instantiating new objects inside loops
Prefer StringBuffer over += for concatenating strings
Avoid using redundant field initializer for ...
Optimization でチェック内容を確認した上で、以下のルールで適用することにします。
- 以下の2つの rule はそこまで final だらけにしたくないと思ったので、外します。
- MethodArgumentCouldBeFinal(
Parameter ... is not assigned and could be declared final
) - LocalVariableCouldBeFinal(
Local variable ... could be declared final
)
- MethodArgumentCouldBeFinal(
- AvoidInstantiatingObjectsInLoops(
Avoid instantiating new objects inside loops
)は適用しますが、CSV ファイルのレコードをループでチェックしている処理の中のerrors.reject(..., new Object[]{...}, ...)
のように指摘を受けても修正しようがない箇所もあったので、その場合には@SuppressWarnings({PMD.AvoidInstantiatingObjectsInLoops})
アノテーションを付加して PMD のチェックが行われないようにします。 - UseStringBufferForStringAppends(
Prefer StringBuffer over += for concatenating strings
) は、簡単な文字列結合でも指摘を受けていて、さすがにそこまで全部 StringBuffer で処理したくないので外します。 - RedundantFieldInitializer(
Avoid using redundant field initializer for ...
)は入れた方がよいのか否かちょっと悩みましたが、例えデフォルトでも明示的に書きたい場合もあると思ったので、外すことにします。
<rule ref="rulesets/java/optimizations.xml"> <exclude name="LocalVariableCouldBeFinal"/> <exclude name="MethodArgumentCouldBeFinal"/> <exclude name="UseStringBufferForStringAppends"/> <exclude name="RedundantFieldInitializer"/> </rule>
java-strictexception
java-strictexception は 14 PMD rule violations were found.
という結果でした。メッセージの種類は3種類で、以下のメッセージでした。
A method/constructor shouldnt explicitly throw java.lang.Exception
Avoid throwing raw exception types.
Avoid catching generic exceptions such as NullPointerException, RuntimeException, Exception in try-catch block
Strict Exceptions でチェック内容を確認した上で、以下のルールで適用することにします。
- SignatureDeclareThrowsException(
A method/constructor shouldnt explicitly throw java.lang.Exception
)はthrows Exception
としか書きようがないところまで指摘されていたので、外します。 - AvoidThrowingRawExceptionTypes(
Avoid throwing raw exception types.
)は RuntimeException ではなくてきちんと場面に応じた例外を throw しようという指摘なのですが、安直に RuntimeException を使いたい時もあるので、外すことにします。 - AvoidCatchingGenericException(
Avoid catching generic exceptions such as NullPointerException, RuntimeException, Exception in try-catch block
)も Exception を catch したい時があるので外します。 - それ以外はそのまま適用します。
<rule ref="rulesets/java/strictexception.xml"> <exclude name="SignatureDeclareThrowsException"/> <exclude name="AvoidThrowingRawExceptionTypes"/> <exclude name="AvoidCatchingGenericException"/> </rule>
java-strings
java-strings は 30 PMD rule violations were found.
という結果でした。メッセージの種類は3種類で、以下のメッセージでした。
StringBuffer (or StringBuilder).append is called consecutively without reusing the target variable.
Avoid appending characters as strings in StringBuffer.append.
The String literal ... appears ... times in this file; the first occurrence is on line ...
String and StringBuffer でチェック内容を確認すると適用しても問題なさそうだったので、そのまま適用することにします。
<rule ref="rulesets/java/strings.xml"/>
java-sunsecure
java-sunsecure は何もメッセージは出ませんでした。
Security Code Guidelines でチェック内容を確認すると適用しても問題なさそうだったので、そのまま適用することにします。
<rule ref="rulesets/java/sunsecure.xml"/>
java-typeresolution
java-typeresolution は 6 PMD rule violations were found.
という結果でした。メッセージの種類は1種類で、以下のメッセージでした。
A method/constructor shouldnt explicitly throw java.lang.Exception
Type Resolution でチェック内容を確認した上で、以下のルールで適用することにします。
- SignatureDeclareThrowsException(
A method/constructor shouldnt explicitly throw java.lang.Exception
)は削除できないthrows Exception
を指摘されていたので、外します。 - それ以外の rule は適用します。
<rule ref="rulesets/java/typeresolution.xml"> <exclude name="SignatureDeclareThrowsException"/> </rule>
java-unnecessary
java-unnecessary は 41 PMD rule violations were found.
という結果でした。メッセージの種類は2種類で、以下のメッセージでした。
Avoid modifiers which are implied by the context
Useless parentheses.
Unnecessary でチェック内容を確認した上で、 以下のルールで適用することにします。
- UselessParentheses(
Useless parentheses.
)は削除できない箇所を指摘されていたので、外します。 - それ以外の rule は適用します。
<rule ref="rulesets/java/unnecessary.xml"> <exclude name="UselessParentheses"/> </rule>
java-unusedcode
java-unnecessary は 6 PMD rule violations were found.
という結果でした。メッセージの種類は2種類で、以下のメッセージでした。
Avoid unused private methods such as ...
Avoid unused local variables such as ...
Unused Code でチェック内容を確認した上で、以下のルールで適用することにします。
- この RuleSet はそのまま適用します。
- メッセージが出力された箇所で指摘を受ける必要がないところは
@SuppressWarnings({...})
アノテーションを付加して PMD のチェックが行われないようにします。
<rule ref="rulesets/java/unusedcode.xml"/>
pmd-project-rulesets.xml を作成し、build.gradle を変更する
build.gradle を リンク先のその2の内容 に変更します。
再び build タスクを実行する
clean タスク → Rebuild Project → build タスク の順に実行します。
今度は
219 PMD rule violations were found.
という結果でした。
続きます。。。
次回、指摘を受けた箇所を修正します。また今回記事を書き始めた時は PMD のバージョンは 5.7.0 だったのですが、その後で 5.8.0 が出たようです。5.8.0 に変更して確認しながら修正します。
ソースコード
build.gradle
■その1
.......... apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'idea' apply plugin: 'org.springframework.boot' apply plugin: 'de.undercouch.download' apply plugin: 'groovy' apply plugin: 'net.ltgt.errorprone' apply plugin: 'checkstyle' apply plugin: 'findbugs' apply plugin: 'pmd' .......... pmd { toolVersion = "5.7.0" sourceSets = [project.sourceSets.main] ignoreFailures = true consoleOutput = true ruleSets = [ // ruleSet の種類・説明は 以下の URL 参照 // https://github.com/pmd/pmd/tree/master/pmd-java/src/main/resources/rulesets/java // https://pmd.github.io/pmd-5.7.0/pmd-java/rules/index.html 'java-basic' , 'java-braces' , 'java-clone' , 'java-codesize' , 'java-comments' , 'java-controversial' , 'java-coupling' , 'java-design' , 'java-empty' , 'java-finalizers' , 'java-imports' , 'java-logging-jakarta-commons' , 'java-logging-java' , 'java-migrating' , 'java-naming' , 'java-optimizations' , 'java-strictexception' , 'java-strings' , 'java-sunsecure' , 'java-typeresolution' , 'java-unnecessary' , 'java-unusedcode' ] }
apply plugin: 'pmd'
を追加します。pmd { ... }
を追加します。ruleSets はjava-j2ee
,java-javabeans
,java-junit
,java-migrating
を除き一旦全部入れています。
■その2
pmd { toolVersion = "5.7.0" sourceSets = [project.sourceSets.main] ignoreFailures = true consoleOutput = true ruleSetFiles = rootProject.files("/config/pmd/pmd-project-rulesets.xml") ruleSets = [] }
ruleSetFiles = rootProject.files("/config/pmd/pmd-project-rulesets.xml")
を追加します。ruleSets = []
に変更します。
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/rulesets/java https://pmd.github.io/pmd-5.7.0/pmd-java/rules/index.html ※"pmd-5.7.0" の部分は適用しているバージョンに変更すること。 --> <rule ref="rulesets/java/basic.xml"/> <rule ref="rulesets/java/braces.xml"/> <rule ref="rulesets/java/clone.xml"/> <rule ref="rulesets/java/codesize.xml"> <exclude name="TooManyMethods"/> </rule> <rule ref="rulesets/java/comments.xml/CommentRequired"> <properties> <property name="fieldCommentRequirement" value="Ignored"/> <property name="publicMethodCommentRequirement" value="Ignored"/> <property name="protectedMethodCommentRequirement" value="Ignored"/> <property name="enumCommentRequirement" value="Ignored"/> </properties> </rule> <rule ref="rulesets/java/design.xml"> <exclude name="UseUtilityClass"/> <exclude name="UncommentedEmptyMethodBody"/> <exclude name="MissingStaticMethodInNonInstantiatableClass"/> </rule> <rule ref="rulesets/java/empty.xml"/> <rule ref="rulesets/java/finalizers.xml"/> <rule ref="rulesets/java/imports.xml"/> <rule ref="rulesets/java/logging-java.xml"/> <rule ref="rulesets/java/naming.xml"> <exclude name="ShortVariable"/> <exclude name="LongVariable"/> <exclude name="ShortMethodName"/> <exclude name="ShortClassName"/> </rule> <rule ref="rulesets/java/optimizations.xml"> <exclude name="LocalVariableCouldBeFinal"/> <exclude name="MethodArgumentCouldBeFinal"/> <exclude name="UseStringBufferForStringAppends"/> <exclude name="RedundantFieldInitializer"/> </rule> <rule ref="rulesets/java/strictexception.xml"> <exclude name="SignatureDeclareThrowsException"/> <exclude name="AvoidThrowingRawExceptionTypes"/> <exclude name="AvoidCatchingGenericException"/> </rule> <rule ref="rulesets/java/strings.xml"/> <rule ref="rulesets/java/sunsecure.xml"/> <rule ref="rulesets/java/typeresolution.xml"> <exclude name="SignatureDeclareThrowsException"/> </rule> <rule ref="rulesets/java/unnecessary.xml"> <exclude name="UselessParentheses"/> </rule> <rule ref="rulesets/java/unusedcode.xml"/> </ruleset>
履歴
2017/06/30
初版発行。