Java ClassLoader: findClass is not called again after ClassNotFoundException occurs - java

Context:
I have a microservice that at application BootStrap goes and gets all the classes it needs from another microservice as a Zip, then it loads all the classes this Zip contains and executes some code.
Problem:
What we are experiencing is that in some cases the service that gives the Zip with the classes does not answer (this is not the problem I want to address here).
The problem is that when this happens we throw a ClassNotFoundException and execute again and in this execution the programm again detects it needs to load a class it does not have... So it goes to the findClass() method and tries to get it by calling the Microservices asking for this class, but the service that gives the Zip with the class again does not respond so we throw another ClassNotFoundException... And again execute but this time it does not even try to call findClass() method, it is like Java is saying "okay this is definetely not here so im not even going to bother calling findClass()", I am trying to find where in the documentation is this specified (because I wanna see where is this specified, is it normal?).
Oracle Documentation ClassLoader
The closest I could find to what I experience is this
But in that case they do have the class, but the loader does not bother in loading it again because it detects it already has it, we are experiencing the same but in reverse, the loader does not have it, and it does not bother in trying again. Where is this in the docs?

Actually, the absence of a guaranty that multiple resolution attempts will be made is already enough to allow the behavior of remembering and reusing the result of the first symbol resolution attempt, whether it is a failure or success.
But it’s even mandated behavior, as specified in The Java Virtual Machine Specification, §5.4.3. Resolution:
Subsequent attempts to resolve the symbolic reference always fail with the same error that was thrown as a result of the initial resolution attempt.
This is not be confused with manual invocations of loadClass on a ClassLoader instance.

Related

Is it possible to redefine core JDK classes using instrumentation?

I want to redefine the bytecode of the StackOverflowError constructor so I have a "hook" for when a stack overflow occurs. All I want to do is insert a single method call to a static method of my choosing at the start of the constructor. Is it possible to do this?
You should be able to do it using one of two ways (unless something changed in the last 1-2 years, in which case I'd love some links to changelogs/docs):
Mentioned in a comment, not very feasible I guess, modify the classes you are interested in, put them in a jar and then use the -bootclasspath option to load them instead of the default ones. As was mentioned before this can have some legal issues (and is a pain to do in general).
You should be able to (or at least you used to be able to) instrument almost all core classes (iirc Class was the only exception I've seen). One of many problems you might have is the fact that many of core classes are being initialized before the agents you provide (or well their premain methods to be exact) are consulted. To overcome this you will have to add Can-Retransform-Classes property to your agent jar and then re-transform the classes you are interested in. Be aware that re-transformation is a bit less powerful and doesn't give you all the options you'd have normally with instrumentation, you can read more about it in the doc.
I am assuming you know how to do instrumentation?
There are several things to consider.
It is possible to redefine java.lang.StackOverflowError. I tried it successfully on 1.7.0_40. isModifiableClass(java.lang.StackOverflowError.class) return true and I successfully redefined it inserting a method invocation into all of its constructors
You should be aware that when you insert a method call into a class via Instrumentation you still have to obey the visibility imposed by the ClassLoader relationships. Since StackOverflowError is loaded by the bootstrap loader it can only invoke methods of classes loaded by the bootstrap loader. You would have to add the target method’s class(es) to the bootstrap loader
This works if the application’s code throws a StackOverflowError manually. However, when a real stackoverflow occurs, the last thing the JVM will do is to invoke additional methods (keep in mind what the error says, the stack is full). Consequently it creates an instance of StackOverflowError without calling its constructor (a JVM can do that). So your instrumentation is pointless in this situation.
As already pointed out by others, a “Pure Java Application” must not rely on modified JRE classes. It is only valid to use Instrumentation as add-on, i.e. development or JVM management tool. You should keep in mind that the fact that Oracle’s JVM 1.7.0_40 supports the redefinition of StackOverflowError does not imply that other versions or other JVMs do as well.

How do I control which ClassLoader loads a class?

The situation at hand is not as simple as the title seems to indicate.
Java 1.6_17 running via JWS.
I have a class, lets say MyClass and one of its instance member variables is a Type from an errant 3rd party library where during class initialization it dynamically tries loading some of its own classes with Class.forName(String). In one of these cases it happens to dynamically call: Class.forName("foo/Bar").This class name doesn't follow the JLS for binary names and ultimately leads to a java.lang.NoClassDefFoundError: foo/Bar.
We have a custom ClassLoader which I've added a sanitize method to ClassLoader.findClass(String) and ClassLoader.loadClass(String) which fixes this problem.
I can call stuff like:
myCustomClassLoader.findClass("foo/Bar")
Which then loads the class without any problems. But even if I load the class ahead of time, I still get the exception later. This is because during initialization of MyClass which refers to Bar - their code ends up calling Class.forName("foo/Bar") in a static block somewhere. This actually would be OK if the ClassLoader it was trying to use was my custom class loader. But it isn't. It is the com.sun.jnlp.JNLPClassLoader which doesn't do such sanitation, thus my problem.
I've made sure that Thread.currentThread().getContextClassLoader() is set to my custom class loader. But this (as you know) has no effect. I even set it as the first thing i do in main() due to some stuff I read and still, MyClass.class.getClassLoader() - is the JNLPClassLoader. If I could force it to NOT be the JNLPClassLoader and to use mine instead, problem solved.
How can I control which ClassLoader is used to load the class via their static Class.forName("foo/Bar") call made during class initialization? I believe if I can force MyClass.class.getClassLoader() to return my custom class loader, my problem will be resolved.
I'm open to other options if anyone has ideas.
TL;DR: Help me force all Class.forName(String) calls in a third party library which are referenced by MyClass - to use the classloader of my choosing.
This reminds me of an article I read 10 years ago about the classloading arrangements in Java. It's still there on JavaWorld.
The article won't answer your question directly, but it may help understand your problem. You need to cause MyClass to be loaded through your custom class loader and trump the default class loading behavior, which is to first delegate class loading to the parent classloader and only attempt to load a class if that fails.
Allowing MyClass to get loaded by a classloader other than yours will store a relationship from the instantiated class to that classloader (via getClassLoader) and cause Java to use that other classloader to try to discover any referenced classes found at compile time, effectively bypassing your custom class loader by virtue of the class loader hierarchy and the delegation model. If MyClass is instead defined by your class loader, you get a second chance.
It sounds like a job for something like URLClassLoader, overriding loadClass and trumping the delegation model for classes residing in your JARs. You'll probably want to use a bootstrap approach (as suggested by Thomas in a comment above) to force a single entrypoint class to be loaded through your custom class loader, dragging all the others with it.
Also informative is this other JavaWorld article by the same guy, which warns you about the caveats of Class.forName. That too may trip your classloading arrangements.
I hope this helps and proves informative. In any case, it sounds like a difficult solution that is easy to break as your code evolves.
I think everyone gave good solid attempts at answering the problem. However, it turns out that I misdiagnosed the problem.
I had a coworker take over the problem and asked him to get a JDK with debug flags on so we could debug the JNLPClassLoader to see what was going on as I had tried all of the suggestions here + some.
We ended up getting OpenJDK because recompiling the JDK from scratch is a total nightmare (we tried). After getting OpenJDK working with our product and debugging through the JNLPClassLoader - it turns out that it was still using a REALLY old .jnlp from months earlier that had the resource path wrong and thus why it couldn't find the class.
We were confused why it was still using the ancient .jnlp even though we had redeployed the server correctly many times with the correct .jnlp and lots of code changes between which were reflected in our client application when run.
Well, it turns out that on client machines, Java caches the .jnlp file. Even if your application changes and it redownloads your application, it still won't re-download the new .jnlp for whatever reason. So it will use all of the new code, but look up resources/class paths using the cached .jnlp.
If you run:
javaws -uninstall
On the client machine then that will clear the .jnlp cache and next time it will use the correct .jnlp file.
Really sad that this was the problem. Hopefully, this saves someone else endless hours of frustration like it caused us.
If you run out of ideas with patching the ClassLoaders themselves, you might consider rewriting the library bytecode itself -- just replace the "foo/bar" constant with the correct value, and then you don't need to customize further class loading at all!
You could do this either at runtime or beforehand.

why running android 2.3.4 and android 1.6 emulator throw different exception?

I test some code
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Fragment f = new Fragment();
}
I usd sdk4.0 compile, and AndroidManifest.xml use-sdk minSdk=4 targetSdkVersion=14
When I run it on android 1.6 emulator it throw java.lang.VerifyError, but on phone running android 2.3.4 it throws java.lang.NoClassDefFoundError.
Why??
I do not need Fragment class, I just test a Class that include android 3.0 sdk. I just want to see what will print.
java.lang.VerifyError(Thrown when the VM notices that an attempt is made to load a class which does not pass the class verification phase.)
java.lang.NoClassDefFoundError
(Thrown when the VM is unable to locate a class which it has been asked to load.)
They both extend LinkageError,LinkageError is the superclass of all error classes that occur when loading and linking class files.
I found this in dalvik docs
Verification Failures
The verifier may reject a class immediately, or it may defer throwing
an exception until the code is actually used. For example, if a class
attempts to perform an illegal access on a field, the VM should throw
an IllegalAccessError the first time the instruction is encountered.
On the other hand, if a class contains an invalid bytecode, it should
be rejected immediately with a VerifyError.
Immediate VerifyErrors are accompanied by detailed, if somewhat
cryptic, information in the log file. From this it's possible to
determine the exact instruction that failed, and the reason for the
failure.
It's a bit tricky to implement deferred verification errors in Dalvik.
A few approaches were considered:
We could replace the invalid field access instruction with a special
instruction that generates an illegal access error, and allow class
verification to complete successfully. This type of verification must
be deferred to first class load, rather than be performed ahead of
time during DEX optimization, because some failures will depend on the
current execution environment (e.g. not all classes are available at
dexopt time). At that point the bytecode instructions are mapped
read-only during verification, so rewriting them isn't possible. We
can perform the access checks when the field/method/class is resolved.
In a typical VM implementation we would do the check when the entry is
resolved in the context of the current classfile, but our DEX files
combine multiple classfiles together, merging the field/method/class
resolution results into a single large table. Once one class
successfully resolves the field, every other class in the same DEX
file would be able to access the field. This is incorrect. Perform the
access checks on every field/method/class access. This adds
significant overhead. This is mitigated somewhat by the DEX optimizer,
which will convert many field/method/class accesses into a simpler
form after performing the access check. However, not all accesses can
be optimized (e.g. accesses to classes unknown at dexopt time), and we
don't currently have an optimized form of certain instructions
(notably static field operations). In early versions of Dalvik (as
found in Android 1.6 and earlier), the verifier simply regarded all
problems as immediately fatal. This generally worked, but in some
cases the VM was rejecting classes because of bits of code that were
never used. The VerifyError itself was sometimes difficult to
decipher, because it was thrown during verification rather than at the
point where the problem was first noticed during execution.
The current version uses a variation of approach #1. The dexopt
command works the way it did before, leaving the code untouched and
flagging fully-correct classes as "pre-verified". When the VM loads a
class that didn't pass pre-verification, the verifier is invoked. If a
"deferrable" problem is detected, a modifiable copy of the
instructions in the problematic method is made. In that copy, the
troubled instruction is replaced with an "always throw" opcode, and
verification continues.
You need to be in x:\android-sdk\extras\android\support\v4 find android-support-v4. Jar added to the project.
In the project
import android.support.v4.app.Fragment;

ClassCircularityError inside custom SecurityManager

I am writing a SecurityManager and getting ClassCircularityError exceptions while running a unit test. Examining the stacktrace shows it is complaining about some class that is referenced inside my SM.checkPermission method. To guarantee all classes in my SM.checkP are loaded i cheated and call it once before i officially set it as the System SM. This however does not solve the problem. I am utterly confused why the JVM is attempting to load a class again.
It appears i missed pre-loading one class that is referenced inside my SM, thus is it was having trouble loading that class as it needed it to be loaded before it could verify the load attempt. Ouch.

Java: Finding out *why* a class is loaded

I am currently having the problem that I have a (partial) program that is trying to load a class but fails because it cannot find this class. Looking at the stack trace, I cannot see any particular reason for why the VM tries to load this particular class at the first place. Are there any tools that would let me figure out why a particular class is being loaded?
Hint:
I am already getting a stack trace at the exact point where the JVM tries to load the class (through an agent). However, the stack trace contains no line numbers. Therefore I only know which method triggers the class being loaded, not which statement. Then, even knowing the statement may not be enough. A single statement can cause a class to be loaded in many ways, because sometimes the VM needs to load part of the transitive closure of classes.
Run your program with the -XX:+TraceClassLoading and -XX:+TraceClassResolution flags. This will create a LOT of output that looks like the following:
[Loaded com.kdgregory.example.memory.PermgenExhaustion$MyClassLoader from file:/home/kgregory/Workspace/Website/programming/examples/bin/]
RESOLVE com.kdgregory.example.memory.PermgenExhaustion$MyClassLoader java.net.URLClassLoader
RESOLVE java.net.URLClassLoader java.lang.Class URLClassLoader.java:188
You'll need to trace the chain of RESOLVE messages for a particular class. Or more likely, you'll see an error when your program attempts to load the class, preceeded by resolve messages for the class that loads it).
You might try a static analysis tool like JDepend to see what classes have references to that class.
Classloaders
If it's an envorinment where multiple class-loaders are in the game (like a web application) you should be careful. Please tell us what's the app.
resource
Tell us where are the jars (you file/dir structure) and what class is loading. Are you loading it dinamically using Class.forName? or using spring or another framework of IOC? Is it the main class?
Some previous testing (to help us)
Maybe you can test some things using Class.getResource() from within the main method. If you class is foo.bar.Clazz try Class.getResource("/foo/bar/Clazz.class") to see if it returns something valid or not. Try to do the same with the class that loads your failing class to see if it's where you expect.

Categories

Resources