There are several posts about how to add jar-file to classpath at runtime by following idea:
- get current system classpath;
- suppose it is URLClassLoader;
- use reflection to set access for protected addURL method;
- use mentioned method to add url to classpath.
Here is an example:
Adding files to java classpath at runtime
Because of 2 and 3 steps this looks like "nasty hack".
How could I extend URLClassLoader and set it as a current? I am bit confused with classloaders and tried the following:
public static void main(String... args) {
URLClassLoader loader = new URLClassLoader(new URL[]{new URL("file:jxl.jar")});
System.out.println(loader.loadClass("jxl.Workbook"));
Thread.currentThread().setContextClassLoader(loader);
System.out.println(Class.forName("jxl.Workbook"));
} // main
I get ClassNotFoundException on the fourth line, while second works ok. (why it is so, by the way?)
The Class.forName method uses the "defining class loader of the current class," not the thread context classloader. In your case the ClassLoader that Class.forName will use is the one that loaded your application, i.e. the system class loader. This is a class loader that looks for resources in the class path.
Related
I'm currently trying to load classes into my application so that I can then filter out those that don't contain any test / #Test-methods. I want to run these tests in my application afterwards.
So far, so good - except it seems like the URLClassLoader I'm using (or probably any ClassLoader) doesn't actually reload classes that are located on my applications classpath.
More precisely, a user of my application starts by selecting a number of .java source files. Those are then copied to a temporary location and a number of regex match/replace pairs are applied to the copy of the original source files. Next, the copies are compiled, and I then want to load those compiled .class-files into my application so I can run them as tests.
In most cases, this works as desired.
Unfortunately, if the fully qualified class name (FQCN) of any of the selected files is identical to the FQCN of a class that is part of this application (such as tests for this application), the ClassLoader ignores the specified path (to %temp%\myfolder\) and the (updated) version of the class file located there, but instead uses the already present/loaded version of the class.
After some research, this behaviour can be expected according to the docs (emphasis mine):
• The loadClass method in ClassLoader performs these tasks, in order, when called to load a class:
- If a class has already been loaded, it returns it.
- Otherwise, it delegates the search for the new class to the parent class loader.
- If the parent class loader does not find the class, loadClass calls the method findClass to find and load the class.
I tried using findClass, but it's unfortunately not visible.
Hence my question - does anyone know how to force java / the ClassLoader to actually load the class from the specified path, ignoring any - FQCN-wise - identical existing classes?
A classloader first delegates to its parent classloader, which is how it determines "if a class has already been loaded". If you want to force a classloader to load a new class, one way is to insert another classloader in the chain which fails to load/find the same class. Very quick, incomplete example:
class FirewallLoader extends ClassLoader {
public FirewallLoader(ClassLoader parent) {
super(parent);
}
public loadClass(String name, boolean resolve) {
if (name.equals("some.class.Xyz")) {
throw ClassNotFoundException();
}
super.loadClass(name, resolve);
}
}
You make the "FirewallLoader" the parent or your URLClassLoader, and then your URLClassLoader will load new versions of whatever classes the Firewall loader filters out.
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 am learning the process of loading java class and encounter some confusion.
I know when loading a java class, current classLoader wont load the java class directly and it will delegate to its parent classLoader(a recursive process) until it parent cant load this class.
The question is that :what is the current classLoader? Bootstrap? Extension? App?
how to get the current classLoader?.
and I know there is an API:
xxx.Class.getClassLoader();
but I am not sure whether the return value is currentClassLoader. I think it should be the classLoader which load this java class in reality.
To describe my question more detail I will give an example.
I get the follow content in a blog.
ThreadContextClassLoader is used to deal with java SPI, Interface is defined in java core lib and loaded by Bootstrap ClassLoader and third party implement these interface then the jar are loaded by AppClassLoader
Solution: traditional classLoader cant deal with this case,because it cant discovery the third party jar when we use the third party implement in core lib.
most of the above that I can understand but the solution make me confusion:
for example, the Interface CoreA and class CoreB are in java core lib and should be loaded by Bootstrap ClassLoader and the AImpl is a implement of A by third party and should be loaded by AppClass loader.
the code segment as below:
public Interface CoreA{
void add();
}
public Interface AImpl implements CoreA{
void add(){};
}
public class B{
public void demo(){
a = new AImpl();
}
}
then if we reference B in main method, then we will load B because the class loader of B is Bootstrap then about AImpl the current Loader is Bootstrap so it cant be found?
I dont know whether it is as what I guess?
Any advice will be appreciated.
Generally speaking you are right, it can't be found. Let me show you the following example. Let's say we have 3 classes: A, B and Main like these:
public class A {
public String a() {
return "a";
}
}
public class B {
public String b() {
return new A().a();
}
}
public class Main {
public static void main(String... args) {
System.out.println(new B().b());
}
}
And we package these classes into correspondent jars: a.jar, b.jar and place Main.class into the working directory. After that let's test the following scenarios:
1) Everything (A.class, B.class, Main.class) is loaded by system classloader and works fine:
$ java -cp .:a.jar:b.jar Main
a
2) B.class is loaded by system classloader and A.class is loaded by bootstrap classloader and everything still works fine because system classloader delegates loading to bootstrap classloader (just because bootstrap classloader can load it):
$ java -Xbootclasspath/a:a.jar -cp .:b.jar Main
a
3) A.class is loaded by system classloader and B.class is loaded by bootstrap classloader (your case). In this scenario during loading of B.class current classloader is bootstrap classloader, but it can't found B.class and fails:
$ java -Xbootclasspath/a:b.jar -cp .:a.jar Main
Exception in thread "main" java.lang.NoClassDefFoundError: A
at B.b(B.java:4)
at Main.main(Main.java:4)
Lets take a look at the last example more carefully. What's happening here:
Try to find class with public static main(String[] args) method
1.1. system classloader hasn't loaded it yet so delegates to extension classloader
1.2. extension classloader hasn't loaded it yet so delegates to bootstrap classloader
1.3. bootstrap classloader hasn't loaded it yet and tries to load, it can't load it and returns control to extension classloader
1.4. extension classloader tries to load, it can't load it and returns control to system classloader
1.5. system classloader loads Main.class
Main.class is processed and we try to load B.class with current classloader system classloader
2.1. system classloader hasn't loaded it yet so delegates to extension classloader
2.2. extension classloader hasn't loaded it yet so delegates to bootstrap classloader
2.3. bootstrap classloader hasn't loaded it yet and loads B.class
B.class is processed and and we try to load A.class with current classloader bootstrap classloader
3.1. bootstrap classloader hasn't loaded it yet and tries to load and
fails
I hope it will help you.
When a class A attempts to load another class B, the ClassLoader that loaded A is the current ClassLoader. The word current vaguely refers to the execution context - e.g. how do you end up in the method that triggers the current class loading call.
There is no method - say getCurrentClassLoader - that simply gives the current ClassLoader, but there are api methods that internally use the concept of current ClassLoader. For example, Class.forName(String className)
If you check how that method is implemented, it tells you the meaning of "current class loader":
public static Class<?> forName(String className) throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
If you can get hold of a Class instance, you can always ask for the loader behind it by calling Class::getClassLoader() method. That will be your current class loader. The tricky bit, however, is to decide if the loader is bootstrap, or extension, or system class loader. The reason of the trickiness is that it is implementation specific, and you can always implement your own class loading mechanism.
The example given by #dmitrievanthony is an example of how things can become really complicated. It is a similar situation faced by JNDI, and the reason why the hack Thread.getContextClassLoader() was introduced. More about it here
Quote the most relevant piece from the article:
... By definition, a current classloader loads and defines the class to which your current method belongs. This classloader is implied when dynamic links between classes resolve at runtime, and when you use the one-argument version of Class.forName(), Class.getResource(), and similar methods. It is also used by syntactic constructs like X.class class literals ...
the 'current classloader' is the real classloader(load this class in reality) of class that refer it.
e.g.
if the classLoader of class A is ext classloader and class A refer class B C D. Then the 'current classloader' of B C D is ext classLoader. And of course the 'current classLoader' of main class is System classLoader.
I am currently loading Java classes using Class.forName() to load it.
clazz = Class.forName("interfaces.MyClass");
But now I want to load classes from different directory, I have tried to set classpath by
clazz = Class.forName("-cp \"C:/dir\" distantinterfaces.DistantClass");
With no success and ClassNotFoundException. Full path to distant class is:
C:/dir/distantinterfaces/DistantClass.class
Use an URLClassLoader for this. The code might be something along the lines of:
File f = new File("C:/dir");
URL[] cp = {f.toURI().toURL()};
URLClassLoader urlcl = new URLClassLoader(cp);
Class clazz = urlcl.loadClass("distantinterfaces.DistantClass");
Either the directory is in the classpath, and you can use Class.forName() (which only accepts fuly qualified name classes, and not -cp command line options), or it's not in the classpath and you should then use a custom class loader.
You're not saying what you really want to do (why are you loading classes dynamically), but your best bet is to have the directory in the classpath.
You have to create an instance of ClassLoader which is aware of the directory with classes. See stackoverflow questions tagged urlclassloader.
I am using reflection to get all methods from a specific class.
This class has references to class that not in my class path so I get an exception:
java.lang.NoClassDefFoundError:
On this:
Method methods[] = theClass.getDeclaredMethods();
Is it possible, somehow,to "skip" everything that is not in classpath?
Class.forName() will not load a class, whether it is or isn't in the classpath. It will only return a handle to a class that is already loaded.
A class gets loaded in one of 2 main ways:
1.)The class is referenced in the import statements(java.lang.* is imported automatically so every class in java.lang package is class-loaded from the start)
2.)A class is loaded using a call from a ClassLoader, in which case all of its dependencies are resolved. and loaded as well
So if you are trying to load a class outside of the classpath, or with dependencies outside the classpath, you need to subclass ClassLoader and tell it how to load your classes and their dependencies.
See ClassLoader specification here: http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/ClassLoader.html
Also, there are ready made subclasses of ClassLoader that may do what you want such as URL ClassLoader which will let you simply point the ClassLoader instance at the path, and load any classes in that path.