NoClassDefFoundError with java reflection - java

I am using the following code to dynamically load a class in java:
URL url = new File(ACTIONS_PATH).toURI().toURL();
URLClassLoader clazzLoader = new URLClassLoader(new URL[]{url});
Class<RatingAction> clazz = (Class<RatingAction>) clazzLoader.loadClass(name);
return clazz.newInstance();
This code works with simple classes (no inheritance or interfaces), but the class I want to load is implementing an interface (that the class loader can find using findClass)
and when i call class.newInstance I get the mentioned exception.
What am i doing wrong?
Thank you.

You have problems with your classpath. My guess it happens since you don't define the parent classloader - does "url" contains all the needed classes including the system classes?
You are getting the exception, when the class is actually resolved, so the classes that appear in the loaded class are also loaded. If you change clazzLoader.loadClass(name) to clazzLoader.loadClass(name, true), you will get the exception in loadClass line.
Try the following:
URL url = new File(ACTIONS_PATH).toURI().toURL();
URLClassLoader clazzLoader = new URLClassLoader(new URL[]{url}, getClass().getClassLoader());
Class<RatingAction> clazz = (Class<RatingAction>) clazzLoader.loadClass(name);
return clazz.newInstance();

Related

Unable to cast to interface class with URLClassLoader and reflection

I have a jar:
/home/cole/lib/a.jar
And in this jar I have the following interface/classes (horrible names for illustration purposes only!):
CreatorInterface.java
Base.java (implements CreatorInterface.java)
AbstractBase.java (extends Base.java)
Implementation.java (extends AbstractBase.java)
In a separate project I have the following code:
final URL[] jars = new URL[] {
new File("/home/cole/lib/a.jar").toURL();
}
final URLClassLoader classLoader = new URLClassLoader(jars, null);
final Class<?> implementation = classLoader.loadClass("Implementation");
final CreatorInterface object = (CreatorInterface)implementation.newInstance();
However when I run the above, I get the following:
java.lang.ClassCastException: Implementation cannot be cast to CreatorInterface
Given Implementation is ultimately an instance of a class that implements CreatorInterface, why do I get the ClassCastException?
Update 1
This isn't a question about using URLClassLoader, the class is found ok, the problem appears to be in the instantiation. For example, the following code works fine:
final Object object = implementation.newInstance();
Update 2
As #davidxxx answered, I have the interface class twice (once in the jar and once in the project using it). Although the interface was the same, this was the cause of the issue.
However to make it work, I needed to fix my URLClassLoader like this, to avoid a ClassNotFoundException:
final ClassLoader parent = this.getClass().getClassLoader();
final URLClassLoader classLoader = new URLClassLoader(jars, parent);
This exception :
java.lang.ClassCastException: Implementation cannot be cast to
CreatorInterface
makes me think that you have very probably two distinct CreatorInterface classes : one included in the jar and another other coming from the client program that tries to load it.
Even if the two classes have the same name (qualified names), these are different classes for each classloader as here you use two unassociated classloaders.
You have the current classloader of the program that you run and this other classloader as you specified null as parent classloader :
final URLClassLoader classLoader = new URLClassLoader(jars, null);
So as you try to assign the object created by reflection to the CreatorInterface variable, the cast fails because two distinct CreatorInterface were loaded by each classloader and are used : one coming from the classloader of your client code and another coming from the the instantiated classloader.
Using a single classloader would solve the issue but a best practice would be including the jar in the classpath of the project and to ensure to have a single version of the classes provided in the jar.
To decouple things you should probably split the jar into 2 jars : an API jar that contains only the interface and an implementation jar that depends on the API jar and that contains other classes.
In the client code, add in the classpath only the API jar to be able to assign to a variable with the interface declared type.
About your second point :
This isn't a question about using URLClassLoader, the class is found ok, the problem appears to be in the instantiation. For example, the
following code works fine:
final Object object = implementation.newInstance();
In this case you don't refer the interface type.
You indeed assign the Implementation object to an Object and not to a CreatorInterface variable.
The correct/consistent interfaces and subclasses are loaded by the classloader but here you never give a chance to provoke a ClassCastException as you never assign it to a type of a duplicate class but Object that is defined a single time.
So the problem previously encountered cannot occur.
About the third point :
However to make it work, I needed to fix my URLClassLoader like this,
to avoid a ClassNotFoundException:
final ClassLoader parent = this.getClass().getClassLoader();
final URLClassLoader classLoader = new URLClassLoader(jars, parent);
It works because here you create a classloader associated to the parent classloader.
In fact if you did :
final URLClassLoader classLoader = new URLClassLoader(jars);
It would produce the same result as the URLClassLoader object created would use by default the delegation to the parent classloader (here the classloader that started your application).

ClassLoader classes are not properly loaded in the thread

I am creating a new classLoader using URLClassLoader and trying to set it as the classLoader for the current thread.
But it is not working properly for me.
As per my understanding, if I set a classLoader to the current thread, the methods and interfaces referenced by the Current Thread should be from the present classLoader.
But it is not the case with me. The method is picked up from another jar and I am getting classCastExecption.
Following is the code for getting classLoader:
public ClassLoader getClassLoader(boolean b) {
ClassLoader loader = null;
File file = new File(SamVariables.JAR_FILE);
if (file.exists()){
try {
List<URL> urlsList = new ArrayList<URL>();
urlsList.add(file.toURI().toURL());
URL[] urls = new URL[urlsList.size()];
urlsList.toArray(urls);
URLClassLoader url = new URLClassLoader(urls);
try {
loader = Class.forName("org.jboss.naming.remote.client.InitialContextFactory", false, url).getClassLoader();
} catch (ClassNotFoundException e) {
loader = Class.forName("org.jboss.jms.client.JBossConnectionFactory", false, url).getClassLoader();
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
return loader; // I am successfully getting the classLoader for the class
}
I set it to the current thread
Thread.currentThread().setContextClassLoader(getClassLoader);
But later when I try to get the topicConnectionFactory object, it gives me typecast exception:
topicConnectionFactory = (TopicConnectionFactory) topicConnectionFactObj;
It gives me classCastException.
When I checked the TopicConnectionFactory object, it is coming from another jar file which is causing the issue.
As per my understanding, if I set a classLoader to the current thread,
the methods and interfaces referenced by the Current Thread should be
from the present classLoader.
No, this is a misconception. The context class loader is not used unless code specifically uses it. In particular, the context class loader is not used by the JVM (but it is used by specific APIs, such as for finding an XML parser implementation). Instead, the class loader of the originating class is used.
If you want your code to be able to load classes from a custom class loader, then you must load your classes in that class loader. For example, put those classes in a separate JAR, put that JAR on the URLClassLoader class path, and use reflection to load/call your class from that URLClassLoader.

Reflections could not get class type

I am using a third party library called Reflections (not to be mistaken with Java reflection) to search another jar for Classes that extend Foo using the following code:
Reflections reflections = new Reflections("com.example");
for(Class<? extends Foo> e : reflections.getSubTypesOf(Foo.class)) {
doSomething()
}
When I do this Reflections throws the following error:
org.reflections.ReflectionsException: could not get type for name com.example.ExtendsFoo
Does anyone know how to fix this cause I'm stumped?
Thanks in advance!
The problem may be due to not having a class loader that can resolve the name (even though it can resolve the subtype). This sounds contradictory, but I had the error message when I was building a Configuration and using ClasspathHelper.forClassLoader on an application- instantiated URLClassloader to figure out what to scan on the classpath, but not passing in said URLClassLoader into the Reflections configuration so that it could instantiate things correctly.
So you may want to try something along the lines of the following:
URLClassLoader urlcl = new URLClassLoader(urls);
Reflections reflections = new Reflections(
new ConfigurationBuilder().setUrls(
ClasspathHelper.forClassLoader(urlcl)
).addClassLoader(urlcl)
);
where urls is an array of URLS to the jars containing the classes you want to load. I was getting the same error as you if I did not have the final addClassLoader(...) call to the ConfigurationBuilder.
If this doesn't work, or is not applicable, it may be worth just setting a breakpoint in ReflectionsUtil.forName(String typeName, ClassLoader... classLoaders)) to see what is going on.
Take a look: https://code.google.com/p/reflections/issues/detail?id=163
Reflections (in its current version 0.9.9-RC1) doesn't re-throw exception correctly. That's why you may miss the true cause of the problem. In my case it was a broken .class file, which my default class loader failed to load and threw an exception. So, first of all, try to make sure that your class is truly loadable.
Scanning for classes is not easy with pure Java.
The spring framework offers a class called ClassPathScanningCandidateComponentProvider that can do what you need. The following example would find all subclasses of MyClass in the package org.example.package
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);
provider.addIncludeFilter(new AssignableTypeFilter(MyClass.class));
// scan in org.example.package
Set<BeanDefinition> components = provider.findCandidateComponents("org/example/package");
for (BeanDefinition component : components)
{
This method has the additional benefit of using a bytecode analyzer to find the candidates which means it will not load all classes it scans.
Class cls = Class.forName(component.getBeanClassName());
// use class cls found
}
Fore more info read the link

URLClassloader Dependencies

In my previous question, I asked how to load remote jar files. My current code is this:
//f is the path to the jar
URLClassLoader loader = new URLClassLoader(new URL[]{f.toURI().toURL()});
Class<?> jarClass = Class.forName(main, true, loader);
Class<? extends Module> module = jarClass.asSubclass(Module.class);
Constructor<? extends Module> constructor = module.getConstructor();
System.out.println(constructor);
Module module = constructor.newInstance();
This works well, but the remotely-loaded modules extend a class that is in the jar that is loading them, which gives this error:
Caused by: java.lang.ClassNotFoundException: package.whatever.Module, which I presume is because it is using URLClassLoader instead of getClass().getClassLoader().. how can I make it use URLClassLoader and then fall back to the default one?
Thanks,
Bart
You can set your application class loader to be the parent of the url class loader:
URLClassLoader loader = new URLClassLoader(
new URL[]{f.toURI().toURL()}, Module.class.getClassLoader());
From the Oracle Java tutorial (class loading mechanism):
The Java platform uses a delegation model for loading classes. The
basic idea is that every class loader has a "parent" class loader.
When loading a class, a class loader first "delegates" the search for
the class to its parent class loader before attempting to find the
class itself.
I faced the same issue and I was using Java 9, downgrading back it to Java 8 resolved my issue.

Java example with ClassLoader

I have small problem. I learn java SE and find class ClassLoader. I try to use it in below code:
I am trying to use URLClassLoader to dynamically load a class at runtime.
URLClassLoader urlcl = new URLClassLoader(new URL[] {new URL("file:///I:/Studia/PW/Sem6/_repozytorium/workspace/Test/testJavaLoader.jar")});
Class<?> classS = urlcl.loadClass("michal.collection.Stack");
for(Method field: classS.getMethods()) {
System.out.println(field.getName());
}
Object object = classS.newInstance();
michal.collection.Stack new_name = (michal.collection.Stack) object;
The java virtual machine does not see me class, and I get the following exception:
Exception in thread "main" java.lang.Error: Unresolved compilation problems: michal cannot be resolved to a type michal cannot be resolved to a type at Main.main(Main.java:62)
Do you know how I can solve this problem?
The above answers are both wrong, they don't understand the root problem. Your main refers to the Stack class which was loaded by one class loader. Your urlclassloader is attempting to load a class with the same name. You cannot cast the loaded to the referred because they are not the same, they belong to different classloaders. You can print the has code of each to see they are different. An equality test will also show the cclass references to be different. Your problem is probably because dependent classes referenced by sstack can be found, which will result in NoClassDefErrors etc. Your main will probably fail with a classcastexception.
Class<?> classS = urlcl.loadClass("michal.collection.Stack");
[...]
Object object = classS.newInstance();
michal.collection.Stack new_name = (michal.collection.Stack) object;
So you're attempting to dynamically load a class and then you statically refer to it. If you can already statically link to it, then its loaded and you can't load it again. You'll need to access the methods by reflection.
What you would usually do is have the loaded class implement an interface from the parent class loader. After an instance is created (usually just a single instance), then you can refer to it through a reference with a type of the interface.
public interface Stack {
[...]
}
[...]
URLClassLoader urlcl = URLClassLoader.newInstance(new URL[] {
new URL(
"file:///I:/Studia/PW/Sem6/_repozytorium/workspace/Test/testJavaLoader.jar"
)
});
Class<?> clazz = urlcl.loadClass("michal.collection.StackImpl");
Class<? extends Stack> stackClass = clazz.asSubclass(Stack.class);
Constructor<? extends Stack> ctor = stackClass.getConstructor();
Stack stack = ctor.newInstance();
(Usual Stack Overflow disclaimer about not so much as compiling.)
You'll need to add error handling to taste. URLClassLoader.newInstance adds a bit of refinement to URLClassLoader. Class.newInstance has completely broken exception handling and should be avoided.
You can't refer to the dynamically-loaded type by name in the code, since that has to be resolved at compile-time. You'll need to use the newInstance() function of the Class object you get back from loadClass().

Categories