Loading JDBC Drivers from JavaScript

2,587 views
Skip to first unread message

T.J. Crowder

unread,
Sep 17, 2012, 3:30:02 AM9/17/12
to ve...@googlegroups.com
Hi folks,

Loading JDBC drivers from JavaScript doesn't seem to work, and I'm curious why.

I'm working on my simple, non-generic JDBC example in JavaScript, and this code (in my JavaScript worker verticle) should, to my mind, work:

stdout.println("Loading driver");
java
.lang.Class.forName("com.mysql.jdbc.Driver");
stdout
.println("Getting connection");
conn
= java.sql.DriverManager.getConnection("jdbc:mysql://localhost/jdbcexample", "username", "password");

...but it doesn't. The Class.forName line fails saying the class can't be found. If I refer to the class directly:

stdout.println("Loading driver");
var driver = new com.mysql.jdbc.Driver();
stdout
.println("Getting connection");
conn
= java.sql.DriverManager.getConnection("jdbc:mysql://localhost/jdbcexample", "username", "password");

...then I get the driver, but the DriverManager doesn't know about it and the getConnection call fails. (I don't even know how that could happen. The JDBC driver registers itself with DriverManager during static init, and I fail to see how the two lines above -- the one creating an instance of the driver, and therefore guaranteeing [in theory] that static init has been done, and the one calling getConnection -- could be using different classloaders.)

If I bypass DriverManager entirely, it works:

stdout.println("Loading driver");
driver
= new com.mysql.jdbc.Driver();
stdout
.println("Getting connection");
props
= new java.util.Properties();
props
.put("user", "username");
props
.put("password", "password");
conn
= driver.connect("jdbc:mysql://localhost/jdbcexample", props);

..but that's not a good idea, not least because I can't be absolutely sure everything's been initialized correctly if DriverManager.getConnection doesn't work.

If I use Java code to load the driver, it works:

package com.farsightsoftware;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.DriverManager;

public class DriverBridge {

   
public static final void loadDriver(String cls)
   
throws ClassNotFoundException {
       
Class.forName(cls);
   
}

   
public static final Connection getConnection(String url, String user, String pass)
   
throws SQLException {
       
return DriverManager.getConnection(url, user, pass);
   
}
}

Then in the JavaScript worker verticle:

stdout.println("Loading driver");
com
.farsightsoftware.DriverBridge.loadDriver("com.mysql.jdbc.Driver");
stdout
.println("Getting connection");
conn
= com.farsightsoftware.DriverBridge.getConnection(
   
"jdbc:mysql://localhost/jdbcexample",
   
"username",
   
"password"
);

In addition to being surprising, it's a bit roundabout. But at least it's a functional workaround -- assuming all init has been successfully completed in that case.

It's not just the MySQL driver, I had exactly the same thing with the HyperSQL driver.

Does anyone understand what's going on here?

Thanks in advance,

-- T.J.

Brian Lalor

unread,
Sep 17, 2012, 6:05:17 AM9/17/12
to ve...@googlegroups.com
On Sep 17, 2012, at 3:30 AM, "T.J. Crowder" <t...@crowdersoftware.com> wrote:

Loading JDBC drivers from JavaScript doesn't seem to work, and I'm curious why.

I believe this is another classloading problem.  We seem to be having a fair number of issues related to this lately. :-)

From the JavaDoc for DriverManager (which is essentially deprecated):

When the method getConnection is called, the DriverManager will attempt to locate a suitable driver from amongst those loaded at initialization and those loaded explicitly using the same classloader as the current applet or application.

I believe the problem is that DriverManager is loaded from the JVM's main classpath and as such doesn't have visibility to the plugin's classpath.  I would take a look at the JavaDoc for DriverManager and read the notes related to class loading.

I think you can avoid these problems by invoking the driver's DataSource implementation directly and bypassing DriverManager.  For MySQL: http://dev.mysql.com/doc/refman/5.0/en/connector-j-reference-configuration-properties.html

T.J. Crowder

unread,
Sep 17, 2012, 7:11:10 AM9/17/12
to ve...@googlegroups.com
Hi,

Thanks for the reply.

On Monday, 17 September 2012 11:05:20 UTC+1, blalor wrote:
On Sep 17, 2012, at 3:30 AM, "T.J. Crowder" <t...@crowdersoftware.com> wrote:

Loading JDBC drivers from JavaScript doesn't seem to work, and I'm curious why.

I believe this is another classloading problem.  We seem to be having a fair number of issues related to this lately. :-)

From the JavaDoc for DriverManager (which is essentially deprecated):

When the method getConnection is called, the DriverManager will attempt to locate a suitable driver from amongst those loaded at initialization and those loaded explicitly using the same classloader as the current applet or application.

I believe the problem is that DriverManager is loaded from the JVM's main classpath and as such doesn't have visibility to the plugin's classpath.  I would take a look at the JavaDoc for DriverManager and read the notes related to class loading.

Yes, but again, note that my code is ensuring the driver has been located already, which means that the DriverManager doesn't have to load the driver class at all. If you look at the source of the MySQL Connector/J driver, here's the static init block in com.mysql.jdbc.Driver.java:

static {
   
try {
        java
.sql.DriverManager.registerDriver(new Driver());
   
} catch (SQLException E) {
       
throw new RuntimeException("Can't register driver!");
   
}
}


So if I can create an instance (and I can):

var driver = new com.mysql.jdbc.Driver();

...then I know that whatever classloader was used to load that class has (presumably) triggered the static init block, which will explicitly register the driver with the DriverManager, which I would assume would be the same DriverManager I'm referencing with the next line:

conn = java.sql.DriverManager.getConnection("jdbc:mysql://localhost/jdbcexample", "username", "password");

...but as that doesn't work, either the static init didn't happen (which seems unlikely) or I'm not talking to the same DriverManager class. This is probably the crux of the question, although I'm also confused by the Class.forName results I see when I experiment (see below).
 
I think you can avoid these problems by invoking the driver's DataSource implementation directly and bypassing DriverManager.  For MySQL: http://dev.mysql.com/doc/refman/5.0/en/connector-j-reference-configuration-properties.html

Yes, I demonstrated that I can avoid these problems using the driver directly. That's not really the point. Using DriverManager is a bit outdated I'll grant you, but my question is much more about Vert.x's class loading, why things seem to be different using JavaScript vs. Java, and why the static init on the MySQL driver doesn't seem to have worked when used from JavaScript. Using DataSource may well avoid the problem, but what I'm interested in is what the nature of the problem is, so I can understand other potential impacts on my code.

For example: If I create a jar with org.example.foo.Nifty in it and put both it and the MySQL Connector/J jar in the lib directory of a module, this code in the module's main:

try {
    stdout
.println("typeof org.example.foo.Nifty: " + typeof org.example.foo.Nifty);
    stdout
.println("Loading org.example.foo.Nifty");
    java
.lang.Class.forName("org.example.foo.Nifty");
    stdout
.println("typeof com.mysql.jdbc.Driver: " + typeof com.mysql.jdbc.Driver);
    stdout
.println("Loading com.mysql.jdbc.Driver");
    java
.lang.Class.forName("com.mysql.jdbc.Driver");
}
catch (e) {
    stdout
.println("Exception: " + (e.message || e.toString()));
}

...produces this output:

typeof org.example.foo.Nifty: function
Loading org.example.foo.Nifty
typeof com.mysql.jdbc.Driver: function
Loading com.mysql.jdbc.Driver
Exception: java.lang.ClassNotFoundException: com.mysql.jdbc.Driver

That's at least part of what I don't understand. :-)

-- T.J.

Brian Lalor

unread,
Sep 17, 2012, 7:42:05 AM9/17/12
to ve...@googlegroups.com
On Sep 17, 2012, at 7:11 AM, "T.J. Crowder" <t...@crowdersoftware.com> wrote:

java.lang.Class.forName("com.mysql.jdbc.Driver");
}
catch (e) {
    stdout
.println("Exception: " + (e.message || e.toString()));
}

...produces this output:
[…]
Exception: java.lang.ClassNotFoundException: com.mysql.jdbc.Driver

That's because Class is coming from the root class loader. If you did Nifty.class.forName() I bet you'd find the Driver. I expect DriverManager is either using Class.forName() or getClass().forName(), both of which would use the root class loader. The driver self-registration step via forName() probably doesn't actually load the class, but just registers the fully-qualified name of the class. When it goes to instantiate it, it's using the root class loader. 

Speculation, since I haven't looked at the code for DriverManager…

T.J. Crowder

unread,
Sep 17, 2012, 7:53:42 AM9/17/12
to ve...@googlegroups.com
The code you referred to doesn't use DriverManager at all, for anything. It purely tries to locate two classes, one in one jar and another in another jar. It succeeds in finding one of them, but fails to find the other, despite both jars being in the classpath. This, as I say, is one of the things confusing me. :-) 

-- T.J.

Brian Lalor

unread,
Sep 17, 2012, 8:15:44 AM9/17/12
to ve...@googlegroups.com
On Sep 17, 2012, at 7:53 AM, T.J. Crowder <t...@crowdersoftware.com> wrote:

The code you referred to doesn't use DriverManager at all, for anything. It purely tries to locate two classes, one in one jar and another in another jar. It succeeds in finding one of them, but fails to find the other, despite both jars being in the classpath. This, as I say, is one of the things confusing me. :-) 

I was jumping ahead a bit, I guess, and I definitely didn't read your JS code example thoroughly.  Sorry about that.  My theory also doesn't explain the DriverBridge that *does* work.  

What's the full stack trace for the ClassNotFoundException look like when loading the Driver via JS?

T.J. Crowder

unread,
Sep 17, 2012, 8:36:25 AM9/17/12
to ve...@googlegroups.com
Here are the rhinoException and the javaException traces (there's some redundancy):

Exception: java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
rhinoException stack trace:
org.mozilla.javascript.WrappedException: Wrapped java.lang.ClassNotFoundException: com.mysql.jdbc.Driver (file:/home/tjc/projects/vertx/mods/farsight.v1.0/main.js#18)
at org.mozilla.javascript.Context.throwAsScriptRuntimeEx(Context.java:1754)
at org.mozilla.javascript.MemberBox.invoke(MemberBox.java:148)
at org.mozilla.javascript.NativeJavaMethod.call(NativeJavaMethod.java:225)
at org.mozilla.javascript.optimizer.OptRuntime.call1(OptRuntime.java:32)
at org.mozilla.javascript.gen.file__home_tjc_projects_vertx_mods_farsight_v1_0_main_js_1._c_script_0(file:/home/tjc/projects/vertx/mods/farsight.v1.0/main.js:18)
at org.mozilla.javascript.gen.file__home_tjc_projects_vertx_mods_farsight_v1_0_main_js_1.call(file:/home/tjc/projects/vertx/mods/farsight.v1.0/main.js)
at org.mozilla.javascript.ContextFactory.doTopCall(ContextFactory.java:394)
at org.mozilla.javascript.ScriptRuntime.doTopCall(ScriptRuntime.java:3091)
at org.mozilla.javascript.gen.file__home_tjc_projects_vertx_mods_farsight_v1_0_main_js_1.call(file:/home/tjc/projects/vertx/mods/farsight.v1.0/main.js)
at org.mozilla.javascript.gen.file__home_tjc_projects_vertx_mods_farsight_v1_0_main_js_1.exec(file:/home/tjc/projects/vertx/mods/farsight.v1.0/main.js)
at org.mozilla.javascript.commonjs.module.Require.executeModuleScript(Require.java:340)
at org.mozilla.javascript.commonjs.module.Require.getExportedModuleInterface(Require.java:288)
at org.mozilla.javascript.commonjs.module.Require.requireMain(Require.java:137)
at org.vertx.java.deploy.impl.rhino.RhinoVerticle.start(RhinoVerticle.java:239)
at org.vertx.java.deploy.impl.VerticleManager$9.run(VerticleManager.java:642)
at org.vertx.java.core.impl.Context$2.run(Context.java:118)
at org.jboss.netty.channel.socket.nio.AbstractNioWorker.processEventQueue(AbstractNioWorker.java:361)
at org.jboss.netty.channel.socket.nio.AbstractNioWorker.run(AbstractNioWorker.java:245)
at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:35)
at org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:102)
at org.jboss.netty.util.internal.DeadLockProofWorker$1.run(DeadLockProofWorker.java:42)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:186)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.mozilla.javascript.MemberBox.invoke(MemberBox.java:126)
... 22 more
javaException stack trace:
java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:186)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.mozilla.javascript.MemberBox.invoke(MemberBox.java:126)
at org.mozilla.javascript.NativeJavaMethod.call(NativeJavaMethod.java:225)
at org.mozilla.javascript.optimizer.OptRuntime.call1(OptRuntime.java:32)
at org.mozilla.javascript.gen.file__home_tjc_projects_vertx_mods_farsight_v1_0_main_js_1._c_script_0(file:/home/tjc/projects/vertx/mods/farsight.v1.0/main.js:18)
at org.mozilla.javascript.gen.file__home_tjc_projects_vertx_mods_farsight_v1_0_main_js_1.call(file:/home/tjc/projects/vertx/mods/farsight.v1.0/main.js)
at org.mozilla.javascript.ContextFactory.doTopCall(ContextFactory.java:394)
at org.mozilla.javascript.ScriptRuntime.doTopCall(ScriptRuntime.java:3091)
at org.mozilla.javascript.gen.file__home_tjc_projects_vertx_mods_farsight_v1_0_main_js_1.call(file:/home/tjc/projects/vertx/mods/farsight.v1.0/main.js)
at org.mozilla.javascript.gen.file__home_tjc_projects_vertx_mods_farsight_v1_0_main_js_1.exec(file:/home/tjc/projects/vertx/mods/farsight.v1.0/main.js)
at org.mozilla.javascript.commonjs.module.Require.executeModuleScript(Require.java:340)
at org.mozilla.javascript.commonjs.module.Require.getExportedModuleInterface(Require.java:288)
at org.mozilla.javascript.commonjs.module.Require.requireMain(Require.java:137)
at org.vertx.java.deploy.impl.rhino.RhinoVerticle.start(RhinoVerticle.java:239)
at org.vertx.java.deploy.impl.VerticleManager$9.run(VerticleManager.java:642)
at org.vertx.java.core.impl.Context$2.run(Context.java:118)
at org.jboss.netty.channel.socket.nio.AbstractNioWorker.processEventQueue(AbstractNioWorker.java:361)
at org.jboss.netty.channel.socket.nio.AbstractNioWorker.run(AbstractNioWorker.java:245)
at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:35)
at org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:102)
at org.jboss.netty.util.internal.DeadLockProofWorker$1.run(DeadLockProofWorker.java:42)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)

Thanks,

-- T.J. 

Brian Lalor

unread,
Sep 17, 2012, 11:05:28 AM9/17/12
to ve...@googlegroups.com
On Sep 17, 2012, at 8:36 AM, "T.J. Crowder" <t...@crowdersoftware.com> wrote:

Here are the rhinoException and the javaException traces (there's some redundancy):

That is bizarre.  I'm out of ideas. :-(

T.J. Crowder

unread,
Sep 17, 2012, 11:19:09 AM9/17/12
to ve...@googlegroups.com
:-) Yeah, I think this is one for Tim. Thanks anyway, though.

-- T.J.

T.J. Crowder

unread,
Sep 19, 2012, 1:12:59 PM9/19/12
to ve...@googlegroups.com
FYI: Discussion of this has moved to this other thread: https://groups.google.com/d/topic/vertx/qPdsP2QD7Wk/discussion

If you have any follow-ups, please first read that thread and then post there.

-- T.J.
Reply all
Reply to author
Forward
0 new messages