MavenでSpringを含む依存jarごと一つのjarにするときハマった件

タイトルが長い。

さらに詳しく言うなら、
MavenでSpringを含む依存jarごと一つのjarにしてインターネットに出られないサーバでそのjarを実行したときハマった件
です。

まず前提として、Springさんはクラスパス内にapplicationContext.xmlとかで定義したxsdファイルが見つからない場合、そのURLを見に行ってパースするようです。
このxsdファイルは、例えば「http://www.springframework.org/schema/beans/spring-beans-4.0.xsd」で言うと、spring-beans-xxx.jarの中のMETA-INF/spring.schemasの中で

http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd
http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsd
http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd
http\://www.springframework.org/schema/beans/spring-beans-3.1.xsd=org/springframework/beans/factory/xml/spring-beans-3.1.xsd
http\://www.springframework.org/schema/beans/spring-beans-3.2.xsd=org/springframework/beans/factory/xml/spring-beans-3.2.xsd
http\://www.springframework.org/schema/beans/spring-beans-4.0.xsd=org/springframework/beans/factory/xml/spring-beans-4.0.xsd
http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-4.0.xsd

のように記載されており、ここにある通り、その実体は同じくspring-beans-xxx.jarの中のorg.springframework.beans.factory.xmlパッケージの中に存在しています。

次に、依存jarごと一つのjarにまとめる場合、よく使うのはmaven-assembly-pluginです。
ということで実際にmaven-assembly-pluginでjarを作って、インターネットに出られないサーバで実行してみます。

WARN  org.springframework.util.xml.SimpleSaxErrorHandler:48 - Ignored XML validation warning
org.xml.sax.SAXParseException: schema_reference.4: 1)ドキュメントが見つからなかった、2)ドキュメントを読み取れなかった、3)ドキュメントのルート要素が<xsd:schema>ではなかったため、スキーマ・ドキュメント'http://www.springframework.org/schema/beans/spring-beans-4.0.xsd'の読取りに失敗しました。

_人人 人人_
> 突然の死 <
 ̄Y^Y^Y^Y ̄

なんぞこれーということでちょっとjarを展開してみます。
META-INFの下にはspring.schemasがありましたが、中身を見てみるとspring-beansの記述が無く、
spring-contextなどの記述がある状態。
いくつかのSpringのjarに依存していたので、どうやらそれぞれのspring.schemasが上書きに次ぐ上書きを繰り返し、最後に上書きされたやつが成果物に含まれている模様。
というわけで色々調べていると、「maven-shade-plugin」というプラグインならうまいこと出来るみたいなので、maven-assembly-pluginの部分と置き換えてみます。

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-shade-plugin</artifactId>
	<version>2.2</version>
	<configuration>
		<finalName>shade-sample</finalName>
		<transformers>
			<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
				<mainClass>com.tm8r.ShadeTest</mainClass>
			</transformer>
			<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
				<resource>META-INF/spring.handlers</resource>
			</transformer>
			<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
				<resource>META-INF/spring.schemas</resource>
			</transformer>
		</transformers>
	</configuration>
	<executions>
		<execution>
  			<phase>package</phase>
			<goals>
			  <goal>shade</goal>
			</goals>
		</execution>
	</executions>
</plugin>

こんな感じ。
「org.apache.maven.plugins.shade.resource.AppendingTransformer」を指定したtransformerを定義してやると、resourceに指定したファイルが複数あったとき、そのファイルに追記をしていくような挙動をしてくれる模様。

というわけでこれでpackageを実行してやると、spring.schemasとspring.handlersに対して、依存jarに含まれる同名ファイルの内容が全て記述されたものが成果物に含まれる形になりました。
めでたしめでたし。

スポンサーリンク