Loading Arbitrary Classes from Arbitrary Places in Java

In Java by timfanelliLeave a Comment

One of my very favorite things about the JDK is portability… and not just the “compile-once-run-anywhere” portability Java is so famous for, but also the portability across providers. JAAS, JCE, JDBC and JAXP are few among the handful of technologies that have been rolled into the Core SDK as sevice wrappers, for which you need an implementation provider. Specifically today I’ve been concerned with the JDBC, but todays lesson in class loaders applies across the board.

As part of my current development project, I am writing a simple SQL Query Builder tool. (I know, it’s been done, but this is also my M.S. thesis and the work needs to be mine). This query tool needs to be able to connect to any JDBC database host, query meta data, and build SQL Queries based on it. Not too difficult or involved (a minor part of a larger client tool…), but I wanted to be as dynamic as possible in my implementation; which means having the ability to plug new JDBC Driver packages into the system at run-time….

My particular implementation scenario is a little more complicated than I’m making it here; jETIA is a J2EE solution and my Query Tool is a thin client on it, so in particular I need to be able plug in JDBC drivers without having to restart my J2EE server. Still, this doesn’t sound too difficult yet, right? Anyone with JDBC experience has done the familiar routine of dynamically loading a JDBC driver:

Class.forName( "org.postgresql.Driver" ).newInstance();
con = DriverManager.getConnection( 
           "jdbc:postgresql://dummy.eskeeter.net/database" );

My problem today, however, was that the class org.postgresql.Driver may or may not be in my system CLASSPATH. Throurgh the miracle of XML configuration files, I happen to know exactly where the JAR that contains org.postgresql.Driver is located, the problem is loading the class out of it. Fortunately this is a whole lot easier than it sounds.

At first, I was concerned. I knew what I needed was a ClassLoader that would extract the Class from my Jar, but for the life of me I couldn’t find anything resembling a “JARClassLoader” in the SDK Docs… I did, however, stumble across a URLClassLoader. The URLClassLoader takes as an argument to its constructor an array of java.net.URL objects, which, in my case, reference the JAR file(s) on my local file system that I need to open:

StringBuffer path = 
     new StringBuffer( 
          System.getProperty( "net.eskeeter.querytool.homedir" );
path.append( "lib" );
path.append( File.separatorChar );
path.append( "postresql.jar" );

URL url = new URL( "file://" + path.toString() );
URLClassLoader loader = 
     URLClassLoader.newInstance( new URL[]{ url } );

Class.forName( "org.postgresql.Driver", true, loader );

In this little example block I assume that I have a system property (set w/ -D on the java command line) named net.eskeeter.querytool.homedir that is the path to my applications directory. Off of that directory I have a subdirectory ‘lib’ that contains all the JAR files for all the JDBC drivers I want to support (and are not in my CLASSPATH). The StringBuffer builds the path to the particular JAR file I want, and then I use that path to instantiate a URL, and in turn a URLClassLoader. I can then use that class loader as an argument to Class.forName() — the boolean value there indicates wether or not the new instance should be initialized. Class.forName uses the specified ClassLoader to attempt loading the class org.postgresql.Driver. The class loader looks in each of the URL’s it knows about for the class, and loads the class object from the appropriate location.

It turned out to be much easier than I thought it would be, and it works great. I still wish there were some way I could just tell the Java VM that it should add “foo.jar” to the CLASSPATH and use the default class loader to open it, but I suppose that could potentially open the system up to all kinds of security risks, version control problems, and other nasty things…. Perhaps maybe in a future release it will be allowable with the use of the SecurityManager to ensure I have authorization to do that? Who knows… for now though, this worked great and wasn’t very difficult at all.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.