問題
-
OSGiモジュールにカスタムコードを実装し、 Apache Axis クライアントを使用し、外部のSOAPウェブサービスを呼び出そうとしています。
- 外部サービスを呼び出そうとすると、Axis Library内部でClassCastExceptionエラーが発生します:
Caused by: java.lang.ClassCastException: class com.liferay.util.axis.SimpleHTTPSender cannot be cast to class org.apache.axis.Handler (com.liferay.util.axis.SimpleHTTPSender is in unnamed module of loader org.apache.catalina.loader.ParallelWebappClassLoader @4a335fa8; org.apache.axis.Handler is in unnamed module of loader org.eclipse.osgi.internal.loader.EquinoxClassLoader @184313b4)
at org.apache.axis.deployment.wsdd.WSDDTargetedChain.makeNewInstance(WSDDTargetedChain.java:157)
at org.apache.axis.deployment.wsdd.WSDDDeployableItem.getNewInstance(WSDDDeployableItem.java:274)
at org.apache.axis.deployment.wsdd.WSDDDeployableItem.getInstance(WSDDDeployableItem.java:260)
at org.apache.axis.deployment.wsdd.WSDDDeployment.getTransport(WSDDDeployment.java:410)
at org.apache.axis.configuration.FileProvider.getTransport(FileProvider.java:257)
at org.apache.axis.AxisEngine.getTransport(AxisEngine.java:332)
at org.apache.axis.client.AxisClient.invoke(AxisClient.java:163)
... 121 more
Environment
- Liferay DXP 7.0、7.1、7.2、7.3、7.4
解決策
問題の根本的な原因
この問題の根本的な原因は
-
axis.jar
ライブラリは、OSGi コンテキストの外側にある Liferay Web アプリケーションのクラスローダーに配置されています。 - しかし、カスタムOSGiモジュールの内部では、軸クライアントはこのモジュールから独自のクラスローダーを使っていくつかの軸クラスをロードしています。
このページでは、Liferayアプリケーションの様々なクラスローダを見ることができます: Liferay Portal Classloader Hierarchy(クラスローダー階層)
同じJavaクラスを異なるクラスローダーから読み込むと、JVMが内部的に異なるJavaクラスとして扱うため、ClassCastExceptionエラーが発生することがあります。
問題を回避するためのワークアラウンド
この問題を回避するには、カスタムOSGiモジュールにいくつかの修正を加え、SOAPウェブサービスを呼び出す前にJavaスレッドのクラスローダーを変更し、その後、元のクラスローダーに戻すという2つの回避策が考えられます。
オプション1: スレッドクラスローダを org.apache.axis.client.Call
クラスのものに変更します。
Thread currentThread = Thread.currentThread();
ClassLoader threadClassLoader = currentThread.getContextClassLoader();
ClassLoader correctClassLoader = org.apache.axis.client.Call.class.getClassLoader();
try {
currentThread.setContextClassLoader(correctClassLoader);
// Insert here the custom code that calls the external web service
}
finally {
currentThread.setContextClassLoader(threadClassLoader);
}
オプション2: スレッドクラスローダーを現在のクラスに変更する this.getClass()
Thread currentThread = Thread.currentThread();
ClassLoader threadClassLoader = currentThread.getContextClassLoader();
ClassLoader correctClassLoader = this.getClass().getClassLoader();
try {
currentThread.setContextClassLoader(correctClassLoader);
// Insert here the custom code that calls the external web service
}
finally {
currentThread.setContextClassLoader(threadClassLoader);
}
追加情報