Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing Package Private element between parent / child classloaders

In a Java application, I have two classes declared, one class (One) declared in ClassLoader A and another class (Two) declared in ClassLoader B. ClassLoader A is B's parent. Both of these classes have the same package (ie: org.test).

I cannot seem to access One's package private methods or varialbes from Two event though A is B'd parent ClassLoader, I get an IllegalAccessError exception. I understand that package private accessibility is based both on the package name and the ClassLoader.

Is there a way to re-associate One and Two so that Two can access One's Package Private elements?

Here's the test to demonstrate this:

package org.test;

public class ClassLoaderTest {

    @Test
    public void testLoading() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        One one = new One();
        one.value = "test";

        MemoryClassLoader classLoader = new MemoryClassLoader();

        String name = one.getClass().getPackage().getName() + ".Two";

        classLoader.add(name,
            "package org.test;\n" +
            "\n" +
            "public class Two{\n" +
            "    public static String getValue(One one){\n" +
            "        return one.value;\n" +
            "    }\n" +
            "}");

        Class<?> twoClass = classLoader.loadClass(name);

        assertEquals("test", twoClass.getMethod("getValue", One.class).invoke(null, one));
    }
}

public class One{
    String value;
}

MemoryClassLoader can be found here.

which errors with:

testLoading(org.test.ClassLoaderTest)  Time elapsed: 0.214 sec  <<< ERROR!
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.test.ClassLoaderTest.testLoading(ClassLoaderTest.java:37)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    ....
Caused by: java.lang.IllegalAccessError: tried to access field org.test.One.value from class org.test.Two
    at org.test.Two.getValue(Two.java:5)
    ... 34 more

Thanks.

Edit:

I've produced a Gist with a self contained test demonstrating this here.

like image 670
John Ericksen Avatar asked Feb 22 '14 20:02

John Ericksen


People also ask

Can private method be accessed by child?

You cannot call a private method of a class from any other class(be it the extending class). It can only be accessed inside that class itself. There is a reason it is said to be private .

What is Classloader in Servlet?

In effect, a classloader is responsible for loading only the classes not available to the parent. The exception is the web application classloader, which follows the delegation model in the Servlet specification. The web application Classloader looks in the local classloader before delegating to its parent.

What is platform class loader in Java?

A class loader is an object that is responsible for loading classes. The class ClassLoader is an abstract class. Given the binary name of a class, a class loader should attempt to locate or generate data that constitutes a definition for the class.

How to load a class in Java?

Load class with forName() method in Java The class object associated with the class with the given string name can be returned with the method java. lang. Class. forName(String name, boolean initialize, ClassLoader loader), using the class loader that is used to load the class.


1 Answers

Is there a way to re-associate One and Two so that Two can access One's Package Private elements?

Simple Answer: With Reflection: YES; Without Reflection: NO.

Detailed Answer: Java has its own mechanisms to control the access, prevention of users of a package or class from depending on unnecessary details of the implementation of that package or class. If access is permitted, then the accessed entity is said to be accessible. Accessibility is a static property that can be determined at compile time; it depends only on types and declaration modifiers.

From Java SE specs

If the member or constructor is declared private, then access is permitted if and only if it occurs within the body of the top level class that encloses the declaration of the member or constructor. A private class member or constructor is accessible only within the body of the top level class that encloses the declaration of the member or constructor. It is not inherited by subclasses.

Hence, it is not possible in Java SE without using Reflection API.

If Reflection API is used, then the static introspection of accessibility is changed to dynamic. Classes are loaded dynamically, binding is done dynamically, and object instances are created dynamically on the fly when they are needed. What has not been very dynamic historically is the ability to manipulate "anonymous" classes. In this context, an anonymous class is one that is loaded or presented to a Java class at run time and whose type was previously unknown to the Java program.

Snippet of class & method access code using Reflection API

Object o = hi.getClass().getMethod("foo").invoke(hi); 
Method m = o.getClass().getMethod("bar"); 
m.setAccessible(true); 
m.invoke(o);

Take an in-depth look at the Java Reflection API.

It is not always necessary to prove that impossible itself says I M Possible. In this case Reflection API has reinvented the wheel for us, we shouldn't be looking to reinvent it all together again from scratch. (on a lighter note, nothing to offense.)

Shishir

like image 98
Shishir Kumar Avatar answered Oct 13 '22 01:10

Shishir Kumar