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
という実装があるので、特に無ければこれが選ばれそう。
- 調べるとDIで設定するらしい。
-
- maven: /org/apache/maven/shared/filtering/DefaultMavenFileFilter.java
- copyFileの実装を追っていくと、内部で
FileUtils.copyFile
を呼ぶ。
- copyFileの実装を追っていくと、内部で
- maven: /org/codehaus/plexus/util/FileUtils.java
- filteringが有効になっているファイルか、そうでないかで動きが変わる(判定自体は前のDefaultMavenFileFilterで行われる)
- 有効: overwriteの設定を無視して常に置き換え(filteringで置き換えるので最新になるはずだからoverwriteは意味がない、という考えだと思う。)
- 無効: ファイル更新日時(lastModified)が新しくなっているか、overwrite==trueであれば更新する
- ※filteringとは:ファイル内の
${*}
を置き換える(Spring関連ファイルなら@*@
を置き換える)
- filteringが有効になっているファイルか、そうでないかで動きが変わる(判定自体は前のDefaultMavenFileFilterで行われる)
動きは何となくわかりましたが、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の設定を渡せるようにしてほしい...)
愚痴
ネットの情報は前提条件が省かれてるものも多いので、ちゃんと検証して自分で説明できるぐらいの裏を取ってから使いたいものです。