Wednesday, May 20, 2009

Axis2 child first class loading

With axis2 it is possible to deploy services as service archive files and modules as module archive files. Both these two types of archives can contain .class or .jar files within it. Axis2 creates a separate class loader to load these class and jar files within the archive file.
By default Axis2 uses parent first class loading. This means jvm search for a particular class in parent before search it at the current class loader. As a result of this user could not use a different version of a jar file within their archives.
I recently added the child first class loading support (this is only available with main trunk) so that users can use different versions of class files at their services.

How to enable it?
This can be globally enable by setting the 'EnableChildFirstClassLoading' parameter to true at the axis2.xml file. Further the behavior of the services or modules can be set adding the same parameter to services.xml and module.xml.

How it works?
Axis2 uses a custom class loader called DeploymentClassLoader which is extended from the java URLClassloader. URLClassloader uses a parent first approach. LoadClass method of the java classloader class looks like this,

protected synchronized Class loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClass0(name);
}
} catch (ClassNotFoundException e) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}

it first checks the parent class loader to check the class and child class loader after that. We can revise this order simply overriding this method at the DeploymentClassLoader like this,
protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class c = null;
if (!isChildFirstClassLoading) {
c = super.loadClass(name, resolve);
} else {
c = findLoadedClass(name);
if (c == null) {
try {
c = findClass(name);
} catch (Exception e) {
c = super.loadClass(name, resolve);
}
}
}
return c;
}

Here the isChildFirstClassLoading is set according to the parameters uses set above.

Thursday, May 7, 2009

Improving Axis2 client http transport performance

Although Axis2 supports a many transports http is the highly used one. Axis2 uses Apache HttpClient to implement its http transports sender. By default Axis2 creates a new http client object per invocation. This causes a new MultiThreadedHttpConnectionManager object being created for each invocation. As a result axis2 by default does not use the http 1.1 keep alive feature as well.
The solution for this problem is to re use the same HttpClient and MultiThreadedHttpConnectionManager per one thread. Axis2 client invocation may happen using one thread or using multiple threads. After reusing the MultiThreadedHttpConnectionManager has to shut down to release connections. If not this can lead to multiple tcp CLOSE_WAIT connections situation.

Following code shows this settings.

// creates a new connection manager and a http client object
MultiThreadedHttpConnectionManager httpConnectionManager = new MultiThreadedHttpConnectionManager();
HttpClient httpClient = new HttpClient(httpConnectionManager);

try {

client = new ServiceClient(null, null);
Options opts = new Options();
client.setOptions(opts);
opts.setTo(new EndpointReference("http://localhost:8085/axis2/services/TestInOutService"));
opts.setAction("urn:TestInOutService");

OMElement payload = buildSoapObject(
"" +
"IBM" +
"
"
);
System.out.println("Sending the request ...." + System.currentTimeMillis());


// set the above created objects to re use.
client.getOptions().setProperty(HTTPConstants.REUSE_HTTP_CLIENT, Constants.VALUE_TRUE);
client.getOptions().setProperty(HTTPConstants.CACHED_HTTP_CLIENT, httpClient);

// since we reuse the same http client and conneciton manager all these http messages
// will go in one tcp connection.
// this can be verified by sending the messages through tcpmon.
for (int i = 0; i < 10; i++) {
OMElement response = client.sendReceive(payload);
response.build();
}


} catch (Exception e) {
e.printStackTrace();
}
finally {
if (client != null) {
try {
client.cleanupTransport();
} catch (Exception e) {
e.printStackTrace();
}

try {
client.cleanup();
} catch (Exception e) {
e.printStackTrace();
}
}

}
try {
Thread.sleep(1000);
} catch (Exception e) {// do nothing}
}
httpConnectionManager.closeIdleConnections(0);
httpConnectionManager.shutdown();