Issue
-
We have implemented a custom code in an OSGi module that uses the Apache Axis client and is trying to invoke an external SOAP web service.
- When we try to call the external service, a ClassCastException error is produced inside the Axis Library:
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
Resolution
Root cause of the issue
The root cause of this issue is:
-
axis.jarlibrary is located in the Liferay web application classloader, outside of the OSGi context - But inside the custom OSGi module, the axis client is loading some axis classes using its own classloader from this module.
You can see the different classloaders of Liferay application on this page: Liferay Portal Classloader Hierarchy
Having the same java class loaded from different classloaders can lead to ClassCastException errors as they are internally handled by the JVM as different java classes.
Workarounds to avoid the issue
To avoid this issue, there are two possible workarounds, applying some modifications to the custom OSGi module to change the java thread classloader before calling the SOAP web service and after that, restoring the original classloader
Option 1: Change the thread classloader to the org.apache.axis.client.Callclass one
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);
}
Option 2: Change the thread classloader to the current class 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);
}
Additional Information