Do not use context class loader when attempting to load classes
Description
Final resolution
Remove in Reflection the incriminated line below:
Using the supertype’s class loader explicitly is not necessary since Class.forName would implicitly use the current class' class loader, which is equivalent given that all supertypes are driver types.
Initial description
Reflection.buildFromConfig calls Reflection.loadClass with the class loader provided by the user:
If that class loader is not null, it is used, otherwise Thread.currentThread().getContextClassLoader() will be used.
However this might lead to a situation where the expected supertype and the concrete implementation class are loaded by two distinct class loaders. For example, in an OSGi environment, the expected supertype will be loaded by the driver bundle's class loader (since it's an interface declared there), whereas the implementation class will be loaded by the thread's context class loader which is usually a totally different one (the system class loader).
This causes a cryptic error to be raised: "Expected class X (specified by Y) to be a subtype of Z".
This is being currently circumvented in our tests with the following trick:
But this is really a workaround, and withClassLoader(CqlSession.class.getClassLoader()) is particularly ugly.
Since the implementing class and its interface must be loaded by compatible class loaders, the correct call in buildFromConfig would be:
We might also want to get rid of the following statement in Reflection.loadClass since classLoader would never be null anymore, and the thread's context class loader is probably never the right one to use:
A quick test demonstrated that the changes outlined above worked for OSGi deployments; no need to call withClassLoader. They worked as well for web deployments where expectedSuperType.getClassLoader() returns the right class loader to use (system or webapp), depending on where the driver jar is placed.
The only situation that requires a specific class loader to be specified is when the driver jar is in the web server’s system classpath, but the implementing class is the web app’s classpath. For this specific case and only for it, it is legit to call withClassLoader() and pass Thread.currentThread().getContextClassLoader(), which, in web environments, is the web app’s class loader.
Final resolution
Remove in
Reflection
the incriminated line below:Using the supertype’s class loader explicitly is not necessary since
Class.forName
would implicitly use the current class' class loader, which is equivalent given that all supertypes are driver types.Initial description
Reflection.buildFromConfig
callsReflection.loadClass
with the class loader provided by the user:If that class loader is not null, it is used, otherwise
Thread.currentThread().getContextClassLoader()
will be used.However this might lead to a situation where the expected supertype and the concrete implementation class are loaded by two distinct class loaders. For example, in an OSGi environment, the expected supertype will be loaded by the driver bundle's class loader (since it's an interface declared there), whereas the implementation class will be loaded by the thread's context class loader which is usually a totally different one (the system class loader).
This causes a cryptic error to be raised: "Expected class X (specified by Y) to be a subtype of Z".
This is being currently circumvented in our tests with the following trick:
But this is really a workaround, and
withClassLoader(CqlSession.class.getClassLoader())
is particularly ugly.Since the implementing class and its interface must be loaded by compatible class loaders, the correct call in
buildFromConfig
would be:We might also want to get rid of the following statement in
Reflection.loadClass
sinceclassLoader
would never be null anymore, and the thread's context class loader is probably never the right one to use:A quick test demonstrated that the changes outlined above worked for OSGi deployments; no need to call
withClassLoader
. They worked as well for web deployments whereexpectedSuperType.getClassLoader()
returns the right class loader to use (system or webapp), depending on where the driver jar is placed.The only situation that requires a specific class loader to be specified is when the driver jar is in the web server’s system classpath, but the implementing class is the web app’s classpath. For this specific case and only for it, it is legit to call
withClassLoader()
and passThread.currentThread().getContextClassLoader()
, which, in web environments, is the web app’s class loader.