【Maven】リソースファイルのコピーで異常なファイルになるときの解決方法の紹介

Mavenのmaven-resources-pluginを使用してリソースファイルのコピーをするときは、ビルドスクリプトの指定方法によりコピー元とコピー先でファイル内容が異なる問題が発生することがあります。

今回は、ファイル内容が異なる問題が発生する原因と解決方法を紹介していきます。

maven-war-plugin(warを作成するためのプラグイン)でも、maven-resources-pluginを使用しているため同じ問題が発生します。

スポンサーリンク

検証環境

検証に使用した環境/ライブラリを次に記載します。

  • Apache Maven
    • バージョン:3.6.3
  • maven-resources-plugin
    • バージョン:3.1.0、3.2.0

maven-resources-pluginは、バージョンにより発生するエラー内容が違います。
動作の違いについては補足情報で説明します。

プロジェクト構成

検証するためのプロジェクト構成を次に記載します。テキストファイル、画像ファイルをコピーする想定で検証していきます。

project_name
└─src
    └─main
        ├─java
        └─resources
              example-sjis.txt
              example-utf8.txt
              example.bmp
              example.jpg
              example.png

「example-sjis.txt」は、sjis形式で保存したテキストファイルになります。
「example-utf8.txt」は、utf-8形式で保存したテキストファイルになります。
そのほかは、拡張子にあわせて任意のファイルを作成してください。

ビルドスクリプト

検証に使用するMavenビルドスクリプトです。

ビルドスクリプト(正常)

リソースファイルコピーにより、コピー元とコピー先でファイル内容に違いが発生しないビルドスクリプトを次に記載します。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.fumidzuki.example</groupId>
  <artifactId>maven</artifactId>
  <packaging>war</packaging>
  <version>0.0.1</version>
  <name>example-maven</name>
  <url>http://maven.apache.org</url>
  <build>
    <finalName>example-maven</finalName>
    <sourceDirectory>src/main/java</sourceDirectory>
    <outputDirectory>src/main/webapp/WEB-INF/classes</outputDirectory>
    <testSourceDirectory>src/test/java</testSourceDirectory>
    <testOutputDirectory>src/test/webapp/WEB-INF/test-classes</testOutputDirectory>
    <resources>
      <resource>
        <directory>${project.basedir}/src/main/resources</directory>
      </resource>
    </resources>
    <testResources>
      <testResource>
        <directory>${project.basedir}/src/test/resources</directory>
      </testResource>
    </testResources>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.1.0</version>
      </plugin>
    </plugins>
  </build>
</project>

ビルドスクリプト(異常)

リソースファイルコピーにより、コピー元とコピー先でファイル内容に違いが発生するビルドスクリプトを次に記載します。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.fumidzuki.example</groupId>
  <artifactId>maven</artifactId>
  <packaging>war</packaging>
  <version>0.0.1</version>
  <name>example-maven</name>
  <url>http://maven.apache.org</url>
  <build>
    <finalName>example-maven</finalName>
    <sourceDirectory>src/main/java</sourceDirectory>
    <outputDirectory>src/main/webapp/WEB-INF/classes</outputDirectory>
    <testSourceDirectory>src/test/java</testSourceDirectory>
    <testOutputDirectory>src/test/webapp/WEB-INF/test-classes</testOutputDirectory>
    <resources>
      <resource>
        <directory>${project.basedir}/src/main/resources</directory>
        <filtering>true</filtering>
      </resource>
    </resources>
    <testResources>
      <testResource>
        <directory>${project.basedir}/src/test/resources</directory>
      </testResource>
    </testResources>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.1.0</version>
      </plugin>
    </plugins>
  </build>
</project>

ビルドスクリプトの設定項目の違いは、<filtering>true</filtering>だけです。

発生事象

検証環境のプロジェクト構成を使用して、ファイル内容に違いが発生するビルドスクリプトを実行すると、次の事象が発生します。

  • テキストファイル
    • 一部のファイルが、コピー元とコピー先でファイル内容に違いが発生します。
  • 画像ファイル
    • すべてのファイルが、コピー元とコピー先でファイル内容に違いが発生しません。

発生原因

フィルタリングの設定有無により、リソースファイルをコピーするときに問題が発生します。

フィルタリング機能は、リソースファイルをコピーするときにファイルに設定した変数を置換する処理を実施します。処理を実施するときに、指定した文字コードを使用して読取/書込処理を実行します。

【テキストファイルはファイル内容に違い発生する原因】

テキストファイルの場合は、コピー元の文字コードとフィルタリング機能で使用する文字コードが違うときに、ファイル内容が違うファイルが作成されます。

たとえば、ファイル内容の文字コードがsjis形式、フィルタリング機能で使用する文字コードがutf-8形式の場合は、使用する文字コードが違うためファイル内容が違うファイルになります。

【画像ファイルはファイル内容に違いが発生しない原因】

画像ファイルの場合は、特定のファイル拡張子のときにフィルタリング機能の対象外になります。公式サイトでは、次の拡張子が対象外になっています。

  • jpg
  • jpeg
  • gif
  • bmp
  • png
Apache Maven Resources Plugin – Binaries Filtering

解決方法

解決方法は、次の方法を選択することができます。

  1. フィルタリング機能を使用しない。
  2. フィルタリング機能を使用しつつ特定のファイル拡張子を対象外にする。

【フィルタリング機能を使用しない】

ビルドスクリプトからフィルタリング機能の設定を削除します。ただし、設定している理由がわからないと削除することは難しいと思います。

【フィルタリング機能を使用しつつ特定のファイル拡張子を対象外にする】

ビルドスクリプトに特定のファイル拡張子をフィルタリングの対象外にする設定を追加します。

フィルタリングを設定して、特定のファイル拡張子を対象外にしたビルドスクリプトは、次のようになります。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.fumidzuki.example</groupId>
  <artifactId>maven</artifactId>
  <packaging>war</packaging>
  <version>0.0.1</version>
  <name>example-maven</name>
  <url>http://maven.apache.org</url>
  <build>
    <finalName>example-maven</finalName>
    <sourceDirectory>src/main/java</sourceDirectory>
    <outputDirectory>src/main/webapp/WEB-INF/classes</outputDirectory>
    <testSourceDirectory>src/test/java</testSourceDirectory>
    <testOutputDirectory>src/test/webapp/WEB-INF/test-classes</testOutputDirectory>
    <resources>
      <resource>
        <directory>${project.basedir}/src/main/resources</directory>
        <filtering>true</filtering>
      </resource>
    </resources>
    <testResources>
      <testResource>
        <directory>${project.basedir}/src/test/resources</directory>
      </testResource>
    </testResources>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.1.0</version>
        <configuration>
          <nonFilteredFileExtensions>
            <nonFilteredFileExtension>txt</nonFilteredFileExtension>
          </nonFilteredFileExtensions>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

修正箇所は、maven-resources-pluginのconfigurationタグに次の情報を追加しています。

<nonFilteredFileExtensions>
  <nonFilteredFileExtension>txt</nonFilteredFileExtension>
</nonFilteredFileExtensions>

フィルタリングに特定のファイル拡張子を対象外にする設定は、次の公式サイトを参考に設定しています。

Apache Maven Resources Plugin – Binaries Filtering

補足情報

文字コード指定方法

フィルタリングで使用される文字コードの指定方法です。どちらの方法でも指定することができるため、プロジェクトの状況に合わせて使用方法を変更してください。

プロジェクト全体で使用する文字コードを指定する場合は、次のように指定します。

<project ...>
 ...
 <properties>
   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   ...
 </properties>
 ..
</project>

maven-resources-pluginだけで使用する文字コードを指定する場合は、次のように指定します。

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.1.0</version>
        <configuration>
          ...
          <encoding>UTF-8</encoding>
          ...
        </configuration>
      </plugin>
    </plugins>
    ...
  </build>
  ...
</project>

設定例は、次の公式サイトの情報を引用しています。

Apache Maven Resources Plugin – Specifying a character encoding scheme

maven-resources-pluginのバージョンによる違い

maven-resources-pluginのバージョンにより、フィルタリング設定有無による動作が異なります。

【maven-resources-plugin(3.1.0)】

実行時にエラーは発生せずに正常に終了します。コピー元とコピー先でファイル内容がファイル内容に違いが発生します。

エラーが発生しないため、コピー先のファイルを使用するまで気づくことができないことがあります。

3.1.0以前のプラグインもこの動作になります。

【maven-resources-plugin(3.2.0)】

実行時にエラーが発生して異常終了します。ファイルコピーは行われません。次のようなエラー情報が出力されます。

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-cli) on project maven: Input length = 1 -> [Help 1]

デバッグ情報出力するオプションを設定して実行すると次のような情報が出力されます。

推測になりますが、指定した文字コードでファイルの読込ができないため例外が発生しているように見受けられます。

org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-cli) on project maven: Input length = 1
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:215)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:156)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:148)
	at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:117)
	at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:81)
	at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:56)
	at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
	at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:305)
	at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:192)
	at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:105)
	at org.apache.maven.cli.MavenCli.execute(MavenCli.java:957)
	at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:289)
	at org.apache.maven.cli.MavenCli.main(MavenCli.java:193)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:282)
	at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:225)
	at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:406)
	at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:347)
Caused by: org.apache.maven.plugin.MojoExecutionException: Input length = 1
	at org.apache.maven.plugins.resources.ResourcesMojo.execute(ResourcesMojo.java:362)
	at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:137)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:210)
	... 20 more
Caused by: org.apache.maven.shared.filtering.MavenFilteringException: Input length = 1
	at org.apache.maven.shared.filtering.DefaultMavenFileFilter.copyFile(DefaultMavenFileFilter.java:113)
	at org.apache.maven.shared.filtering.DefaultMavenResourcesFiltering.filterResources(DefaultMavenResourcesFiltering.java:262)
	at org.apache.maven.plugins.resources.ResourcesMojo.execute(ResourcesMojo.java:356)
	... 22 more
Caused by: java.nio.charset.MalformedInputException: Input length = 1
	at java.base/java.nio.charset.CoderResult.throwException(CoderResult.java:274)
	at java.base/sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:339)
	at java.base/sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
	at java.base/java.io.InputStreamReader.read(InputStreamReader.java:185)
	at java.base/java.io.BufferedReader.read1(BufferedReader.java:210)
	at java.base/java.io.BufferedReader.read(BufferedReader.java:287)
	at java.base/java.io.BufferedReader.fill(BufferedReader.java:161)
	at java.base/java.io.BufferedReader.read(BufferedReader.java:182)
	at org.apache.maven.shared.filtering.BoundedReader.read(BoundedReader.java:85)
	at org.apache.maven.shared.filtering.MultiDelimiterInterpolatorFilterReaderLineEnding.read(MultiDelimiterInterpolatorFilterReaderLineEnding.java:235)
	at org.apache.maven.shared.filtering.MultiDelimiterInterpolatorFilterReaderLineEnding.read(MultiDelimiterInterpolatorFilterReaderLineEnding.java:197)
	at java.base/java.io.Reader.read(Reader.java:229)
	at org.apache.maven.shared.utils.io.IOUtil.copy(IOUtil.java:199)
	at org.apache.maven.shared.utils.io.IOUtil.copy(IOUtil.java:181)
	at org.apache.maven.shared.utils.io.FileUtils.copyFile(FileUtils.java:1908)
	at org.apache.maven.shared.filtering.DefaultMavenFileFilter.copyFile(DefaultMavenFileFilter.java:98)
	... 24 more

まとめ

maven-resources-pluginでフィルタリング機能を利用する場合は注意が必要です。テキストファイルだけでなくバイナリファイルなども正しくコピーできないことがあります。

参考情報

maven-resources-pluginについては、次の公式サイトから確認することができます。

Apache Maven Resources Plugin – Introduction

maven-resources-pluginのフィルタリング機能については、次の公式サイトから確認することができます。

Apache Maven Resources Plugin – Filtering

注意事項として、画像やバイナリファイルにはフィルタリング機能を使用しないこと。ファイルが破損する可能性についての記載があります。

Warning: Do not filter files with binary content like images! This will most likely result in corrupt output.

https://maven.apache.org/plugins/maven-resources-plugin/examples/filter.html