2

I have an application which is deployed on Websphere 8.5, the class loading is configured to "Parent Last". In the application, we use GroovyScriptEngine to run a groovy script file.

In this groovy script file, we use:

Thread.currentThread().getContextClassLoader().addURL( new URL("file:/test/ojdbc6.jar"))

to load external jar file. But it causes error:

groovy.lang.MissingMethodException: No signature of method: com.ibm.ws.classloader.CompoundClassLoader.addURL() is applicable for argument types: (java.net.URL) values: file:/test/ojdbc6.jar

Two things I need to mention are:

  1. the same application and groovy work fine on tomcat
  2. if I change the groovy to ClassLoader.systemClassLoader.addURL(new URL("file:/test/ojdbc6.jar")), then it works fine on Websphere as well as tomcat

My questions

  1. is this error has anything to do with "Parent Last" setting on Websphere? Why?
  2. why does the system class loader work fine?
slm
  • 15,396
  • 12
  • 109
  • 124
gfytd
  • 1,747
  • 2
  • 24
  • 47

1 Answers1

3

You have to check the javadoc, java.lang.ClassLoader, doesn't implement the addURL() method, it is implemented by java.net.URLClassLoader. The CompoundClassLoader is not extending it. So to be safe you should always check using instanceof. If not, you can try to find the correct class loader higher in the stack using the methods getParent() or getSystemClassLoader().

Q: Does this error have anything to do with the "Parent Last" setting in Websphere? Why?

Not exactly. However it is related to modularity of class loaders in WebSphere. In default settings, each module is loaded by a separate classloader and it happens that module classloader is not extending URLClassLoader. If you would switch the server classloader policy to Single for the server, then it would use single classloader for all applications however it is still a hierarchy, so it will not solve your issue. Check this link for details about WebSphere Classloaders.

Quoting the article

When the application class-loader policy is set to Single, then a single application class loader loads all EJB modules, dependency JAR files, and shared libraries in the system. When the application class-loader policy is set to Multiple, then each application receives its own class loader that is used for loading the EJB modules, dependency JAR files, and shared libraries for that application.

But for your issue maybe you should consider adding ojdbc6.jar as a shared library? Then you would not need to embed code for loading it in the application?

Here is sample code to see the classloader hierarchy:

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
do {
    System.out.println(classLoader.getClass().getName());
    System.out.println("Is URLClassLoader: " + (classLoader instanceof URLClassLoader));
    classLoader = classLoader.getParent();
} while(classLoader != null);

ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println("System: " + systemClassLoader.getClass().getName());
System.out.println("Is URL: " + (systemClassLoader instanceof URLClassLoader));

And output:

SystemOut     O com.ibm.ws.classloader.CompoundClassLoader
SystemOut     O Is URLClassLoader: false
SystemOut     O com.ibm.ws.classloader.CompoundClassLoader
SystemOut     O Is URLClassLoader: false
SystemOut     O com.ibm.ws.classloader.ExtJarClassLoader
SystemOut     O Is URLClassLoader: false
SystemOut     O com.ibm.ws.classloader.ProtectionClassLoader
SystemOut     O Is URLClassLoader: false
SystemOut     O com.ibm.ws.bootstrap.ExtClassLoader
SystemOut     O Is URLClassLoader: true
SystemOut     O org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader
SystemOut     O Is URLClassLoader: false
SystemOut     O sun.misc.Launcher$AppClassLoader
SystemOut     O Is URLClassLoader: true
SystemOut     O sun.misc.Launcher$ExtClassLoader
SystemOut     O Is URLClassLoader: true
SystemOut     O System: sun.misc.Launcher$AppClassLoader
SystemOut     O Is URLClassLoader: true
slm
  • 15,396
  • 12
  • 109
  • 124
Gas
  • 17,601
  • 4
  • 46
  • 93
  • I go to websphere admin console>>servers->server types->websphere application servers->server1->classloader policy, change it to Single, but doesn't help. – gfytd Nov 27 '14 at 02:31
  • @morven my bad - even single classloader policy is using hierarchy. Check my update. I'm not groovy expert, but if you want to load driver maybe do it via datasources (if available from groovy) or via shared library, then you will not have to hard code loading jars in application. – Gas Nov 27 '14 at 10:44
  • many thanks for your explanation. Can I just use ClassLoader.systemClassLoader.addURL(), it does work as I mentioned before, but not sure if any side-effect since I'm not familiar with it. – gfytd Nov 28 '14 at 02:02
  • @morven If the library being load is self-contained and doesn't require any other dependencies, you should be fine. But it will not be able for example to find classes from your app, as they will be loaded by application (child) class loader. There is too little details about these loaded libraries, so you need to test. – Gas Nov 28 '14 at 23:59