Validate Java Classes Inside Jar - java

Java class files inside jars can be easily replaced and modified. For instance, the following command can be used to replace a compiled class file within a jar:
jar uf JarFile.jar com\something\Class.class
If the class file was replaced with a file such that no dependencies were broken, then the code is still able to execute. The same happens with class files that are not inside jars.
Is there any way to validate a set of class files (whether inside a jar or not) to see if all their dependencies are present and not broken?
I do not want to prevent class files from being modified but rather to be able to verify that changes are valid (with respect to dependencies). The compiler does this check (dependency-check) at compile time, but once the classes are compiled, how can one verify the class files themselves?

You might have sealing and signing JARs in mind.
Update:
Apparently I've missed the mark with my first guess.
What do you plan to do if they're not? If they're a 3rd party, I'd say that you've got little choice besides reporting to the bug database that the download is bad.
If you mean "I want to make sure that all their 3rd party JAR dependencies are correct", you've got a much bigger problem. Most downloads that I know of (e.g. Spring) make dependencies available using Maven. That's the best you can do.
If you mean you want to check your own dependencies, I'd say that testing would reveal any errors you've made.

Just loading the class will ensure that.

no, you cannot.
at least: not really.
the problem is that java loads classes at runtime only when needed. so eventually it might be alright to remove a class from the jar file and as long as no code referencing that class is executed things run very smoothly.
consider this example:
class A{ public static void main( String args[] ){ out.println( "hello" ); } }
class B{}
compile this, put it in a jar, remove the B.class from it, no problem there :)
now you might think you can go through each .class file, check what classes it references and see if the files are all there. not only is this painful, it is also incomplete. you will never quite catch files loaded with reflection because their class names might be constructed just at runtime.
my advice: don't go there. if someone removes a class file it's their own fault.
the best thing you can do is (but only if this really really worries you) try to catch ClassNotFoundExceptions at runtime (look into thread.setUncaughtExceptionHandler)

Related

how to compile extended class in java?

i want to compile child.java class an get child.class, child class extends parent class.
i want child.class include all necessary code from parent class, in fact i can use it independently.
is it possible?
second Question: suppose we have a .jar library containing a.class , b.class , c.class ... i have make an updated version of a.class, how can i replace the new one with the original a.class in jar? is it possible ?
sorry for bad English.
1) as Dmitry mentioned in the comment is impossible. Even if your parent class did absolutely nothing it's definition will still be checked, and if not found ClassNotFoundException (or similar) will be thrown
2) Is certainly possible and usefull in some situations (patching external library without recompiling everything from sources comes to mind) - see Is there are way to patch jar files?
Specifically the most usefull (IMO) is CoolBeans's answer:
jar uf test.jar com\test\Test.class
Note that you have to take care of correct packaging. And it will not work if the jar is signed.

Class loading by bootstrapclassloader

Here is what I was asked in interview:
If a new method is added to String class and compiled and put in
rt.jar, then will bootstrap class loader load it?
I answered that it will not get loaded but could not tell why.
Please help me with correct answer and explanation of that.
It will be loaded.
And not just that: it need not even be in rt.jar, if you prepend a jar file to the boot-classpath (see -Xbootclasspath/p:path), then you can even load java.lang.* classes from other jar files.
This of course is a way to violate the security of the JVM, but you need pretty deep access (either write-access to rt.jar or access to the command line parameters of the JVM) and when you have those, you can do a lot more than just replace String.toString().

ClassNotFoundException occured when replace a jar in runtime

I run a java application packaged in A.jar, in which some classes in B.jar are used.
All related jars are placed in a specific directory, which is included in the classpath.
The program is like this:
main(){
run method ClassA.M1() in A.jar; //the method may keep running for 2 minutes
do some other prepare;
call method ClassB.M2 in B.jar;
}
When the program is running M1, I manually replaced B.jar with a newer version(the name is also B.jar).
But, the program throw ClassNotFoundException.
Then, start the program again, and it works fine.
so, my question is: why the ClassNotFoundException is thrown, as the jar path and jar name is not changed, the classloader should load it without any troubles.
Give me some directions please.
You cannot simply change jar files during runtime by replacing them with ones having the same name, because the class loader might have already loaded some classes from it.
If you need to support such behavior, you need to look into libraries or frameworks that would provide hot replace mechanisms.
Here are two articles to help you understand class loaders better:
The Basics of Java Class Loaders
(1996)
Understanding the Java ClassLoader
(2001)
There are plenty more articles on this subject and even related questions here on the Stack OverFlow, I recommend you read more.

How do I prevent implicit Java compilation when the class exists in the classpath?

Let's say that I have two classes (Bob and Tom) such that Bob uses Tom, but Tom does not require Bob. Both of these files are located in the same directory structure.
public class Bob {
public static void main(String[] args) {
System.out.println(Tom.MESSAGE);
}
}
public class Tom {
public static String MESSAGE = "Hello World";
}
If I attempt to compile Bob in the typical fashion, I can get it to implicitly compile Tom.java as well since it is recognized as a dependency of Bob.java. That works great.
But now let's say that I want to place Tom in a JAR file. So I build Tom.java and place the results into a JAR file: libTom.jar. I now compile with the classpath pointing at libTom.jar. Unfortunately, the compiler sees the Tom.java file and causes it to be compiled rather than using the class in libTom.jar. Is there a way to get the compiler to skip the implicit construction of Tom.java if the class is found in the classpath?
I recognize that this example is fairly contrived. Rest assured that there is a more complicated, less contrived use-case that surrounds this issue. Thanks.
If there are two classes with the same name and in the same package in the classpath, it is difficult to predict which one will get picked up when java compiles the code. In case the Tom class exists in the source itself, it will surely get picked up.
One way you can avoid it is to put one of the Toms in a different package. Another way is if you can move your current Tom to a separate project. I understand either of these might not be practical for you. If it is really necessary, you could experiment with writing your own ClassLoader. See this question for reference: Jar hell: how to use a classloader to replace one jar library version with another at runtime
It appears that this just isn't possible, which is what I feared from the beginning.
Yes, you could do that by typing the full name of a class, i.e. Typing
System.out.println(the.package.of.external.Tom.MESSAGE);
System.out.println(the.current.package.Tom.MESSAGE);
Can you split the java files into separate source directories?
This would make sense as you could build your jar from the contents of one source dir, then just include the other source dir when compiling all the other set of files.
This works well in Eclipse.
Even better would be to use two projects in Eclipse.

Is the Java classpath final after JVM startup?

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.

Categories

Resources