13

I'm having an issue with some of my code, I've searched and tried everything I know of, without any luck.


Scenario:

  • Application checks if JDBC driver exists, in this case, the H2 driver (org.h2.Driver).
  • If it doesn't exist, the application downloads the JDBC driver and adds it to a ClassLoader like so: (note: storageDataManager is a class of mine I use for SQL methods)

       File h2Driver = new File(directory.toString() + File.separator + "lib" + File.separator + "h2.jar");
       if (h2Driver.exists()) {
           URL[] url = new URL[0];
           try {
               url = new URL[]{h2Driver.toURI().toURL()};
               storageDataManager.setClassLoader(new URLClassLoader(url));
           } catch (MalformedURLException ignore) {}
        }
    

When the storageDataManager runs the first query, it tries to connect with the specified driver, and if it does have a ClassLoader, it uses ClassLoader instead:

if (getClassLoader() != null) {
    getLogging().debug("Loading custom class loader for H2 driver: " + getClassLoader().toString());
    Driver driver = (Driver) Class.forName("org.h2.Driver", true, getClassLoader()).newInstance();
    getLogging().debug("Loaded H2 driver: " + driver.toString() + " - " + driver.getMinorVersion() + " - " + driver.getMajorVersion());
    DriverManager.registerDriver(driver);
} else {
    getLogging().debug("Loading H2 driver.");
    Class.forName("org.h2.Driver");
}
outputDrivers();
this.con = DriverManager.getConnection(this.url, this.username, this.password);
break;

When I run the application I get this error:

"*No suitable driver found for jdbc:h2:plugins\Odin\data\OdinStorage;AUTO_RECONNECT=TRUE*"

Here's the full log:

[Debug] Loading custom class loader for H2 driver: java.net.URLClassLoader@3bf3d5f4
[Debug] Loaded H2 driver: org.h2.Driver@67257ce8 - 3 - 1
[Debug] Checking DriverManager drivers.
[Debug] Found driver #1: sun.jdbc.odbc.JdbcOdbcDriver
[Debug] Found driver #2: com.mysql.jdbc.Driver
[Debug] Found 2 drivers in DriverManager.
--------------------------- STACKTRACE ERROR ---------------------------
Class name: java.sql.DriverManager
Error message: No suitable driver found for jdbc:h2:plugins\Odin\data\OdinStorage;AUTO_RECONNECT=TRUE
Error cause: null
File name: null
Function name: getConnection
Error line: -1
--------------------------- STACKTRACE START ---------------------------
java.sql.DriverManager.getConnection(Unknown Source)
java.sql.DriverManager.getConnection(Unknown Source)
com.craftfire.commons.managers.DataManager.connect(DataManager.java:756)
com.craftfire.commons.managers.DataManager.executeQuery(DataManager.java:526)
com.craftfire.odin.managers.StorageManager.checkInventoryDatabase(StorageManager.java:65)
com.craftfire.odin.managers.StorageManager.checkDatabases(StorageManager.java:56)
com.craftfire.odin.managers.StorageManager.<init>(StorageManager.java:34)
com.craftfire.odin.managers.OdinManager.loadDatabases(OdinManager.java:206)
com.craftfire.odin.managers.OdinManager.init(OdinManager.java:75)
com.craftfire.odin.layer.bukkit.Odin.onEnable(Odin.java:63)
org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:217)
org.bukkit.plugin.java.JavaPluginLoader.enablePlugin(JavaPluginLoader.java:374)
org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:381)
org.bukkit.craftbukkit.CraftServer.loadPlugin(CraftServer.java:266)
org.bukkit.craftbukkit.CraftServer.enablePlugins(CraftServer.java:248)
org.bukkit.craftbukkit.CraftServer.<init>(CraftServer.java:200)
net.minecraft.server.ServerConfigurationManagerAbstract.<init>(ServerConfigurationManagerAbstract.java:50)
net.minecraft.server.ServerConfigurationManager.<init>(SourceFile:11)
net.minecraft.server.DedicatedServer.init(DedicatedServer.java:105)
net.minecraft.server.MinecraftServer.run(MinecraftServer.java:377)
net.minecraft.server.ThreadServerApplication.run(SourceFile:539)
---------------------------- STACKTRACE END ----------------------------

My question is then, why doesn't the driver show up in the DriverManager.getDrivers()?

Note: I do not wish to add the library to the CLASSPATH, which is why I need to find a solution to the issue I described above.

And how can I solve this issue? I simply need to load the H2 JDB driver from a jar.

I have also tried this:

  • Not use DriverManager.registerDriver(Driver), but use Class.forName("org.h2.Driver", true, getClassLoader()) instead, I've also tried with .newInstance(), neither has worked.

Could anyone provide me with a solution to this?

Thanks!

Contex
  • 141
  • 1
  • 1
  • 6

8 Answers8

5

According to the Oracle docs: http://docs.oracle.com/cd/E19501-01/819-3659/beadf/index.html

Classloaders delegate classloading to child classloaders, searching for the class on the classpath. However, the URLClassloader you used to load your library is not visible to the system or bootstrap hierarchy, so it can not find the class (despite it being loaded, albeit in another castl...classloader).

The easiest solution is to replace your system classloader with a URLClassloader and use addUrl(...path...) to load your library, as this answer suggests: How should I load Jars dynamically at runtime?

Community
  • 1
  • 1
Afforess
  • 894
  • 7
  • 15
  • 2
    As an additional note: Even registering a driver loaded with an "alien" classloader won't help, as the driver manager checks for this whenever a driver is requested, e.g. when creating a connection. See the 'isDriverAllowed(Driver driver, ClassLoader classLoader)' method in DriverManager (I checked in JDK7_u7). Therefore you cannot share a JDBC driver in between different classloader hierarchies. Using @Afforess proposed solution will fix that. – Pyranja Oct 16 '12 at 18:54
  • Personally, I think this answer is incorrect. I don't agree that it's necessary to go to all this trouble. Put the JAR in your CLASSPATH and get on with it. – duffymo Oct 16 '12 at 19:57
  • @duffymo - there are situations where you may not want to shade libraries into your project, or use alternative jdbc connectors. Just because *you* don't see a use-case does not mean there are none. – Afforess Oct 17 '12 at 16:59
  • But you are using the JDBC connector for H2; you have no choice whatsoever. You're not solving anything with this solution. You're just adding complexity by using a different class loader from the system. You haven't described any use case besides "I said so." – duffymo Oct 17 '12 at 17:42
3

I had the same problem. The h2 driver was configured in the pom.xml with

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.193</version>
</dependency>

Because I'm using Java 6 in my project (don't ask why ;-)) but the h2-1.4.193.jar from the Maven Repository depends on Java 7, this driver version could not be used.

Changing the pom.xml to use h2-1.4.190.jar solved the problem for me.

See also issue #300 in the h2database git project.

Erik_A
  • 138
  • 8
1

You must add the exact org.h2.Driver *.jar file name to the classpath of the application.

Example: /home/applications/games/minecraft/drivers/jdbcH2driver.jar

So decide where the driver will always be in your application and find the application home folder path to construct it completely when an application using it is ever installed.

Also, it probably does not operate because during download from the net it can take time and after must be copied into the "correct folder for the classpath of the application" to be able to use it and with the correct .jar name(as in the above paragraph). To successfully download the "query and application-operation should be put in a thread" to prevent overun to the next line of instructions.

Sensibly, on application start you would check the .jar was present and download it if it was not present and installed while issueing a warning to the user to download it if the .jar was not present.

Community
  • 1
  • 1
nicephotog
  • 19
  • 2
1

Hibernate properties set url value to: jdbc:h2:mem:test;DB_CLOSE_DELAY=-1

driverClassName:

<property name="driverClassName">
<value>org.hibernate.dialect.H2Dialect</value>

Add H2 driver to pom:

    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>1.3.166</version>
    </dependency>
dafali
  • 11
  • 1
1

In my case the error was in the maven scope

    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>1.4.200</version>
        <scope>test</scope>
    </dependency>

Once you remove it or change it to a visible scope it worked

PbxMan
  • 7,525
  • 1
  • 36
  • 40
0

Add the H2 JAR to your CLASSPATH.

No suitable driver usually means that the URL syntax is incorrect. Check to make sure yours complies.

http://www.h2database.com/html/faq.html

duffymo
  • 305,152
  • 44
  • 369
  • 561
  • 2
    I do not wish to add the H2 jar to my classpath, which is why I download it instead, however loading the downloaded jar is not working, which is what I'm trying to find a solution for. – Contex Oct 16 '12 at 18:24
  • You don't have a choice, and I don't see why you would not want to add H2 JAR to your CLASSPATH. That's the way Java works. – duffymo Oct 16 '12 at 19:49
0

For me, I got this error using groovy and grapes for dependency resolution.

The solution:

You need to tell grapes to use the systemClassLoader. e.g.

@GrabConfig(systemClassLoader=true)
Community
  • 1
  • 1
Nick Grealy
  • 24,216
  • 9
  • 104
  • 119
0

I have had the same problem in my Kotlin project. I have added in bild.gradle.kts

dependencies {
    implementation("com.h2database:h2:1.4.199")
..
}

and after that in IntelliJ File->Invalidate Cache.

Viktor Sirotin
  • 169
  • 1
  • 8