Here's my problem in generalized terms as I can't post my actual code.
I'm dynamically loading a jar containing package com.jar. I'm then trying to load class com.jar.MyClass from that jar and call doThing() on the newly loaded class. I'm getting a java.lang.NoClassDefFoundError when doThing() makes a reference to another class that is in com.jar and the .class file is in the jar.
Here what I'm trying to do in my code:
File jarFile = new File(pathToJar)
URLClassLoader classLoader = new URLClassLoader(new URL[] { jarFile.toURI().toURL() }, this.getClass().getClassLoader());
Thread.currentThread().setContextClassLoader(classLoader);
Class jarClass = Class.forName("com.jar.MyClass", true, classLoader);
Constructor<?> ctor = adapterClass.getDeclaredConstructor(Integer.class);
MyClass newMyClass = (MyClass) ctor.newInstance(1);
newMyClass.doThing(); //At some point in method makes reference to com.jar.AnotherClass
I'm at a loss as to why I can load com.jar.MyClass but it can't make a reference to other classes in the same jar.
Related
I'm having a hard time setting the classpath for a directory to a package of classes. I'm trying to run a jar file that takes a directory as a command line argument. The program uses the directory to access class files in a folder and uses reflection to explore the class fields and methods.
final File folder = new File(args[0]);
classList = dirParse.listFilesForFolder(folder);
I then go through the classList, get the name of each class, and use the Class.forName() method to access the classes.
Class c = Class.forName(className);
For the line above to work, I have to set the classpath to the address of the directory containing the classes.
I can get the program to run just fine when I'm using a directory of classes that do not belong to a package like below:
java -cp "Explorer.jar:/Users/john/Desktop/TestClass/" explorer.ExplorerDemo /Users/john/Desktop/TestClass/
However, for the following line, monopoly is a package and the program throws a ClassNotFoundException after calling Class.forName(className)
java -cp "Explorer.jar:/Users/john/Desktop/Programming\ Project/Monopoly/build/classes/monopoly/" explorer.ExplorerDemo /Users/john/Desktop/Programming\ Project/Monopoly/build/classes/monopoly/
For testing purposes, I tried adjusting `Class.forName() call to include the package name like below:
Class c = Class.forName("monopoly."+className);
However, this also throws ClassNotFoundException.
Class.forName is a shortcut to obtaining class information within the context of ClassLoader of the current class. Javadoc states that this is equivalent to
Class.forName("Foo", true, this.getClass().getClassLoader())
Provided that you class directory is supplied as runtime parameter and is not part of the original classpath, I would suggest you instantiating custom URLClassLoader instance that will be pointing to your directory.
Sample code:
public class ReflectionClassAnalysis {
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException {
// URLClassLoader supports both directories and jar files
Path directory = Paths.get("/some/directory/");
Path jar = Paths.get("/some/binary.jar");
// You may be interested in providing parent ClassLoader for your new instance
// You can either use current class ClassLoader like
ClassLoader contextClassLoader = ReflectionClassAnalysis.class.getClassLoader();
// or current thread ClassLoader
// ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
URLClassLoader myClassLoader = new URLClassLoader(
new URL[]{
directory.toUri().toURL(),
jar.toUri().toURL()
},
contextClassLoader
);
// You may use ClassLoader directly to load class meta
Class<?> externalClass = myClassLoader.loadClass("your.class.name");
// or supply ClassLoader to forName method
// Class.forName("your.class.name", true, myClassLoader);
// Do your class analysis here
}
}
For JAR with classpath instructions please refer to: Run a JAR file from the command line and specify classpath
I would like to load a class from another JAR (imported as file during runtime) and handover an Object (this) to the Constructor of the class which should be loaded.
I tried doing that with the following code:
URLClassLoader cl2 = URLClassLoader.newInstance(new URL[] { new URL("jar:file:" + jarPath +"!/") });
Class c2 = cl2.loadClass("main.Class2BeLoaded");
Object loadedClass = c2.getConstructor(Object4Constructor.class).newInstance(this));
It seems so, that the Object which I handover to the constructor (this) can't be used from the code which runs in the JAR.
What is wrong? If I run this code in the same JAR everything works.
I have a path to a .class file and i want to instantiate it from a running program.
I have to "load" this class but its classpath isn't in my .jar or my project, it is in a folder besides it (can't use Class.forName()).
How can i instantiate this .class ?
You can proceed as next:
File myFolder = new File("myfolder");
URLClassLoader classLoader = new URLClassLoader(new URL[]{myFolder.toURI().toURL()}, Thread.currentThread().getContextClassLoader());
Class<?> myClass = Class.forName("my.package.Myclass", true, classLoader);
Myclass obj = (Myclass)myClass.newInstance();
First you create an instance of URLClassLoader using the context Classloader as parent, then you load the class using this new ClassLoader and finally you create an instance (here it calls a constructor with no arguments).
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.
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();