Monday, March 05, 2012

Grails Class.forName (loading classes dynamically)

This is a quickie for now.. I will have to come back and expand this entry when I understand more of why it happens.

simple problem. Wanted to load classes dynamically based on a domain instance name.

Simple (in theory)

String name="Domain"
String className = "com.package.test."+name+"Service"
Object o =Class.forName(className).newInstance()

But it fails with a ClassNotFoundException. (and of course the class does exist).

After a lot of tested I came to the following answer
Object o = Thread.currentThread().contextClassLoader.loadClass(className).newInstance()
Object o = SomeOtherService.getClassLoader().loadClass(className).newInstance()

Why I don't know. One tends to work in unit tests and not in application code, and the opposite for the other, so often I include both

Here's some links that may help explain.

Based on this I wrote a simple test to check the different class loaders

def testB(){
    parseCL(this.getClass().getClassLoader()) //This ClassLoader does NOT work
    parseCL(AnotherService.getClassLoader()) // This class Loader does work
def parseCL(ClassLoader cl){
    println "********************"
    println cl
    println "********************"
    println "Parent ="+cl.getParent()
    cl = cl.getParent()
    def indent = "--"
        println indent+cl
        cl = cl.getParent()
        println indent+"Parent ="+cl
        indent = indent +"--"

Note the top level classloader is different

******************** // DOES NOT LOAD CLASS DYNAMICALLY
----Parent =sun.misc.Launcher$AppClassLoader@35a16869
------Parent =sun.misc.Launcher$ExtClassLoader@77cde100
--------Parent =null
******************** // LOADS CLASS DYNAMICALLY
--Parent =sun.misc.Launcher$AppClassLoader@35a16869
----Parent =sun.misc.Launcher$ExtClassLoader@77cde100
--------Parent =null

1 comment:

Anonymous said...

Thanks a lot!