JDBC Driver Registration Deadlock?
Asked Answered
R

1

6

In one thread, a JackRabbit is being created:

"docs-on-startup" #32 prio=5 os_prio=0 tid=0x00007f730d73e800 nid=0x601d in Object.wait() [0x00007f725bffc000]
   java.lang.Thread.State: RUNNABLE
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at java.lang.Class.newInstance(Class.java:442)
    at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:380)
    at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:404)
    at java.util.ServiceLoader$1.next(ServiceLoader.java:480)
    at java.sql.DriverManager$2.run(DriverManager.java:603)
    at java.sql.DriverManager$2.run(DriverManager.java:583)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.sql.DriverManager.loadInitialDrivers(DriverManager.java:583)
    at java.sql.DriverManager.<clinit>(DriverManager.java:101)
    at org.apache.derby.jdbc.EmbeddedDriver.boot(Unknown Source)
    at org.apache.derby.jdbc.EmbeddedDriver.<clinit>(Unknown Source)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:264)
    at org.apache.jackrabbit.core.util.db.ConnectionFactory.getDriverClass(ConnectionFactory.java:261)
    at org.apache.jackrabbit.core.util.db.ConnectionFactory.createDataSource(ConnectionFactory.java:234)
    at org.apache.jackrabbit.core.util.db.ConnectionFactory.getDataSource(ConnectionFactory.java:170)
    - locked <0x000000066c71ea70> (a java.lang.Object)
    at org.apache.jackrabbit.core.persistence.pool.BundleDbPersistenceManager.getDataSource(BundleDbPersistenceManager.java:569)
    at org.apache.jackrabbit.core.persistence.pool.BundleDbPersistenceManager.init(BundleDbPersistenceManager.java:537)
    at org.apache.jackrabbit.core.persistence.pool.DerbyPersistenceManager.init(DerbyPersistenceManager.java:250)
    at org.apache.jackrabbit.core.RepositoryImpl.createPersistenceManager(RepositoryImpl.java:1375)
    at org.apache.jackrabbit.core.RepositoryImpl.createVersionManager(RepositoryImpl.java:512)
    at org.apache.jackrabbit.core.RepositoryImpl.<init>(RepositoryImpl.java:313)
    at org.apache.jackrabbit.core.RepositoryImpl.create(RepositoryImpl.java:615)

and in a different thread, a driver for Postgres is being initialized using Class.forName( "org.postgresql.Driver" ):

"schema-task-1" #37 prio=5 os_prio=0 tid=0x00007f725402b000 nid=0x6021 in Object.wait() [0x00007f725baf9000]
   java.lang.Thread.State: RUNNABLE
    at org.postgresql.Driver.register(Driver.java:730)
    at org.postgresql.Driver.<clinit>(Driver.java:70)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:264)

None of the thread is completing its task, not sure to understand why. Is there anything wrong about the way to initialize the JDBC drivers?

Radbun answered 28/4, 2016 at 9:16 Comment(0)
A
10

Looking at the stacktrace, the problem seems to be that JackRabbit

  1. Is loading the Derby embedded driver class,
  2. which triggers the registration,
  3. which triggers the classloading of java.sql.DriverManager,
  4. which triggers java.sql.DriverManager to load the driver through the JDBC 4 service loader mechanism,
  5. which triggers construction of a Derby embedded driver instance, which has to wait until classloading of step 1 has been completed.

As classloading is not reentrant (as far as I know), this causes everything to come to a standstill. The loading of the DriverManager waits for the Derby driver, which is waiting for DriverManager. When you then also try to load the PostgreSQL driver, it also has to wait for DriverManager.

This is a theory of what is causing this, and I might be wrong (because if this theory is right, I would expect it to be a more common problem). I will do some testing to see if I can reproduce this and if necessary report it to Oracle.

The real way to fix this is, is to not load drivers using Class.forName, but depend on the JDBC driver autoloading. However this does seem to point to a flaw in how DriverManager works.

A workaround could be to ensure that java.sql.DriverManager has already been classloaded (eg by calling one of its methods (eg DriverManager.getLoginTimeout()) in some initialization code before RepositoryImpl.create is called.

Another workaround could be to modify your JDBC driver jar(s) and remove the /META-INF/services/java.sql.Driver file; this will disable driver autoloading.

Assignable answered 28/4, 2016 at 9:41 Comment(1)
Thx for your input: adding DriverManager.getDrivers() before the two threads are started seems to fix the issue.Radbun

© 2022 - 2024 — McMap. All rights reserved.