I have created a custom Java ClassLoader but I can't get the JVM to load it. I'm getting a ClassNotFoundException when I launch the application. The JAR that contains my code is added to the CLASSPATH as an argument to the JVM. Now I am wondering if I have the JAR in the correct place...do ClassLoaders get added here? I know that application code gets added to the CLASSPATH, but one could argue if a custom ClassLoader is application code or internal to the JVM. So, any ideas?
EDIT:
Ok, after thinking about this more, I think I may asking the JVM to do something circular. The ClassLoader I have created is a system class loader. These class loaders are the ones that search CLASSPATH for classes to load...but I have put the class loader itself there! So, if I'm correct, where do custom class loaders go?
I'm writing a tool which is supposed to determine what classes have changed when a system is upgraded. What i have as input are:
1 - the jar file used with the pre-upgraded system
2 - the jar file used with the post-upgraded system
3 - a list of classes and their purpose that I care about
asset init package1.myasset.class
asset terminate package1.assetterminate.class
etc.
What I want to do is be able to load the jar that comes with the new system and the jar that came with the old system and determine if a given class has the same parent after the change that it did before the change. i.e. if in the new system asset init
1 - is still package1.myasset.class and
2 - is still an extension of package0.generalasset.class
I assume I can use reflection but I'm not sure how to determine what a given classes parent is in each jar to see if it changed.
The jars are not necessarily used directly (runtime) by my tool - in fact they shouldn't really be used except as an input to the analysis of the system change.
You need two URLClassLoaders, one for each JAR file.
Process your list of classes and load each one in turn via both classloaders.
Do not attempt to cast them, they are just Class<?> at this stage.
Call Class.getSuperClass() on each, and then getName() on each parent class.
Compare.
Although there are many possibly important changes to a class other than its (single) parent, if that's really all you need I wouldn't bother with code, I'd just put both jars on a system with JDK and do:
javap -classpath jar1 aclassname |grep extends
javap -classpath jar2 aclassname |grep extends
# or substitute findstr on unimproved Windows
# repeat for multiple classes as desired
If you want to look for any change in the "public API", the full javap output is a good start.
Recently have been touched Java classloaders and suddenly recognized that do not fully understand what happens step-by-step when someone calls
java -jar App.jar
Well I guess
a new instance of JVM is created
it uses ClassLoader to load main class and other classes
byte-code is started to execute from main() method
But still I suppose there are many things I need to know more about it.
Who and how decides which classes should be loaded at startup and which once needed?
I have found two related questions but there it is not explained how to apply that to Java realities.
What happens when a computer program runs?
What happens when you run a program?
•Who and how decides which classes should be loaded at startup and which once needed?
we need to understand the fundamentals of java class loading. Initially bootstrap classloader (it is implemented natively as part of the VM itself) is responsible for loading core system classes. Then there are other class loaders as well like Extension, system, user-defined(optional) class loaders which decide when and how classes should be loaded.
Fundamentals of class loading
The decision is made by the classloader. There are different implementations, some of which pre-load all classes they can and some only loading classes as they are needed.
A class only needs to be loaded when it is accessed from the program code for the first time; this access may be the instantiation of an object from that class or access to one of its static members. Usually, the default classloader will lazily load classes when they are needed.
Some classes cannot be relied on to be pre-loaded in any case however: Classes accessed via Class.forName(...) may not be determined until this code is actually exectued.
Among other options, for simple experiments, you can use static initializer code to have a look at the actual time and order in which classes are actually loaded; this code will be executed when the class is loaded for the first time; example:
class SomeClass {
static {
System.out.println("Class SomeClass was initialized.");
}
public SomeClass() {
...
}
...
}
Your example shows an executable jar, which is simply a normal java archive (jar) with an extra key/value pair in it's manifest file (located in folder "META_INF"). The key is "Main-Class" and the value the fully qualified classname of that class whose "main" method will be executed, if you "run" the jar just like in your example.
A jar is a zip file and you can have a look inside with every zip archive tool.
Whenever you compile a Java program the following steps takes place
First the Class Loader loads the class into the JVM.
After giving the command javac filename.java the compiler checks for compile time errors and if everything is fine then it will generate the .Class files(byte code).
This will be the first phase.
Later the interpreter checks for the runtime errors and if everything is fine without exceptions then the interpreter converts the byte code to executable code.
First phase in java is done by the JIT compiler(Just In Time).
So, I'm working in eclipse were everything compiles and runs correctly. However, when compiling under ant for the build server, A large number of tests fail with a NoSuchMethodError saying:
class A implements B
interface B extends C
C requires method getSyncID() // standard getter for an int field.
A.java contains getSyncID()
A.class contains getSyncID()
and yet the Error is still thrown.
Does anyone know how the hell this could happen? how to fix it.
This happens when class files get out of sync with each other; in other words, one was compiled to a new version while another one wasn't. Try cleaning and rebuilding from scratch.
The problem occurs when the class loader discovers that a method in one class (A) calls a method in another class (B) that does not exist. The root cause is that the class B that the class loader sees is different to the class B that the compiler saw when it compiled A.
The most common cause of this problem is as #MarkPeters says - that your ".class" files have gotten out of sync with the ".java" files and need to be recompiled from scratch.
Another possibility is that you have an old copy of some of the ".class" files on the classpath when you try to run the application.
But either way, you have to believe the classloader. If it says that the method is not there, then it is not there ... in the particular ".class" file that it is loading. If the method appears to be there in the ".class" file, then that is evidence that you are loading a different version of the file earlier in the classpath!
I have read a lot about the Java class loading process lately. Often I came across texts that claimed that it is not possible to add classes to the classpath during runtime and load them without class loader hackery (URLClassLoaders etc.)
As far as I know classes are loaded dynamically. That means their bytecode representation is only loaded and transformed to a java.lang.Class object when needed.
So shouldn't it be possible to add a JAR or *.class file to the classpath after the JVM started and load those classes, provided they haven't been loaded yet? (To be clear: In this case the classpath is simply folder on the filesystem. "Adding a JAR or *.class file" simply means dropping them in this folder.)
And if not, does that mean that the classpath is searched on JVM startup and all fully qualified names of the found classes are cached in an internal "list"?
It would be nice of you if you could point me to some sources in your answers. Preferably the offical SUN documentation: Sun JVM Spec. I have read the spec but could not find anything about the classpath and if it's finalized on JVM startup.
P.s.
This is a theoretical question. I just want to know if it is possible. There is nothing practical I want to achieve. There is just my thirst for knowledge :)
There are two concepts here that are being intermixed: The classpath and the class files in the classpath.
If you point the classpath to a directory, you will generally have no issue adding a file to the directory and having it picked up as part of the classpath. Due to the potential size of all classes in the classpath it isn't really feasible for a modern JVM to load them all at startup. However this is of limited value as it will not include Jar files.
However, changing the classpath itself (which directories, jars, etc. are searched) on a running JVM will depend very much on the implementation. As far as I know, on standard Sun JVMs there is no documented (as in guaranteed to work) method of accomplishing this.
In general, if this is something you need to do (have a dynamic classpath that changes at runtime) then you want to be implementing a ClassLoader, if for no other reason than to be able to throw it away and make a new one that doesn't reference those classes anymore if they need to be unloaded.
However, for a small amount of dynamic loading there are better ways. In Java 1.6 you can specify all the jar files in a directory (*.jar) so you can tell users to put additional libraries in a specified location (although they have to be there at startup).
You also have the option of including a jar file or other location in the classpath even though you don't need it, as a placeholder for someone to put an optional jar or resource file there (such as a log configuration file).
But if you need serious dynamic class loading and especially unloading while the application is running, a Classloader implementation is required.
Since nobody could give my a definite answer nor a link to a corresponding part of the documentation I provide a answer myself. Nevertheless I would like to thank everybody that tried to answer the question.
Short answer:
The classpath is not final upon JVM start.
You actually can put classes in the classpath after the JVM started and they will be loaded.
Long answer:
To answer this question I went after user unknowns suggestion and wrote a little test program.
The basic idea is to have two classes. One is the main class which instantiates the second class. On startup the second class is not on the classpath. After the cli program started it'll prompt you to press enter. Before you press enter you copy the second class on the classpath. After you press enter the second class is instantiated. If the classpath would be final on JVM startup this would throw an Exception. But it doesn't. So I assume the classpath is not final on JVM startup.
Here are the source codes:
JVMTest.java
package jvmtest;
import java.io.Console;
import jvmtest.MyClass;
public class JVMTest {
public static void main(String[] args) {
System.out.println("JVMTest started ...");
Console c = System.console();
String enter = c.readLine("Press Enter to proceed");
MyClass myClass = new MyClass();
System.out.println("Bye Bye");
}
}
MyClass.java
package jvmtest;
public class MyClass {
public MyClass() {
System.out.println("MyClass v2");
}
}
The folder structure looks like this:
jvmtest/
JVMTest.class
MyClass.class
I started the cli program with this command:
> java -cp /tmp/ jvmtest.JVMTest
As you can see I had my jvmtest folder in /tmp/jvmtest. You obviously have to change this according to where you put the classes.
So here are the steps I performed:
Make sure only JVMTest.class is in jvmtest.
Start the program with the command from above.
Just to be sure press enter. You should see an Exception telling you that no class could be found.
Now start the program again.
After the program started and you are prompted to press enter copy the MyClass file into the jvmtest folder.
Press enter. You should see "MyClass v1".
Additional notes:
This also worked when I packed the MyClass class in a jar and run the test above.
I ran this on my Macbook Pro running Mac OS X 10.6.3
> Java -version
results in:
java version "1.6.0_20"
Java(TM) SE Runtime Environment (build 1.6.0_20-b02-279-10M3065)
Java HotSpot(TM) 64-Bit Server VM (build 16.3-b01-279, mixed mode)
#Jen I don't think your experiment can prove your theory, because it is more about object instantiation: your printline happens when an object of this class is instantiated, but not necessarily telling that JVM knows your code, the class, just when it is instantiating.
My opinion is that all Java classes are loaded when JVM is up, and it is possible to plug in more classes into JVM while it is running: this technique is called: Hot deployment.
Bottom line: it is possible to add entries to the system classpath at runtime, and is shown how. This, however, has irreversible side-effects and relies on Sun JVM implementation details.
Class path is final, in the most literal sense:
The system class loader (the one that loads from the main class path) is sun.misc.Launcher$AppClassLoader in rt.jar.
rt.jar:sun/misc/Launcher.class (sources are generated with Java Decompiler):
public class Launcher
{
<...>
static class AppClassLoader
extends URLClassLoader
{
final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
<...>
rt.jar:sun/misc/URLClassLoader.class:
protected Class<?> findClass(final String paramString)
throws ClassNotFoundException
{
<...>
String str = paramString.replace('.', '/').concat(".class");
Resource localResource = URLClassLoader.this.ucp.getResource(str, false);
<...>
But, even if the field is final, this doesn't mean we can't mutate the object itself if we somehow get access to it. The field is without an access modifier - which means, we can access it if only we make the call from the same package. (the following is IPython with JPype; the commands are readable enough to easily derive their Java counterparts)
#jpype doesn't automatically add top-level packages except `java' and `javax'
In [28]: jpype.sun=jpype._jpackage.JPackage("sun")
In [32]: jpype.sun.misc.Launcher
Out[32]: jpype._jclass.sun.misc.Launcher
In [35]: jpype.sun.misc.Launcher.getLauncher().getClassLoader()
Out[35]: <jpype._jclass.sun.misc.Launcher$AppClassLoader at 0x19e23b0>
In [36]: acl=_
In [37]: acl.ucp
Out[37]: <jpype._jclass.sun.misc.URLClassPath at 0x19e2c90>
In [48]: [u.toString() for u in acl.ucp.getURLs()]
Out[48]: [u'file:/C:/Documents%20and%20Settings/User/']
Now, URLClassPath has a public addURL method. Let's try it out and see what happens:
#normally, this is done with Launcher.getFileURL but we can't call it directly
#public static URLClassPath.pathToURLs also does the same, but it returns an array
In [72]: jpype.sun.net.www.ParseUtil.fileToEncodedURL(
jpype.java.io.File(r"c:\Ivan\downloads\dom4j-2.0.0-RC1.jar")
.getCanonicalFile())
Out[72]: <jpype._jclass.java.net.URL at 0x1a04b50>
In [73]: _.toString()
Out[73]: u'file:/C:/Ivan/downloads/dom4j-2.0.0-RC1.jar'
In [74]: acl.ucp.addURL(_72)
In [75]: [u.toString() for u in acl.ucp.getURLs()]
Out[75]:
[u'file:/C:/Documents%20and%20Settings/User/',
u'file:/C:/Ivan/downloads/dom4j-2.0.0-RC1.jar']
Now, let's try to load some class from the .jar:
In [78]: jpype.org=jpype._jpackage.JPackage("org")
In [79]: jpype.org.dom4j.Entity
Out[79]: jpype._jclass.org.dom4j.Entity
Success!
This will probably fail from a sandbox or such where there are custom class loaders or security settings in the way (AppClassLoader.loadClass does security checks before calling super).
Further code inspection shows that addURL also disables the URLClassPath's lookup cache (implemented in a few native methods), and this is irreversible. Initially, the lookupCacheEnabled flag is set to the value of the sun.cds.enableSharedLookupCache system property.
The interface provides no way to edit the entries. URLs are added to URLClassPath's private ArrayList path and Stack urls. urls is accessible, but it turns out, it only holds entries temporarily, before it's attempted to load from it, at which point the information moves to HashMap lmap and ArrayList loaders. getURLs() returns a copy of path. So, it's theoretically possible to edit it by hacking the accessible fields, but it's nowhere near reliable and won't affect getURLs result.
I can only comment from what I remember of my own experience of hacking a non-sun JVM ten years ago, but it did scan the entire classpath on startup, as an efficiency measure. The names of all classes found were added to an internal hashtable along with their locations (directory or zip/jar file). However, that was ten years ago and I can't help but wonder whether this would still be a reasonable thing to do in most settings considering how disk and memory architectures have evolved.
I believe that the classpath is taken to be static and the result of changing the files is undefined.
If you really want to be able to add and remove classes at runtime, consider doing so in your own classloader. This is what web containers do.
So shouldn't it be possible to add a
JAR or *.class file to the classpath
after the JVM started ...
You add jars and directories to the classpath, not classes. The classes are in either the directory, or the jar.
And if not, does that mean that the
classpath is searched on JVM startup
and all fully qualified names of the
found classes are cached in an
internal "list"?
That could be easily tested: Set the classpath, start your program, move a new class into the CP, call 'Class.forName ("NewClass") from your program. Does it find the new class?
I think you could read documentation of TomCat server. This server implements java classpapth by its own. So, when this server is started, you can deploy new webApp just drag and drop jar in appropriatefolder in hot, without restart server, and it will upload your app.