I have a class Formula, located in package javaapplication4, which I load with a URLClassLoader. However, when I call it from another class Test1, located in the same package, I can’t access its methods that have a default access modifier (I can access public methods).
I get the following exception:
java.lang.IllegalAccessException: Class javaapplication4.Test1 can not access a member of class javaapplication4.Formula with modifiers “”
How can I access package-private methods of a class loaded at runtime from the same package?
I suppose it is a problem with using a different class loader, but not sure why (I have set the parent of the URLClassLoader).
SSCCE reproducing the issue (Windows paths) – I suppose the issue is in the loadClass method:
public class Test1 {
private static final Path TEMP_PATH = Paths.get("C:/temp/");
public static void main(String[] args) throws Exception {
String thisPackage = Test1.class.getPackage().getName();
String className = thisPackage + ".Formula"; //javaapplication4.Formula
String body = "package " + thisPackage + "; "
+ "public class Formula { "
+ " double calculateFails() { "
+ " return 123; "
+ " } "
+ " public double calculate() {"
+ " return 123; "
+ " } "
+ "} ";
compile(className, body, TEMP_PATH);
Class<?> formulaClass = loadClass(className, TEMP_PATH);
Method calculate = formulaClass.getDeclaredMethod("calculate");
double value = (double) calculate.invoke(formulaClass.newInstance());
//next line prints 123
System.out.println("value = " + value);
Method calculateFails = formulaClass.getDeclaredMethod("calculateFails");
//next line throws exception:
double valueFails = (double) calculateFails.invoke(formulaClass.newInstance());
System.out.println("valueFails = " + valueFails);
}
private static Class<?> loadClass(String className, Path path) throws Exception {
URLClassLoader loader = new URLClassLoader(new URL[]{path.toUri().toURL()}, Test1.class.getClassLoader());
return loader.loadClass(className);
}
private static void compile(String className, String body, Path path) throws Exception {
List<JavaSourceFromString> sourceCode = Arrays.asList(new JavaSourceFromString(className, body));
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(path.toFile()));
boolean ok = compiler.getTask(null, fileManager, null, null, null, sourceCode).call();
System.out.println("compilation ok = " + ok);
}
public static class JavaSourceFromString extends SimpleJavaFileObject {
final String code;
JavaSourceFromString(String name, String code) {
super(URI.create("string:///" + name.replace('.', '/') + JavaFileObject.Kind.SOURCE.extension),
JavaFileObject.Kind.SOURCE);
this.code = code;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
}
}
A class at runtime is identified by both its fully qualified name and its ClassLoader.
For example, when you test two
Class<T>objects for equality, if they have the same canonical name but were loaded from different ClassLoaders, they won’t be equal.For two classes to belong to the same package (and in turn being able to access package-private methods), they need to be loaded from the same ClassLoader too, which is not the case here. In fact
Test1is loaded by the system classloader, while the Formula is loaded by the URLClassLoader created insideloadClass().If you specify a parent loader for your URLClassLoader in order to make it load
Test1, still two different loaders are used (you can check it by asserting loaders equality).I don’t think you can make the
Formulaclass loaded by the sameTest1ClassLoader (you’d have to use a well-known path and put it on the CLASSPATH), but I found a way to do the opposite: loading another instance ofTest1in the ClassLoader used for loading the formula. This is the layout in pseudocode:Here is the pastebin. A couple of notes: I specifed a
nullparent for the URLClassLoader to avoid the issue listed above, and I manipulated strings to achieve the purpose – but don’t know how robust this approach can be in other deployment scenarios. Also, the URLCLassLoader I used only searches in two directories to find class definitions, not all the entries listed in the CLASSPATH