Saturday, March 7, 2009

Loading JDBC Driver Programmatically

In one of the tools that I am writing I wanted to allow the user to choose a driver and use it to connect. It took me almost 2 days to get to a solution because the defaul implementation of JDBC does not make this a friendly story.

However, loading a jar file programmatically is no big deal. You use java.net.URLClassLoader to load the class and get an instance to the newly loaded class. Let me start with this piece of code



//lets say the jar file resides in /usr/home/user/myfile.jar
String pathToURL = "usr/home/user/myfile.jar"
URL url[] = new URL[1];
url[0] = new URL("jar:file:/"+pathToURL+"!/");

URLClassLoader classLoader = new URLClassLoader(url);
//now get the class you are interested in


Class myClass = classLoader.loadClass("org.package.MyClass");


This should all work fine until the time you want to load a JDBC driver jar file programmatically at run time. The restriction here is that the call to


DriverManager.getConnection(driverURL,prop);


will result in looking at the system class path and it appears there is no way to add your own jar files into that space. After a lot of search I found a feasible solution. There are some methods in the URLClassLoader like


protected void addURL(URL url);
protected Class findclass(String name);


As you might have guessed these are less useful since these have access modifier of protected. Had it been public then we could have done something of this sort


ClassLoader sysClassLoader = ClassLoader.getSystemClassLoader();
//and then

sysClassLoader.addURL(myJarUrl);
//hopefully this should have worked


However, as these methods are protected you cannot use them. So as far as i can think, you cannot use the DriverManager.getConnection and pass it the url since you don't have the jar file in the System ClassLoader. The solution is to implement your own ClassLoader and get the driver from their, sounds a bit weird here is how you will want to go about it


public class JDBCClassLoader extends URLClassLoader {

//override the methods of interest
@override
public Class findClass(String name) {
return super.findClass(name);
}

@override
public JDBCClassLoader(URL[] urls) {
super(urls);
}
}


So in the above code we define 2 methods of interest. 1 is the 1-arg constructor which passes on the url's array to the super implementation and the 2nd method is the findClass method that will give us back the Driver class that we want to get an instance of

In the implementation class we need to use this


String pathToJDBCJar = "/home/usr/binvij/library/mysql/mysql.jar";
URL url[] = new URL[1];
url[0] = new URL(pathToJDBCJar);

//instantiate our overriden class
JDBCClassLoader loader = new JDBCClassLoader(url);
//this should bring the class instance
Class cls = (Class) loader.findClass("com.mysql.jdbc.Driver");
//get an isntance of this class
java.sql.Driver driver = (java.sql.Driver) cls.newInstance();
//ask the driver to connect to the database
driver.connect(databaseURL,prop);


And this will load the driver and connect with the target database server.
Until next post..