Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access static method from classes loaded by different ClassLoaders

I have two classes (A and B) which are loaded by different ClassLoaders. Furthermore, I have a third class, which providers static getter and setter methods. I hope following picture can clarify the situation:

enter image description here

The Data class looks as following:

public class Data {

    private static String data = "<fill in>";

    public static void setData(String d) {
        data = d;
    }

    public static String getData() {
        return data;
    }
}

In class A, I want to set the static value of Data and in B I want to retrieve this value. However, in B I always get the original value (which is "<fill in>"). I only have a basic understanding of ClassLoaders, so I'm not too sure what is going on under the hood. I thought that both ClassLoaders (clA and clB) will propagate to their parent ClassLoader and that I will get the same Data class in both. Can anyone give me some feedback on the behavior or point me in the direction to look at?

Update

When I print the hashCode() of both Data classes, I get different values for them (meaning obviously I don't get access the same class). Is there and easy way to illustrate the ClassLoader hierarchy?

like image 215
WeSt Avatar asked Nov 30 '14 20:11

WeSt


People also ask

Can a class be loaded by two different Classloaders in Java?

Class Loader Rule 1 A class is always identified using its fully qualified name (package. classname). So when a class is loaded into JVM, you have an entry as (package, classname, classloader). Therefore the same class can be loaded twice by two different ClassLoader instances.

Can main access other static methods?

The main() method cannot have access to the non-static variables and methods, you will get “non-static method cannot be referenced from a static context” when you try to do so. This is because by default when you call/access a method or variable it is really accessing the this.

When static methods are loaded in Java?

Static Block in Java It executes whenever the class is loaded in memory. One class can have numerous static blocks, which will be executed in the same sequence in which they are written.

What are the different Classloaders in Java?

As we can see, there are three different class loaders here: application, extension, and bootstrap (displayed as null). The application class loader loads the class where the example method is contained. An application or system class loader loads our own files in the classpath.


2 Answers

If your question is how to illustrate or visualize the classloader hierarchy for objects, then you can walk up each classes classloader in code. You mentioned that you are using groovy, so an example would look like:

def showObjectClassLoaderHierarchy(Object obj) {
    def classLoader = showClassLoaderHierarchy(obj.getClass().getClassLoader());
    showClassLoaderHierarchy(classLoader);
}

def showClassLoaderHierarchy(ClassLoader loader) {

    if (loader != null) {
        println "Classloader: " + loader.hashCode();
        while (loader.getParent() != null) {
              loader = loader.getParent();
          println "    Child of: " + loader.hashCode();
        }
    }

}

I think you will find, in your code, the two Data objects are actually not loaded from the same classloader, which is why they have different static variables.

I put together a sample that has

  • Main (loaded from parent classloader)
  • DataObj with a static String (loaded also from parent classloader)
  • LoadA, which instantiates a copy of DataObj (loaded from child classloader A)
  • LoadB, which instantiates a copy of DataObj (loaded from child classloader B)

I see that while LoadA and LoadB have different classloaders, the DataObj and the static variable come from a common classloader.

Full code at: https://github.com/lucasmcgregor/groovy_classloader_test

The Main object in groovy:

import java.lang.ClassLoader;
import java.net.URLClassLoader;
import java.net.URL;

def showObjectClassLoaderHierarchy(Object obj) {
        def classLoader = showClassLoaderHierarchy(obj.getClass().getClassLoader());
        showClassLoaderHierarchy(classLoader);
}

def showClassLoaderHierarchy(ClassLoader loader) {

        if (loader != null) {
            println "Classloader: " + loader.hashCode();
            while (loader.getParent() != null) {
                  loader = loader.getParent();
                  println "    Child of: " + loader.hashCode();
            }
        }

}

println "Setting up child classLoaders A and B...";

def URL[] urlsA = [new URL("file:///tmp/cla/")];
def classLoaderA = new URLClassLoader(urlsA, this.getClass().getClassLoader());

def URL[] urlsB = [new URL("file:///tmp/clb/")];
def classLoaderB = new URLClassLoader(urlsB, this.getClass().getClassLoader());


println "Classloader A heirachry:";
showClassLoaderHierarchy(classLoaderA);

println "Classloader B: ";
showClassLoaderHierarchy(classLoaderB);

println "";
println "Now loading Load classes A and B from seperate classloaders:";
def loadA = classLoaderA.loadClass("LoadA").newInstance();
def loadB = classLoaderB.loadClass("LoadB").newInstance();

print "LoadA: heirachry";
showObjectClassLoaderHierarchy(loadA);
print "LoadB: heirachry";
showObjectClassLoaderHierarchy(loadB);

println "";
println "Now pulling the data objects from both and comparing classloders and static data: ";
def dobjA = loadA.getDataObj();
def dobjB = loadB.getDataObj();

println "dataA static field:" + dobjA.getData();
println "dataA static field hashcode: " + dobjA.getData().hashCode();
println "dataA hashcode: " + dobjA.hashCode();
println "dataA classloader: ";
showObjectClassLoaderHierarchy(dobjA);

println "dataB static field: " + dobjB.getData();
println "dataB static field hashcode: " + dobjB.getData().hashCode();
println "dataB hashcode: " + dobjB.hashCode();
println "dataB classLoader:";
showObjectClassLoaderHierarchy(dobjB);

The results are:

Setting up child classLoaders A and B...
Classloader A heirachry:
Classloader: 1926764753
    Child of: 1163157884
    Child of: 1022308509
Classloader B:
Classloader: 846238611
    Child of: 1163157884
    Child of: 1022308509

Now loading Load classes A and B from seperate classloaders:
LoadA: heirachryClassloader: 1926764753
    Child of: 1163157884
    Child of: 1022308509
LoadB: heirachryClassloader: 846238611
    Child of: 1163157884
    Child of: 1022308509

Now pulling the data objects from both and comparing classloders and static data:
dataA static field:Loaded By B
dataA static field hashcode: 1828548084
dataA hashcode: 2083117811
dataA classloader:
Classloader: 1163157884
    Child of: 1022308509
dataB static field: Loaded By B
dataB static field hashcode: 1828548084
dataB hashcode: 157683534
dataB classLoader:
Classloader: 1163157884
    Child of: 1022308509

You see that LoadA and LoadB both have different classloaders, but they share a parent classloader.

The parent classloader loads the DataObj for both instances of the LoadA.dataObj and LoadB.dataObj.

LoadA.dataObj and LoadB.dataObj have different hashcodes.

However, LoadA.dataObj.data and LoadB.dataObj.data have the same hashcode, because this is the static object. They also have the same value. LoadB instantiates it's dataObj last and sets the string to "Loaded By B"

like image 170
Lucas McGregor Avatar answered Oct 10 '22 10:10

Lucas McGregor


I think Lucas actually answered your question for illustrating the hierarchy. I want to add my answer only to clear up some reminders of the question

In Java the pair (defining classloader,class name) is unique. Defining classloader here means the loader, which actually realizes the class as class from bytecode. This uniqueness means a classloader defining the class X cannot define a second class X, it must have a different name. But another classloader can define the class. ClassLoaders are structured in a kind of tree (it is not really a DAG, but that goes to far here) and a classloader is supposed to ask its parent first if queried for a class. So it can happen, that Data exists twice, for example once in cIA and once in cIB. To avoid this you usually want to have Data being defined by a classloader which is a parent to cIA and cIB. That is assuming the two loaders behave according to the classloader constraints, like asking parents first.

Since this is also about a Groovy script, but there are no real details given about the setup my assumption is that ciB has no parent that knows Data and that the library was given in to the url of the GroovyClassLoader used and that you used GroovyShell. What should be done instead is that GroovyShell is instantiated with one of the classloader taking arguments and that this classloader is a child to the loader defining Data, which is also a parent to cIA (parent can be the same loader in all cases I used the term parent).

A warning... GroovyClassLoader is not a fully well behaving class loader. It will prefere defined classes (for example through a script) over classes from the parent. If you have a script with the class Data in it, it will use that Data class, even if the parent is normally the defining classloader

like image 24
blackdrag Avatar answered Oct 10 '22 10:10

blackdrag