-
Notifications
You must be signed in to change notification settings - Fork 338
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
java.lang.NoClassDefFoundError: java/sql/ShardingKey running Hibernate 5.3.37, c3p0 0.10.1, and Java 8 #184
Comments
Hi! This is interesting. Yeah, c3p0 generally compiles in the earliest JDK using the latest version of JDBC, but targets an earlier classfile format. The reason for that is it wants both to be able to support new JDBC methods and old installations. Usually it's fine. If someone runs something that hits new functionality on an older JVM they see c3p0-10.x compiles under JDK11, targets the JDK7 classfile format (major version 51). I can reproduce your issue very easily. I don't know why ordinary access to
Older c3p0's used the same trick, targeting older classfile versions, but introduction of the new class Looking at postgres jdbc, the current version of their classes is JDK 8 (major version 52). But they are not supporting JDBC 4.3 methods, so they can just build under Java 8. c3p0 internally tests for what JDBC methods a driver supports reflectively. JDBC 4.3 |
Oh, that last wasn't quite right, we do reflexively check the classes for the |
The surprise is that, while it is easy to reproduce the OP's problem in a REPL, running tests under Java 8 do not fail in this way, the It's weird to have a bug that reproduces trivially in a REPL that doesn't reproduce in the test environment. But we can certainly make our reflective tests for |
The problem does not reproduce under Java 9. But JDBC 4.3 classes were introduced in Java 9, so that's not surprising. As with the test applications, the problem does not reproduce under Java 8 jshell, with c3p0, mchange-commons-java, and the postgres driver in the System classpath. This is consistent with the hypothesis that more eager Class resolution by some ClassLoaders is the source of the problem. @ForceRs Any hope you could retry your application placing c3p0-0.10.1.jar and mchange-commons-java-0.3.1.jar in the System |
@swaldman Since Hibernate's doing the reflection, I have no control to "reflect on the interface rather than the implementation class". For context, here's the offending method's source code from Hibernate 5,3,37's "org\hibernate\engine\jdbc\env\internal\DefaultSchemaNameResolver.java" class:
And here are the inner class Delegates referenced (also from "org\hibernate\engine\jdbc\env\internal\DefaultSchemaNameResolver.java"):
Maybe Hibernate could do a better job assigning its schema name resolver? I am able to run with c3p0-0.9.5.5 on Java 8 and Java 17. Since c3p0-0.9.5.5 reports no vulnerabilities, I guess I'm fine. I'm more than willing to continue getting c3p0-0.10.1 to work with Java 8. I can certainly advise changes to their code above to perhaps do a simple java version check and return the SchemaNameResolverJava17Delegate (i.e., Java 1.7 delegate, not to be confused with Java 17.0) before blindly reflecting upon getSchema. I'm no Hibernate or c3p0 internals expert. |
I'd love to be able to blame hibernate, but they're using the same tricks I am, trying to support very old JVMs (c3p0-0.10.x also wants to support Java 7) while bringing forward the APIs, using reflection to decide when it's safe to hit newer functionality. It's just the combination of the fact that JDBC 4.3 introduces new interfaces referenced from
You've nothing to apologize for! Thank you for the careful report. So they are set as I most easily reproduce your issue in the We can make this more concrete by just comparing the
Under
To solve the problem under 0.10.x, I think, we want to find some way to place the classes so that This is just a hypothesis, a guess. It could be wrong. If it's not hard for you to check, I'd be curious what the value of I don't think I can "fix" this, except maybe with a Java 7/8 specific build (which is an option!). But it would be good to know if, for common applications like hibernate, there's a configuration or setup under which things don't break. Thanks again for the help! |
I just built c3p0-0.10.1 from source. All I did was change the imports in class src\main\java\com\mchange\v2\c3p0\impl\NewProxyConnection.java from:
To:
That is, I used a wildcard for all java.sql. imports. Please confirm that this works for you. And, of course, if this is even a viable workaroud/fix. UPDATE: I ran my test with Java 17. When I run with Java 8, it fails as expected: java.lang.NoClassDefFoundError: java/sql/ShardingKey |
Here's what get for the classloader information you requested: jdbcConnectionClass=class com.mchange.v2.c3p0.impl.NewProxyConnection connection.getClass().getClassLoader()=ParallelWebappClassLoader |
Crap -- I'm a dummy. I was running with Java 17 after modifying the imports for c3p0. The import change did not work with Java 8. Sorry about that. |
Well, even if under Java 17, that's consistent with the hypothesis that the fragility results from a more eager ClassLoader than the one that typically loads classes from What import change did you make that broke? You are far from a dummy. I appreciate the help. |
The classloader test information above was gathered running Java 8. I always run Tomcat from eclipse as a server. Not familiar with any explicit classpaths. I just drop jars into the WEB-INF/lib folder and they get used. The import change was a red-herring; I'd hoped that not explicitly referencing java.sql.ShardingKey in the imports would resolve the issue, but it didn't. I'd hoped that since we weren't calling the methods with java.sql.ShardingKey, the classloader wouldn't care. Please let me know if you want me to test more stuff. I do have a custom-built version of Hibernate that I've been using to tweak class determineAppropriateResolverDelegate.java. |
HikariCP had the same problem per this issue. Maybe you can look at that source for answers? His CHANGES log states: "fixed 1578 build change to ensure that proxies are generated using Java 8, otherwise we end up with class references to Java 11 classes, which fail to load on Java 8." |
Ah! Thank you again for all the help, and for the explanation. And for tracking down the HikariCP analogue! Just like c3p0, they generate proxies for JDBC classes from It might be too much trouble, but I guess what we might try to address your problem is taking those jars out of In my little world, I launch tomcat from If this is right, it's a pretty annoying way to have to address the issue. I completely understand if you'd prefer to just downgrade to 0.9.5.5 for now and move on. Even if it works, I'll have to look into generating separate builds for Java 8 going forward. It's too annoying to insist users place jars in the native If you do give this a try, let me know how it works, but please don't feel obliged to take the time or do anything disruptive. Thank you again for the careful work. |
I spent some time and came up with a working solution that uses generics. |
I'm glad to take a look at the files, but probably most helpful would just be to explain what you've done. Have you removed from the class the two Thanks again for all your help. |
I left the four methods in the main class as generic <T> stubs. The concrete methods are now in a new class. The new class has a public generic method to match the main class; it also has the concrete classes declared as private. When the main class calls one of the four public methods in the new class, the new class calls its private methods, casting the <T> generic to ShardingKey. It's really just a proof of concept. As I said earlier, I don't even know if the generics version of the main class' methods will be recognized at runtime.
…-------- Original message --------
From: Steve Waldman ***@***.***>
Date: 1/27/25 4:09 PM (GMT-06:00)
To: swaldman/c3p0 ***@***.***>
Cc: ForceRs ***@***.***>, Mention ***@***.***>
Subject: Re: [swaldman/c3p0] java.lang.NoClassDefFoundError: java/sql/ShardingKey running Hibernate 5.3.37, c3p0 0.10.1, and Java 8 (Issue #184)
I'm glad to take a look at the files, but probably most helpful would just be to explain what you've done. Have you removed from the class the two setShardingKey and two setShardingKeyIfValid methods?
Thanks again for all your help.
—
Reply to this email directly, view it on GitHub<#184 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AAYRPHZUXLV5E4AIMHY2JX32M2VAHAVCNFSM6AAAAABV2OMRSWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDMMJXGAYDANZYGI>.
You are receiving this because you were mentioned.Message ID: ***@***.***>
|
Please do attach your work, if you don't mind sharing. I'm interested! |
Will do tomorrow.
…-------- Original message --------
From: Steve Waldman ***@***.***>
Date: 1/27/25 4:37 PM (GMT-06:00)
To: swaldman/c3p0 ***@***.***>
Cc: ForceRs ***@***.***>, Mention ***@***.***>
Subject: Re: [swaldman/c3p0] java.lang.NoClassDefFoundError: java/sql/ShardingKey running Hibernate 5.3.37, c3p0 0.10.1, and Java 8 (Issue #184)
Please do attach your work, if you don't mind sharing. I'm interested!
—
Reply to this email directly, view it on GitHub<#184 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AAYRPH7XWIFGICWQTZVI3T32M2YJ3AVCNFSM6AAAAABV2OMRSWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDMMJXGA2DGNBWHA>.
You are receiving this because you were mentioned.Message ID: ***@***.***>
|
I've attached c3p0-Issue_184.zip, which contains three files:
The purpose of this proof-of-concept is to see if moving the four methods that reference java.sql.ShardingKey to a separate class is viable. For example, NewProxyConnection's method that was: The new class, NewProxyConnectionJava9, has corresponding methods as follows: The code compiles and runs in Java 8 and Java 17. @swaldman, I placed the modified NewProxyConnection code between these two eye-catchers: You might want to run quick tests using the attached c3p0-0.10.1.jar. It's got all the changes in it. I see a comment in NewProxyConnection stating: "This class generated by com.mchange.v2.c3p0.codegen.JdbcProxyGenerator$NewProxyConnectionGenerator. DO NOT HAND EDIT!!!!", so I assume your normal build would completely clobber the attached, modified, NewProxyConnection.java. |
I'm pretty sure this won't work. The only reason it compiled is that the four methods being implemented for the Connection interface utilize the default keyword, meaning they're optional to implement. Adding the @OverRide method annotation results in a compilation error. I should mention that was my first attempt at generics. |
I opened an issue with Hibernate for this, but now believe the issue is with c3p0.
When running Hibernate 5.3.37, c3p0 0.10.1, Tomcat/9.0.63, and Java 1.8.0_391-b13, Hibernate fails to initiate as follows:
Using Java 11, everything is fine.
Hibernate support suspected: "a JDBC driver that was compiled for a newer Java version (i.e. it uses java.sql.ShardingKey that was added in Java 9), but still using the Java 8 classfile version."
To that end, I determined that the com.mchange.v2.c3p0.impl.NewProxyConnection is being used by Hibernate to make this reflective call:
final Method getSchemaMethod = jdbcConnectionClass.getMethod( "getSchema" ); // [where jdbcConnectionClass is com.mchange.v2.c3p0.impl.NewProxyConnection]
Thinking that class com.mchange.v2.c3p0.impl.NewProxyConnection must have been compiled using Java 9 or greater, I ran "javap.exe -verbose NewProxyConnection.class" to determine the major version, assuming it would be that of Java 9 or greater, but it is major version 51 (which is Java 7).
However, I did see many references to java/sql/ShardingKey. Maybe they're in there because c3p0 was compiled using Java 11, but targeted Java 7?
At any rate, is this a bug with c3p0? I mean, can c3p0 run in Java 8 when it has references to Java 9 classes?
Perhaps I'm missing some required configuration to c3p0 and/or Hibernate?
The text was updated successfully, but these errors were encountered: