Thorsten Schöning created AXIS2-5792:
----------------------------------------

             Summary: Add a more strict service first class loader?
                 Key: AXIS2-5792
                 URL: https://issues.apache.org/jira/browse/AXIS2-5792
             Project: Axis2
          Issue Type: Improvement
          Components: deployment
    Affects Versions: 1.7.3, 1.6.3
         Environment: Windows 8.1 x64 Prof., Tomcat 7.0.68, JDK 1.8.0_92 x64
            Reporter: Thorsten Schöning


I have a web service based on Axis2 1.6.3 currently and Axis2 provides 
httpcore-4.0. In my service I want to use HttpCore 4.4.5 and HttpClient 4.5.2 
and both core packages provide the class BasicLineFormatter, but some classes 
new in HttpCore 4.4.5 need a newer version of that class than HttpCore 4.0 can 
provide. This leads to exceptions like the following:

{CODE}
Caused by: java.lang.NoSuchFieldError: INSTANCE
        at 
org.apache.http.impl.io.DefaultHttpRequestWriterFactory.<init>(DefaultHttpRequestWriterFactory.java:53)
        at 
org.apache.http.impl.io.DefaultHttpRequestWriterFactory.<init>(DefaultHttpRequestWriterFactory.java:57)
        at 
org.apache.http.impl.io.DefaultHttpRequestWriterFactory.<clinit>(DefaultHttpRequestWriterFactory.java:47)
        at 
org.apache.http.impl.conn.ManagedHttpClientConnectionFactory.<init>(ManagedHttpClientConnectionFactory.java:82)
        at 
org.apache.http.impl.conn.ManagedHttpClientConnectionFactory.<init>(ManagedHttpClientConnectionFactory.java:95)
        at 
org.apache.http.impl.conn.ManagedHttpClientConnectionFactory.<init>(ManagedHttpClientConnectionFactory.java:104)
        at 
org.apache.http.impl.conn.ManagedHttpClientConnectionFactory.<clinit>(ManagedHttpClientConnectionFactory.java:62)
        at 
org.apache.http.impl.conn.PoolingHttpClientConnectionManager$InternalConnectionFactory.<init>(PoolingHttpClientConnectionManager.java:572)
        at 
org.apache.http.impl.conn.PoolingHttpClientConnectionManager.<init>(PoolingHttpClientConnectionManager.java:174)
        at 
org.apache.http.impl.conn.PoolingHttpClientConnectionManager.<init>(PoolingHttpClientConnectionManager.java:158)
        at 
org.apache.http.impl.conn.PoolingHttpClientConnectionManager.<init>(PoolingHttpClientConnectionManager.java:149)
        at 
org.apache.http.impl.conn.PoolingHttpClientConnectionManager.<init>(PoolingHttpClientConnectionManager.java:125)
        at 
org.apache.http.impl.conn.PoolingHttpClientConnectionManager.<init>(PoolingHttpClientConnectionManager.java:116)
{CODE}

{CODE}
Caused by: java.lang.NoClassDefFoundError: Could not initialize class 
org.apache.http.impl.conn.ManagedHttpClientConnectionFactory
        at 
org.apache.http.impl.conn.PoolingHttpClientConnectionManager$InternalConnectionFactory.<init>(PoolingHttpClientConnectionManager.java:572)
        at 
org.apache.http.impl.conn.PoolingHttpClientConnectionManager.<init>(PoolingHttpClientConnectionManager.java:174)
        at 
org.apache.http.impl.conn.PoolingHttpClientConnectionManager.<init>(PoolingHttpClientConnectionManager.java:158)
        at 
org.apache.http.impl.conn.PoolingHttpClientConnectionManager.<init>(PoolingHttpClientConnectionManager.java:149)
        at 
org.apache.http.impl.conn.PoolingHttpClientConnectionManager.<init>(PoolingHttpClientConnectionManager.java:125)
        at 
org.apache.http.impl.conn.PoolingHttpClientConnectionManager.<init>(PoolingHttpClientConnectionManager.java:116)
{CODE}

The problem is that when my service is about to be started, Axis2 used the 
BasicLineFormatter (indirectly) on its own already, loaded it and 
DeploymentClassLoader.loadClass finds that class in its own cache afterwards. 
This way the newer version of that class bundled with my service will never be 
used by default. Because the class is in the cache, using 
"EnableChildFirstClassLoading" doesn't change a thing here because the cache is 
always queried first.

I could work around that problem by changing the class loader of my service in 
ServiceLifeCycle.startUp: The new class loader is just another instance of 
DeploymentClassLoader which I put all the URLs needed for Jars and classes and 
such into, use "EnableChildFirstClassLoading" by default and provide the 
already available class loader as the new parent:

{CODE}
class WsAxis2SvcCl extends DeploymentClassLoader
[...]
        private static boolean shouldWrap(AxisService service)
        {
                String  paramValue = (String) 
service.getParameterValue("EnableSvcFirstClassLoading");
                                paramValue = 
StringUtils.defaultIfBlank(paramValue, "false");

                if (!JavaUtils.isTrueExplicitly(paramValue.trim()))
                {
                        return false;
                }

                return true;
        }
[...]
        protected WsAxis2SvcCl( Set<URL>        urls,
                                                        ClassLoader     parent)
        {
                super(urls.toArray(new URL[urls.size()]), null, parent, true);
        }
[...]
        static void wrapIf(AxisService service)
        {
                if (!WsAxis2SvcCl.shouldWrap(service))
                {
                        return;
                }

                ClassLoader     origCl  = service.getClassLoader();
                Path            svcDir  = Axis2ServiceConf.getSvcDir(service);
                Set<URL>        clUrls  = 
ClNdClassLoaderUrlsProv.getUrlsRe(svcDir.toFile());
                ClassLoader     newCl   = new WsAxis2SvcCl(clUrls, origCl);

                service.setClassLoader(newCl);
        }
{CODE}

This way the classes and jars of my service are always considered first and 
only things not found in there are forwarded to the default Axis2 class loader 
used before. In the end, my classes using HttpCore 4.4.5 get their 
BasicLineFormatter and Axis2 keeps using its own. The "trick" is that 
DeploymentClassLoader.findLoadedClass only searches the current instance, no 
parent or such, and by providing a new instance I get a clean start for loading 
the service classes without interfering with the former already present Axis2 
loader.

As I understand DeploymentClassLoader, such problems can't be addressed without 
another classloader like mine, so I woudl like to suggest exactly such an 
approach like I implemented now to be added. As I did already, one could 
configure the use of that class loader on a per service level and would 
therefore don't introduce any problems with backwards compatibility. If 
interested, I could provide more of my implementation.

Or is there any other approach I have missed to deal with same classes in 
incompatible versions like in my case?




--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to