ClassNotFoundException using QuarkusClassLoader with local class and Debezium Engine - java

ClassLoader classLoader = QuarkusClassLoader.getSystemClassLoader();
String str = "com.mycompany.service.SomeClass";
try {
Class<? extends SomeClass> someClass =
(Class<? extends SomeClass>) classLoader.loadClass(str);
} catch (Throwable e) {
e.printStackTrace();
}
I'm trying to figure out why I get java.lang.ClassNotFoundException when I try to load com.mycompany.service.SomeClass. This class is defined locally in the project, I'm getting this error when I start my Quarkus app (mvn compile quarkus:dev). If I use another class loader (i.e. this.getClass().getClassLoader()), this error does not happen. It seems like it only happens with QuarkusClassLoader
EDIT:
I think in the end the problem was related to Debezium Engine initialisation. That exception was thrown when calling the following line:
// Create the engine with this configuration ...
engine =
DebeziumEngine.create(Json.class)
.using(props)
.notifying(this::handleDbChangeEvent)
.build();
See my answer for how I fixed it

Using Classloader.getSystemClassLoader is certainly not the correct thing to do because in dev-mode (and more generally, you would rarely want to do that in Java code), Quarkus dot not a flat classloader structure, but a layered one.
See https://quarkus.io/guides/class-loading-reference for more details on how Classloading in dev-mode works.
You can force all classes of a jar to be loaded by the system ClassLoader instead of the Quarkus ClassLoader by using something like:
quarkus.class-loading.parent-first-artifacts=stax:stax-api
Where you essentially configure the groupId and the artifactId of the jar that should be loaded by the system ClassLoader

I fixed this by passing Thread.currentThread().getContextClassLoader() in the engine initialisation.
engine =
DebeziumEngine.create(Json.class)
// Have to pass the current class loader to avoid ClassNotFoundException
.using(Thread.currentThread().getContextClassLoader())
.using(props)
.notifying(this::handleDbChangeEvent)
.build();

Related

Is there a way in my custom classloader to attach my classloader to the class while I delegate to super createing it?

I have this stack trace which is the FIRST time this class is loaded(I have a condition on the breakpoint for this class)....
The CompilingClassLoader is mine(ie. this one https://github.com/deanhiller/webpieces/blob/master/core/runtimecompile/src/main/java/org/webpieces/compiler/impl/CompilingClassloader.java )
I want this class that is being loaded in a way that it is plugin.getClass.getClassLoader is returning the CompilingClassLoader. Instead, it's classloader is AppClassLoader
This is causing many issues for plugin developers in that currently the workaround is a Thread context classloader. Is there a way to claim the compiling classloader loaded it but delegate to the super classloader?
The CRITICAL line is line 111 in the link above by the way. I want that line to use the super class to create the Class for me BUT I want it to look like I created the Class. ie. the Class returned should return 'my' classloader when clazz.getClassLoader() is called. this then fixes a ton of issues.
EDIT---------------------------------------
After further investigation. The CompilingClassLoader is very very tricky. You have these types of classes
Standard java.lang classes
webpieces startup classes
the applications compile on demand classes
webpieces plugin classes that sit below application classes and above them
libraries that sit below application classes AND above them
For #1, once I hit main, many of these like java.lang.Object are loaded by app classloader so this must remain true always so that you don't get the dreaded casting X to X yields ClassCastException
For #2, most of these are loaded by app classloader on startup as well
For #3, this is code that we compile OR recompile. To recompile, we throw away the CompilingClassLoader and recreate another one BUT cache bytes of all previously compiled classes that have not changed.
For #4 & #5, this is where it gets really really tricky!!! We have a ProdServerMeta (an application compiled class) that refers to plugins in webpieces and those plugins then sometimes load other application classes that need to compile on changes. Currently, we inject hibernate with the compiling classloader to avoid an issue.
The more I think about this, hibernate classes are loaded by app classloader right now because we return null when we can't compile a class delegating to the parent.
Now, I ran into these situations with CompilingClassLoader which is very hard to simplify into a classloader like Jan-Willem Gmelig Meyling's classloader below! Mainly because defineClass seems to be critical and missing in that example (and like what Olivier says below is very important).
Anyways, I ran into these things with trying the classloader below
loading of java.lang.* caused a SecurityException so I had to delegate that
After solving that, I ran into ClassCastException from SAXParserFactoryImpl to SAXParserFactoryImpl when slf4j tries to load configuration
This 2nd one makes total senses since Server.main creates a Logger and then every other class that creates a Logger creates it in a CompilingClassLoader. This leads me to the situation that
I cannot load ANYTHING that is loaded before the loading of the application
Next, being a webserver for customers, I also can't tell what is referenced in pre-app load vs. after app load.
I am coming to the conclusion that there is not a solution since as a developer writes code, I throw away the previous classloader and many classes have to continue to exist like Logger which is referenced by AppClassLoader on startup.
Of course, if I boot up with a CompilingClassLoader, I might stand a better chance in that I could 'try' to load everything perhaps but java.lang, BUT then I still run into the issue of some code after server bootup willl always be referenced by the FIRST CompilingClassLoader while later code on recompile will point to a new CompilingClassLoader.
I think the only solution then is for plugins to have to use the ContextClassLoader the more I think through this situation and that Olivier below is correct in it's not possible.
thanks,
Dean
Classloaders load classes from their parent class loaders first. You should be able to work around this by simply not setting a parent classloader for the classloader, and delegate the call for loadClass yourself.
A working example:
public class SomeClass implements Runnable {
#Override
public void run() {
System.out.println("Class loader for class: " + getClass().getClassLoader());
System.out.println("Class loader for thread: " + Thread.currentThread().getContextClassLoader());
System.out.println("Class loader for transitive dependency: " + SomeDependency.class.getClassLoader());
}
}
A transitive dependency:
public class SomeDependency {}
Test and classloader:
import java.net.URL;
import java.net.URLClassLoader;
public class Test {
public static class CustomClassLoader extends URLClassLoader {
ClassLoader delegate;
public CustomClassLoader(URL[] urls, ClassLoader parent) {
super(urls, null);
delegate = parent;
}
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
return super.loadClass(name);
} catch (ClassNotFoundException e) {
return delegate.loadClass(name);
}
}
}
public static void main(String... args) throws Exception {
URL location = SomeClass.class.getProtectionDomain().getCodeSource().getLocation();
CustomClassLoader customClassLoader = new CustomClassLoader(new URL[] { location }, ClassLoader.getSystemClassLoader());
Thread.currentThread().setContextClassLoader(customClassLoader);
Class<?> aClass = customClassLoader.loadClass("com.pkg.SomeClass");
Runnable runnable = (Runnable) aClass.newInstance();
runnable.run();
}
}
Output:
Class loader for class: com.pkg.Test$CustomClassLoader#4e50df2e
Class loader for thread: com.pkg.Test$CustomClassLoader#4e50df2e
Class loader for transitive dependency: sun.misc.Launcher$AppClassLoader#18b4aac2
Seems to do precisely what you want :)

Java Class cast exception, Classloader error on using javax xml providers?

I get the following error when I switched from OSGI Equinox to OSGI Karaf runtime.
Earlier the com.sun implementation was working fine, but now I need an apache implementation to run in one environment and com.sun in the older.
I know that OSGI has different class loading mechanism when compared to Java Class loading.
We are providing the javax packages as a part of the rt.jar which contains the Provider interface.
The implementation is coming from com.sun.xml
com.sun.xml.internal.ws.spi.ProviderImpl cannot be cast to javax.xml.ws.spi.Provider (loaded by org.apache.felix.framework.BundleWiringImpl$BundleClassLoader#0x0000000100162fd8) (found matching super class javax.xml.ws.spi.Provider loaded by , but needed loader org.apache.felix.framework.BundleWiringImpl$BundleClassLoader#0x0000000100162fd8)
at javax.xml.ws.spi.Provider.provider(Provider.java:43)
at javax.xml.ws.Service.<init>(Service.java:35)
at javax.xml.ws.Service.create(Service.java:126)
When I look at the logs, both class loaders seem to have the same ID, then why get a class cast exception?
javax.xml.ws.spi.Provider (loaded by org.apache.felix.framework.BundleWiringImpl$BundleClassLoader#0x0000000100162fd8) (found matching super class javax.xml.ws.spi.Provider loaded by , but needed loader org.apache.felix.framework.BundleWiringImpl$BundleClassLoader#0x0000000100162fd8)
As of now, I am using a hack to get around this in two different environments
private static final String PROVIDER_CXF = "org.apache.cxf.jaxws.spi.ProviderImpl";
private static final String PROVIDER_DEFAULT = "com.sun.xml.internal.ws.spi.ProviderImpl";
private String setProvider() {
log.debug("set default provider");
System.setProperty(PROVIDER, PROVIDER_DEFAULT);
try {
Service.create(new QName(null, ""));
return PROVIDER_DEFAULT;
} catch (Exception e) {
log.debug("setting cxf provider");
System.setProperty(PROVIDER, PROVIDER_CXF);
return PROVIDER_CXF;
}
}
You may need to set -Dorg.osgi.framework.bootdelegation=com.sun.*. See https://osgi.org/specification/osgi.core/7.0.0/framework.module.html#framework.module.parentdelegation. It is possible Equinox provides a common, useful default for boot delegation while Karaf does not.

URLClassLoader finding X but not Y in same folder

General idea: I'm writing on a loader for java that allows dynamically reloading classes to allow for changing the implementation, without restarting the entire program to keep the main application running and minimize downtimes. Every external piece of code is grouped by "modules", each module has a main class with a "onEnable, postEnable, onDisable" entry/exit point and can consist of any amount of classes. To load a module, the class containing the entry point is specified, then loaded. I'll reference them as "modules" and "additional classes" in the following, "module" being the class containing the above mentioned functions by implementing the "public interface Module", "additional classes" refer to everything the module would use on runtime but isn't a Module by itself (e.g. we have a Module called "Car implements Module", and that module requires a class "Engine" to function -> "Car" is the module, "Engine" is an additional class")
Code of what I'm doing to load a module initially (name is a String containing the full classname including path, example given later):
Class<?> clazz = mainLoader.loadClass(name);
Module module = (Module) clazz.newInstance();
addLoadedModule(module);
enableLoadedModule(module);
And here's how I reload the module when it's already existing, so that I can override the implementation. "m" is an instance of the current implementation of the Module that is supposed to be reloaded.
boolean differs = false;
Class<?> newClass = null;
try (URLClassLoader cl = new URLClassLoader(urls, mainLoader.getParent()))
{
// Try to load the class and check if it differs from the already known one
newClass = cl.loadClass(m.getClass().getName());
differs = m.getClass() != newClass;
}
catch (IOException | ClassNotFoundException e)
{
// Class couldn't be found, abort.
e.printStackTrace();
return;
}
if (!differs)
{
// New class == old class -> no need to reload it
return;
}
Module module = null;
try
{
// Try to instantiate the class
module = (Module) newClass.newInstance();
}
catch (InstantiationException | IllegalAccessException e)
{
// Can't instantiate, abort
e.printStackTrace();
return;
}
// Check versions, only reload if the new implementation's version differs from the current one. Version is a custom annotation, don't worry about that; the version check works fine
Version oldVersion = m.getClass().getAnnotation(Version.class);
Version newVersion = module.getClass().getAnnotation(Version.class);
if (oldVersion.equals(newVersion))
{
return;
}
// And if everything went well, disable and remove the old module from the list, then add and enable the new module.
disableModule(m);
modules.remove(m);
modules.put(module, false);
enableLoadedModule(module);
This is the mainLoader, urls is an URL[] pointing to the location containing the external classes to load:
mainLoader = new URLClassLoader(urls, this.getClass().getClassLoader());
The problem arises when I try to RE-load an implementation, that requires multiple classes:
Module of class A requires class B to function. This is what happens when I try to dynamically load, then reload class A:
load A -> "Sure, but I'll need B with it." -> automatically loads B -> "Here ya go, A works fine now."
reload A -> "Sure, but I'll need B with it." -> crashes because B couldn't be found
Both classes are located in the exact same folder, structure like this:
Class A implements Module: com/foo/bar/A.class
Class B: com/foo/bar/B.class
urls: ["com/foo/bar/"]
I call the function with load("com.foo.bar.A"), which works when attempting to load it the first time, but fails when trying to reload it as described above.
It works fine when trying to load a "single class module", the problem arises when the module relies on an additional external class. I tried using different classloaders to use as the parent for the URLClassLoader in the reloading process, those being the sysloader, Module.class.getClassLoader(), mainLoader (using that one, it won't ever find the new class definition because it already knows about it and therefor won't even attempt to load it from the drive again) and the mainLoader.getParent(), the classloader of the old module, and the parent of the modules classloader.
I'm probably just overseeing something obvious, but I can't figure out why it would manage to load the "extra" classes the first time, but fail when I reload the base class...
If you need any debug outputs or exact errors let me know, I replaced the debug outputs with comments explaining what does what so I got a fairly detailed log of what's happening when, but I didn't seem it to be necessary as it goes through the entire "check and then load" process just fine, it crashes when trying to enable the module. The "onEnable" method of the module requires the additional class B, that's where it fails. As I said, if you need the implementation of the classes A and B, Module, any other code or the debug outputs let me know and I'll add them in as requested.
There's a few things you can try:
Create an extension of UrlClassLoader so that you can track when it loads a class and what class loader is used to load the class.
Your other issue is make sure none of these classes are available on the "default" class path as that will cause that version to use. You are not overriding the default class loading behaviour which is to check the parent for the class first.
The other issue you're probably facing relates to the way the VM caches classes - I'm not entirely sure how this works - but from what I've experienced it seems that once a class is loaded it puts it in a shared storage space so that it does not load the class again. This shared space class will not be unloaded until the class loader that loaded it goes unreachable.
The solution lies in the classloader being closed and deleted as soon as the loading of the initial class is done, due to the class loader being only existant in the try/catch clause. I solved the issue by storing the classloader in a map until a new implementation of the module is loaded, then I can discard the old loader and store the new one instead.

JUnitCore giving "No runnable methods" error in Eclipse plugin

I am trying to write an Eclipse plugin that can run JUnit tests and do something with the results. My plugin loads a given class correctly, but fails to run the JUnit tests and gives an error: initializationError(className): No runnable methods. When I run the test class using Result result = JUnitCore.runClasses(className.class); Failure failure : result.getFailures(); from within the same Eclipse instance, however, I don't get any errors.
I think my problem is the one that #gubby describes in the question java.lang.Exception: No runnable methods exception in running JUnits, but I don't know how to implement his suggestion to a solution which reads: "Solution is to load JUnitCore in the same ClassLoader as the tests themselves."
Here is a reduced version of my implementation (please assume that everything except the loading of the runnable methods work):
ClassLoader classLoader = ClassLoaderHelper.getClassLoader(FileFinder.getCurrentProject());
Class clazz = classLoader.loadClass(fileName.substring(0, fileName.indexOf(".class")));
Result result = JUnitCore.runClasses(clazz);
Failure failure : result.getFailures()
The code to get the ClassLoader is the following:
public static ClassLoader getClassLoader(IProject project) {
String[] classPathEntries = null;
try {
project.open(null);
IJavaProject javaProject = JavaCore.create(project);
classPathEntries = JavaRuntime.computeDefaultRuntimeClassPath(javaProject);
} catch (CoreException e1) {
e1.printStackTrace();
}
List<URL> urlList = new ArrayList<URL>();
for (String entry : classPathEntries) {
IPath path = new Path(entry);
URL url;
try {
url = path.toFile().toURI().toURL();
urlList.add(url);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
ClassLoader parentClassLoader = project.getClass().getClassLoader();
URL[] urls = (URL[]) urlList.toArray(new URL[urlList.size()]);
URLClassLoader classLoader = new URLClassLoader(urls, parentClassLoader);
return classLoader;
}
For Eclipse plugins, you have basically two options to have one plugin share a class loader with another.
Buddy Class Loading. Note that this breaks loose coupling, but it's easy to "implement" as you simply add two statements in the two respective plugins' MANIFEST.MF and an export statement as well. The following rules apply (from the link given above).
The bundle Y must specify the registered buddy policy (i.e. Eclipse-BuddyPolicy: registered)
The bundle X must specify the symbolic name of Y in the Eclipse-RegisterBuddy header (i.e Eclipse-RegisterBuddy: Y)
The bundle X must be dependent on a package exported by bundle Y. This can happen through either a Require-Bundle or Import-Package constraint.
Fragments: You can "attach" a fragment to a plugin. Both share the same class loader. Usually, this technique is used for things like plugin i18n, but it's also the recommended practice for adding unit tests to plugins. This way, the tests don't have to go into the same plugin, and possibly unneeded test classes or dependencies won't go in the production code.
There's a wizard for fragments in Eclipse, but they're basically plugins themselves, which declare a "host plugin".
So, you could consider putting your code into a fragment and attach that to the respective plugin containing the code-under-test. Or, if you need to reuse your code for different plugins and do not care about loose coupling, use Buddy Class Loading.
Also, check whether you have all the right dependencies in the plugin (e.g., org.junit). You need the JUnit contained in the Java Development Tools.

Load Java classes based on a classpath in a properties file

My application uses JDBC database drivers. I load these from a jar file, db2jcc.jar in the case of DB2 which I'm currently working with. With this jar in the classpath, everything is fine, but I have a requirement to find the jar from a property in the application's config file instead - for example,
database.driver=/opt/IBM/db2/V9.5/java/db2jcc.jar
I can load the class via a URLClassLoader ok, but the problem is that I need to treat the object thus created as an explicit DB2XADataSource. For example:
URLClassLoader dbClassLoader = new URLClassLoader(new URL[]{driverJar});
xaClass = dbClassLoader.loadClass("com.ibm.db2.jcc.DB2XADataSource");
DB2XADataSource dataSource = (DB2XADataSource) xaClass.newInstance();
dataSource.setCurrentSchema(DATABASE_SCHEMA); // <- dataSource has to be a
dataSource.setDatabaseName(DATABASE_NAME); // DB2XADataSource to do this
(rearranged and renamed somewhat; I actually do the loadClass in the constructor of the class that contains this code, while the newInstance is in one of its methods.)
I guess I'm getting into a classloader tangle because the classloader that loaded my class is trying to find DB2XADataSource in order to do the cast, but the URL classloader is not above it in the tree. The trouble is, it being long after I should have stopped working for the day (here in the UK) I can't think how best to solve it in a vaguely neat and sane manner.
Ideas?
Thanks.
The simplest approach is to just use the java.beans API (or direct reflection if you must) to invoke the setter methods.
Alternatively: Your database code requires to link to the dynamically loaded code. Therefore, dynamically load your database code. How much is up to you. You might load almost everything except the "bootstrap".
Yep - the class can't load its own dependencies. You could do some ClassLoader magic, but I imagine it would get messy very quickly.
One way to reduce the amount of reflection would be to put any code that depends on DB2XADataSource into an implementation that is invoked via an interface available to the calling ClassLoader.
//in mydb2driver.jar
public class MyDb2Driver implements IDriver {
private DB2XADataSource dataSource = new DB2XADataSource();
public void init() {
dataSource.setCurrentSchema(DATABASE_SCHEMA);
}
//etc.
}
This is loaded with your driver:
database.driver=/opt/IBM/db2/V9.5/java/db2jcc.jar:/foo/mydb2driver.jar
Invoking code is in the regular classpath:
public interface IDriver {
public void init();
//etc.
}
...
URLClassLoader dbClassLoader = new URLClassLoader(new URL[]{driverJar});
xaClass = dbClassLoader.loadClass("foo.MyDb2Driver");
IDriver dataSource = (IDriver) xaClass.newInstance();
dataSource.init();

Categories