net.wasdev.maven.tools.targets:liberty-target が古い問題
Liberty 用の Devtool のようなものがあります.
あまり実体を把握していないので細かなことは分からないのですが、テンプレート生成でも dependency に追加されることからも、一般的に使われているようです.
今回、このライブラリと JAX-RS 2.0 の組み合わせで問題を踏んだので、暫定的な回避策を説明したいと思います.
問題
org.apache.wink.common.internal.ResponseImpl に JAX-RS 2.0 の javax.ws.rs.core.Response.getStatusInfo() が実装されていないことが問題でした. JAX-RS 2.0 上は Interfece として定義されているのですが、org.apache.wink.common.internal.ResponseImpl の実装が古く、実装が Interface と合っていないのです.
java.lang.AbstractMethodError: javax.ws.rs.core.Response.getStatusInfo()Ljavax/ws/rs/core/Response$StatusType;
at com.example.TestClass.hoge(TestClass.java:21)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
org.apache.wink.common.internal.ResponseImpl の依存は、net.wasdev.maven.tools.targets:liberty-target から注入されます. 正確には、liberty-target が依存している net.wasdev.maven.tools.targets:third-party から注入されます.
この third-party さん、2016年で更新が止まっています. これが諸悪の根源でした.
Impl を明示的に変えられるか(変えられなかった)
注入される JAX-RS の Impl を明示的に変えられれば、この問題は回避できそうです.
結論から言うと、これは不可能でした.
JAX-RS の依存注入の機構を少し見てみます.
まず、net.wasdev.maven.tools.targets:liberty-target の他に、JAX-RS の Impl として reteasy の依存を加えます.
その上で、実際に注入される Impl クラスを出力してみます.
ServiceLoader serviceLoader = ServiceLoader.load(RuntimeDelegate.class);
Iterator i = serviceLoader.iterator();
while (i.hasNext()) {
System.out.println(i.next().getClass());
}
class org.apache.wink.common.internal.runtime.RuntimeDelegateImpl
class org.jboss.resteasy.spi.ResteasyProviderFactory
結果は上記のとおり、org.apache.wink.common.internal.runtime.RuntimeDelegateImpl が1番目の要素として注入されています.
さて、JAX-RS の機構としては、Impl がどう選択されているのかというと、下記の通りです.
Iterator iterator = ServiceLoader.load(service, FactoryFinder.getContextClassLoader()).iterator();
if(iterator.hasNext()) {
return iterator.next();
}
要するに、最初の Impl が無条件で使われるわけです.
この順序ですが、ターミナル上で mvn install する場合と Eclipse 上で実行する場合でも順番が変わります.
順番を制御できる気がしなかったので、これ以上潜るのは諦めました(汗)
回避策
net.wasdev.maven.tools.targets:third-party が悪さをしているので、それを外してしまえばいいわけです.
net.wasdev.maven.tools.targets:liberty-target で third-party を含めた全依存を注入するのではなく、個別に依存を記載します.
pom.xml は以下のようになります.
これにより、org.apache.wink.common.internal.ResponseImpl が注入されることを避け、問題を回避できます.
結論
net.wasdev.maven.tools.targets:third-party がやばい.
org.apache.wink を使うなとは言わないので、せめて最新版に更新してみてほしいです
(third-party で使われているのは 1.1.1-incubating、最新版は 1.4 です)
JAX-RS 2.0 の実装時期的にも、1.4 であれば Interface の定義が全て実装されていそうな気がします.
以上です.