Mavenで静的解析を実行する

mavenのビルドで静的解析を実行して、規定のレベルに達していなければfailureを返すための設定
`mvn site`で静的解析レポートを出力する例は見かけるが、testと同じようにcheckする設定例をあまり見かけなかったので書いてみた。

# 静的解析ツール

- [checkstyle](http://checkstyle.sourceforge.net/)
- [SpotBugs](https://spotbugs.github.io/index.html)
- [PMD](https://pmd.github.io/)
- [OpenClover](https://openclover.org/)
- [JaCoCo](https://www.eclemma.org/jacoco/)

Jacocoは、OpenCloverとの比較検討のために追加していて、カバレッジのレポート出力(jacoco.exec)だけを実行し、カバレッジ検査は行なっていない。

# pom.xmlの設定例

[GitHubリポジトリ](https://github.com/kazokmr/MembersDiary/blob/master/pom.xml)の`<build/>`と`<reporting/>`を参照

- 静的解析プラグインの`check`ゴールを実行することでビルド時に検査する
- 設定例では静的解析の実行フェーズをverifyとしており、`mvn verify` で静的解析を実行してくれる
- レポートも出力したい場合は、`mvn site` を実行する。  但し後述のJenkinsのpluginでレポーティングする場合は、checkゴールを実行すれば必要なxmlファイルも出力されるので、`mvn site`　は必要ない。

#### 静的解析をverifyフェーズで実行している理由

これはOpenCloverのカバレッジ取得の仕組みが原因で、testフェーズ以前だと静的解析でエラーを検出してしまうため。

OpenCloverはテストカバレッジを取得するために `clover:setup` ゴールを実行してソースコードを改変する。この改変内容が他の静的解析でエラーで検知されることがある。
回避策としては`setup`ゴールではなく`instrument`ゴールで実施する。その結果、"正式なプロダクト用"と"カバレッジ取得用"の２回コンパイルが行われるので正式なプログラム用で静的解析が実行できる。 またテストも２回実行する。
但し、`instrument`ゴールは、２回目のコンパイルがinstallフェーズの後になってしまう。これを回避するのが`instrument-test`ゴールで、２回目のコンパイルをtestフェーズの後に実施するため、testフェーズより後であれば静的解析も正しくcheckする。 なので`prepare-package`ゴールで実行すれば、静的解析後にパッケージングすることもできる。

つまり、OpenCloverを使わないのであれば、testフェーズで静的解析を実行しても問題ない。

# Pluginの設定例

静的解析ツールを実行するためのplugin設定例をpom.xmlから抜粋した。
いずれも`<build/>`タグ内の設定内容。 
`<reporting/>`タグ内には、<denpendencies>タグ以外の設定を定義する

### Checkstyle

- checkstyle自体は8.17を指定
- Rule Configureは、プラグインが標準で持っている google_checks.xml を定義
- "error"の時のみfailureを出力する

```xml
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-checkstyle-plugin</artifactId>
  <version>3.0.0</version>
  <dependencies>
   <dependency>
     <groupId>com.puppycrawl.tools</groupId>
     <artifactId>checkstyle</artifactId>
     <version>8.17</version>
   </dependency>
 </dependencies>
 <configuration>
   <configLocation>google_checks.xml</configLocation>
   <failsOnError>true</failsOnError>
   <failOnViolation>true</failOnViolation>
   <violationSeverity>error</violationSeverity>
   <consoleOutput>true</consoleOutput>
 </configuration>
 <executions>
   <execution>
     <id>checkstyle</id>
     <phase>verify</phase>
       <goals>
         <goal>check</goal>
       </goals>
    </execution>
  </executions>
</plugin>
```

### SpotBugs

- SpotBugs自体は3.1.11を指定
- Defaultレベルを閾値としている

```xml
<plugin>
  <groupId>com.github.spotbugs</groupId>
  <artifactId>spotbugs-maven-plugin</artifactId>
  <version>3.1.11</version>
  <dependencies>
    <dependency>
      <groupId>com.github.spotbugs</groupId>
      <artifactId>spotbugs</artifactId>
      <version>3.1.11</version>
    </dependency>
  </dependencies>
  <configuration>
    <effort>Default</effort>
    <threshold>Default</threshold>
    <failOnError>true</failOnError>
    <trace>true</trace>
    <xmlOutput>true</xmlOutput>
  </configuration>
  <executions>
    <execution>
      <id>spotbugs</id>
      <phase>verify</phase>
      <goals>
        <goal>check</goal>
      </goals>
    </execution>
  </executions>
</plugin>
```

### PMD

- pmd自体は6.11で解析する
- rulesetは標準のルールを全て定義している
- "[WARNING] Removed misconfigured rule: LoosePackageCoupling  cause: No packages or classes specified"という警告が出力されるが、これはDesignルールセット内にあるLoosePackageCouplingが見つからないためである。
- cpdのチェックも一緒に実施する。(cpdはコードの重複をチェックする)
- failurePriorityでエラーとする規定値が変更できる。

```xml
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-pmd-plugin</artifactId>
  <version>3.11.0</version>
  <dependencies>
    <dependency>
      <groupId>net.sourceforge.pmd</groupId>
      <artifactId>pmd-core</artifactId>
      <version>6.11.0</version>
    </dependency>
    <dependency>
      <groupId>net.sourceforge.pmd</groupId>
      <artifactId>pmd-java</artifactId>
      <version>6.11.0</version>
    </dependency>
  </dependencies>
  <configuration>
    <rulesets>
      <ruleset>/category/java/bestpractices.xml</ruleset>
      <ruleset>/category/java/codestyle.xml</ruleset>
      <ruleset>/category/java/design.xml</ruleset>
      <ruleset>/category/java/documentation.xml</ruleset>
      <ruleset>/category/java/errorprone.xml</ruleset>
      <ruleset>/category/java/multithreading.xml</ruleset>
      <ruleset>/category/java/performance.xml</ruleset>
      <ruleset>/category/java/security.xml</ruleset>
    </rulesets>
    <targetJdk>${java.version}</targetJdk>
    <failOnViolation>true</failOnViolation>
    <failurePriority>0</failurePriority>
    <printFailingErrors>true</printFailingErrors>
    <verbose>true</verbose>
    <analysisCache>true</analysisCache>
  </configuration>
  <executions>
    <execution>
      <id>PMD</id>
      <phase>verify</phase>
      <goals>
        <goal>check</goal>
        <goal>cpd-check</goal>
      </goals>
    </execution>
  </executions>
</plugin>
```

### OpenClover

- カバレッジの割合を定義してcheckが出来、設定値を下回るとエラーとなる。
- cloverゴールを実行することで、cloverフォルダおよびclover.xmlなどを出力する

```xml
<plugin>
  <groupId>org.openclover</groupId>
  <artifactId>clover-maven-plugin</artifactId>
  <version>4.3.1</version>
  <configuration>
    <targetPercentage>50%</targetPercentage>
    <failOnViolation>true</failOnViolation>
    <generateHtml>true</generateHtml>
    <generateXml>true</generateXml>
  </configuration>
  <executions>
    <execution>
      <id>check</id>
      <phase>verify</phase>
      <goals>
        <goal>instrument-test</goal>
        <goal>check</goal>
        <goal>clover</goal>
      </goals>
    </execution>
  </executions>
</plugin>
```

### Jacoco

- Testのカバレッジを取得するだけなので、test-compile フェーズで実行する。
- カバレッジの取得時にJVNに渡すjacocoArgsを生成し、maven-surefire-pluginで定義する

```xml
<plugin>
  <groupId>org.jacoco</groupId>
  <artifactId>jacoco-maven-plugin</artifactId>
  <version>0.8.3</version>
  <executions>
    <execution>
      <id>prepare-agent</id>
      <phase>test-compile</phase>
      <goals>
        <goal>prepare-agent</goal>
      </goals>
      <configuration>
        <propertyName>jacocoArgs</propertyName>
        <includes>
          <include>*</include>
        </includes>
      </configuration>
    </execution>
  </executions>
</plugin>

# タグにjacocoArgsを定義する
<plugin>
　　<groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-surefire-plugin</artifactId>
   <version>2.22.1</version>
   <configuration>
   　　<argLine>-Xmx256m ${jacocoArgs}</argLine>
   </configuration>
</plugin>
```

# Jenkinsの設定例

JenkinsのBuildで静的解析も実行するだけならば、`mvn clean varify`とすれば良い。
解析結果をレポートしたい場合`mvn site`を実行する必要がなく、`verify`で出力されるxmlファイルをPost-Build Actionで指定してあげるだけで良い。

#### Build

![image.png](https://qiita-image-store.s3.amazonaws.com/0/160107/f86b6e87-422c-7421-d5b5-d3ba670401be.png)

#### JUnit Test Report

![image.png](https://qiita-image-store.s3.amazonaws.com/0/160107/049cce08-c6dc-4c3e-622b-349bf460059c.png)

#### OpenClover coverage report

![image.png](https://qiita-image-store.s3.amazonaws.com/0/160107/a793e7fe-858a-865b-23b3-d48d61d16532.png)

#### JaCoCo Coverage report

![image.png](https://qiita-image-store.s3.amazonaws.com/0/160107/8e6d9229-6977-1522-54c0-1dc1b24a0e94.png)


#### その他の解析レポートは、[Warnings Next Generation Plugin](https://wiki.jenkins.io/display/JENKINS/Warnings+Next+Generation+Plugin)を使う

checkstyle, findbugs, PMDなどの個別のレポートプラグインはEOLになっており、Warning Next Generation Pluginに統合されたので、今回はこちらを使用してみた。

#### checkstyle

![image.png](https://qiita-image-store.s3.amazonaws.com/0/160107/b6b63f10-be86-a88c-dea6-106978eb17ed.png)

#### spotbugs

![image.png](https://qiita-image-store.s3.amazonaws.com/0/160107/a18e64e1-d862-f579-294e-60b6795b56a9.png)

#### pmd

![image.png](https://qiita-image-store.s3.amazonaws.com/0/160107/7da2d04a-aff7-3f11-0774-17d91c1ba983.png)

#### cmd

![image.png](https://qiita-image-store.s3.amazonaws.com/0/160107/ff64fe8e-1b25-8b20-d3be-b63ff6f49bdc.png)

# 感想

検証に使ったソースコードではそれほどテストコードも無いにも関わらず、Jenkinsのビルド時間が7−8分掛かった。
(ローカルPCでJenkinsを動かしていることもあるだろうが)OpenCloverのために２回compileやtestを行なっているのも原因と思う。
OpenCloverの方がJaCoCoに比べ解析できる内容が広いが、OpenCloverを使うことでの副作用を考えるとJaCoCoだけを使った方が用意場合もある。

# 参考リンク

[Apache Maven Plugins](https://maven.apache.org/plugins/index.html#)
[Apache Maven Chackstyle Plugin](https://maven.apache.org/plugins/maven-checkstyle-plugin/)
[SpotBugs Maven Plugin](https://spotbugs.github.io/spotbugs-maven-plugin/)
[Apache Maven PMD Plugin](https://maven.apache.org/plugins/maven-pmd-plugin/index.html)
[Clover Maven Plugin](http://openclover.org/doc/maven/4.3.1/index.html)
[OpenCloverでカバレッジを取得する](https://kazuhira-r.hatenablog.com/entry/20171104/1509800116)
[THE ULTIMATE CODE QUALITY SETUP FOR YOUR AEM PROJECT](https://blogs.perficientdigital.com/2018/08/23/the-ultimate-code-quality-setup-for-your-aem-project/)