16

How can I add JDBC drivers at runtime to Jython? Using CLASSPATH works, but using sys.path doesn't work with zxJDBC even though the class is imported fine and can be manipulated from the Jython interpreter prompt.

Why does this work:

$ CLASSPATH=/tmp/jtds\-1.2.5.jar ./jython
*sys-package-mgr*: processing new jar, '/private/tmp/jtds-1.2.5.jar'
Jython 2.5.1 (Release_2_5_1:6813, Sep 26 2009, 13:47:54) 
[Java HotSpot(TM) 64-Bit Server VM (Apple Inc.)] on java1.6.0_20
Type "help", "copyright", "credits" or "license" for more information.
>>> from java.lang import Class
>>> Class.forName('net.sourceforge.jtds.jdbc.Driver')
<type 'net.sourceforge.jtds.jdbc.Driver'>

But this doesn't?

$ ./jython

Jython 2.5.1 (Release_2_5_1:6813, Sep 26 2009, 13:47:54) 
[Java HotSpot(TM) 64-Bit Server VM (Apple Inc.)] on java1.6.0_20
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path.extend(['/tmp/jtds-1.2.5.jar'])
>>> from java.lang import Class
>>> Class.forName('net.sourceforge.jtds.jdbc.Driver')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
        at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:169)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)

java.lang.ClassNotFoundException: java.lang.ClassNotFoundException: net.sourceforge.jtds.jdbc.Driver
>>> sys.path
['', '/home/me/pkg/jython2.5.1/Lib/site-packages/distribute-0.6.13-py2.5.egg', '/home/me/pkg/jython2.5.1/Lib', '__classpath__', '__pyclasspath__/', '/home/me/pkg/jython2.5.1/Lib/site-packages', '/home/me/pkg/jython2.5.1/Lib/site-packages/setuptools-0.6c11-py2.5.egg-info', '/tmp/jtds-1.2.5.jar']
>>> import net.sourceforge.jtds.jdbc.Driver as Driver
>>> drv = Driver()
>>> drv
jTDS 1.2.5

Does it have something to do with the classloader?

lmz
  • 1,560
  • 1
  • 9
  • 19

2 Answers2

21

Looks like even the updated link does not work anymore (at least with jython-2.5.3b3).

Here's a working version:

def importJar(jarFile):
    '''
    import a jar at runtime (needed for JDBC [Class.forName])

    adapted from http://forum.java.sun.com/thread.jspa?threadID=300557
    Author: SG Langer Jan 2007 translated the above Java to Jython
    Author: seansummers@gmail.com simplified and updated for jython-2.5.3b3

    >>> importJar('jars/jtds-1.2.5.jar')
    >>> import java.lang.Class
    >>> java.lang.Class.forName('net.sourceforge.jtds.jdbc.Driver')
    <type 'net.sourceforge.jtds.jdbc.Driver'>
    '''
    from java.net import URL, URLClassLoader
    from java.lang import ClassLoader
    from java.io import File
    m = URLClassLoader.getDeclaredMethod("addURL", [URL])
    m.accessible = 1
    m.invoke(ClassLoader.getSystemClassLoader(), [File(jarFile).toURL()])

if __name__ == '__main__':
    import doctest
    doctest.testmod()

I keep this Gist updated with my production version.

Sean Summers
  • 2,514
  • 19
  • 26
  • The code from the updated link above didn't work, but this did! (had to take out the comments) – bobfet1 Feb 20 '13 at 02:38
  • The code in the Jython 1.0 book didn't work for me either on Jython 2.5.2, but importJar did. Thanks ! – David May 06 '13 at 14:34
3

The solution is tricky, but everything is explained here.

  • 1
    It seems that the reason is because the JDBC DriverManager only uses drivers loaded using the system classloader, even though what is registered is a driver instance, not a class. Thanks for your link. For any others reading, http://stackoverflow.com/questions/288828/how-to-use-a-jdbc-driver-from-an-arbitrary-location and http://www.kfu.com/~nsayer/Java/dyn-jdbc.html may be helpful. – lmz Jun 14 '10 at 15:39
  • 5
    Link is dead. I _think_ this is the correct link: http://www.jython.org/jythonbook/en/1.0/appendixB.html#working-with-classpath – gdw2 Jan 04 '12 at 20:47
  • 1
    Link is dead. Solution is lost. Can this question be removed? – Derek Sep 13 '12 at 21:02
  • Wayback machine link: http://web.archive.org/web/20140913063113/http://www.jython.org/jythonbook/en/1.0/appendixB.html#working-with-classpath (not that it matters as the other answer stated the answer does not work anymore). – lmz May 13 '15 at 13:29