`mvn package`で何度もビルドすると置き換わらないファイルが出たりする

Maven初心者です。

tl;dr

時間はかかるが間違いのない方法としては、mvn clean package とすれば大丈夫です。

また、リソースが置き換わる条件は以下のいずれかを満たした場合です。

  • filtering対象のファイル
  • 更新日時が新しくなったファイル
  • overwrite プロパティが指定された場合(-Dmaven.resources.overwrite=trueなど)

環境

Spring Bootを使った開発をしており、プロファイルを切り替えてリリース用ファイルに差し替えてビルドしたい、という感じです。

ファイル構成はこんな感じ。

  • src/main/resouces
    • application.properties: 開発用
    • logback.xml: 開発用
  • profiles/staging/resources
    • application.properties: ステージング用
    • logback.xml: ステージング用

pom.xmlもこんな感じにしている。

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
        <relativePath/>
    </parent>
    <!--中略-->
    <profiles>
      <profile>
          <id>staging</id>
          <build>
              <resources>
                <resource>
                  <directory>profiles/staging/resources</directory>
                </resource>
                <resource>
                  <directory>src/main/resources</directory>
                </resource>
              </resources>
          </build>
      </profile>
    </profiles>

問題の現象

2回目以降のmvn packageで、application.propertiesは置き換わるが、logback.xmlは置き換わらないことがあるという点。

後者については、配置先(target配下)にあるリソースファイルの更新日時を見ており、新しければ更新するようだった。

application.propertiesを特別扱いしてるのかよくわかんないし、logback.xmlが例外的なのかわからなかった。--debug付けてもよくわからない。

maven resources pluginの挙動を調べる

この辺り、誰も引っかかってないのか、ググり方が悪いのか、ネットで調べてもそれっぽい事象が見当たらない。

ならソースコードを見ろということなので、プラグインの動作からファイルのコピー周りの処理まで雑に調べてみました。
ただ、実際の動作がこの通りかどうかはわからない。。。(実装クラスが異なるかもしれない)

  • maven-resources-plugin: /org/apache/maven/plugins/resources/ResourcesMojo.java
    • (MavenのプラグインはAbstractMojoを継承して作られるらしい)
    • MavenResourcesExecutionを作ってMavenResourcesFiltering#filterResourcesに渡している。
    • MavenResourcesFilteringはインタフェースで、実装は多分DefaultMavenResourcesFiltering
  • maven: /org/apache/maven/shared/filtering/DefaultMavenResourcesFiltering.java
    • DefaultMavenResourcesFiltering#filterResources(MavenResourcesExecution)でMavenResourcesExecutionの情報を基にコピーを行う
    • コピーはMavenFileFilterインタフェースを通して行うが、実体はコード上書かれていない(private fieldなのに代入がない)。
    • 調べるとDIで設定するらしい。 DefaultMavenFileFilterという実装があるので、特に無ければこれが選ばれそう。
  • maven: /org/apache/maven/shared/filtering/DefaultMavenFileFilter.java
    • copyFileの実装を追っていくと、内部でFileUtils.copyFileを呼ぶ。
  • maven: /org/codehaus/plexus/util/FileUtils.java
    • filteringが有効になっているファイルか、そうでないかで動きが変わる(判定自体は前のDefaultMavenFileFilterで行われる)
      • 有効: overwriteの設定を無視して常に置き換え(filteringで置き換えるので最新になるはずだからoverwriteは意味がない、という考えだと思う。)
      • 無効: ファイル更新日時(lastModified)が新しくなっているか、overwrite==trueであれば更新する
    • ※filteringとは:ファイル内の${*}を置き換える(Spring関連ファイルなら@*@を置き換える)

動きは何となくわかりましたが、application.propertiesだけ該当するのはまだ解せません。
でも置き換わるということはどこかで設定されているはずです。(ビルド時にファイルが更新される、filteringが有効になっている、overwriteが有効になっている)

実効POMを見る

Eclipseを使っていると、親POMを含めて実際に実行されるpom.xmlの内容が確認できます。(Eclipseでは実効POMと呼んでる)

これでmaven-resource-pluginが使われているかなどを確認してみると、プロジェクトのpom.xmlでは定義していない内容を確認できました。

      <resource>
        <filtering>true</filtering>
        <directory>/PATH/TO/PROJECT/src/main/resources</directory>
        <includes>
          <include>**/application*.yml</include>
          <include>**/application*.yaml</include>
          <include>**/application*.properties</include>
        </includes>
      </resource>

これはSpring Boot関連のpom.xmlで定義されているようです。

**/application*.propertiesについては、filtering設定が有効なので、常に置き換わるだろうということがわかります。

他のリソースも毎回置き換えたい。

filteringを使えば置き換わるので、以下のようにすれば良さそうです。

      <resource>
        <filtering>true</filtering><!-- ここ追加 -->
        <directory>profiles/staging/resources</directory>
      </resource>

でも「filteringを適用させたくない!」という場合はこれは使えません。困った。

その場合はoverwrite設定を入れるか、一度cleanするしかなさそうです。

わかったこと

  • 今回の現象はSpring Bootの親POMを使うように設定しており、**/application*.propertiesは全部filtering対象になるので、packageするたびにapplication.propertiesが置き換わりました。
  • overwriteプロパティの設定を入れるのもありですが、全リソースに作用してしまうため、リソースファイルが多い場合は避けたほうが良いかもしれません。
  • 雑な対応としては、毎回cleanすればよく、jenkins等はmvn clean packageしておけば良さそうです

よくよく考えればcleanしなければ前のファイルが残るし、「プロファイルで指定したリソースで上書きしてほしい」ってのもMavenに伝えてないのだから、そのように動くでしょうなぁ、という感じ。(でも<resource>にoverwriteの設定を渡せるようにしてほしい...)

愚痴

ネットの情報は前提条件が省かれてるものも多いので、ちゃんと検証して自分で説明できるぐらいの裏を取ってから使いたいものです。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.