views:

1213

answers:

2

My application uses JDBC database drivers. I load these from a jar file, db2jcc.jar in the case of DB2 which I'm currently working with. With this jar in the classpath, everything is fine, but I have a requirement to find the jar from a property in the application's config file instead - for example,

database.driver=/opt/IBM/db2/V9.5/java/db2jcc.jar

I can load the class via a URLClassLoader ok, but the problem is that I need to treat the object thus created as an explicit DB2XADataSource. For example:

URLClassLoader dbClassLoader = new URLClassLoader(new URL[]{driverJar});
xaClass = dbClassLoader.loadClass("com.ibm.db2.jcc.DB2XADataSource");

DB2XADataSource dataSource = (DB2XADataSource) xaClass.newInstance();

dataSource.setCurrentSchema(DATABASE_SCHEMA); // <- dataSource has to be a 
dataSource.setDatabaseName(DATABASE_NAME);    // DB2XADataSource to do this

(rearranged and renamed somewhat; I actually do the loadClass in the constructor of the class that contains this code, while the newInstance is in one of its methods.)

I guess I'm getting into a classloader tangle because the classloader that loaded my class is trying to find DB2XADataSource in order to do the cast, but the URL classloader is not above it in the tree. The trouble is, it being long after I should have stopped working for the day (here in the UK) I can't think how best to solve it in a vaguely neat and sane manner.

Ideas?

Thanks.

+1  A: 

The simplest approach is to just use the java.beans API (or direct reflection if you must) to invoke the setter methods.

Alternatively: Your database code requires to link to the dynamically loaded code. Therefore, dynamically load your database code. How much is up to you. You might load almost everything except the "bootstrap".

Tom Hawtin - tackline
A: 

Yep - the class can't load its own dependencies. You could do some ClassLoader magic, but I imagine it would get messy very quickly.

One way to reduce the amount of reflection would be to put any code that depends on DB2XADataSource into an implementation that is invoked via an interface available to the calling ClassLoader.

//in mydb2driver.jar
public class MyDb2Driver implements IDriver {

    private DB2XADataSource dataSource = new DB2XADataSource();

    public void init() {
        dataSource.setCurrentSchema(DATABASE_SCHEMA);
    }

    //etc.

}

This is loaded with your driver:

database.driver=/opt/IBM/db2/V9.5/java/db2jcc.jar:/foo/mydb2driver.jar

Invoking code is in the regular classpath:

public interface IDriver {
    public void init();
    //etc.
}

...

URLClassLoader dbClassLoader = new URLClassLoader(new URL[]{driverJar});
xaClass = dbClassLoader.loadClass("foo.MyDb2Driver");
IDriver dataSource = (IDriver) xaClass.newInstance();
dataSource.init();
McDowell