lundi 11 mai 2020

Why is this lambda function initiating class loading in the wrong classloader?

I am working on an application that has a plugin framework. It uses a custom classloader to load plugins from encrypted jar files. Initially I painted myself into using the custom classloader as a bootstrapped system classloader. In this way it worked, but there are some drawbacks in using a custom system classloader.

I am trying to rework so that the custom classloader is only utilized to load the plugins. The plugins are hierarchical in nature and therefore need the same class context. To that end, the plugin classloader CustomClassloader is a singleton which extends ClassLoader and has the parent classloader set to the SystemClassloader (and delegates classloading to parent as is the normal pattern).

This seems to be working well EXCEPT in a particular case where I need to create a lambda function that allows the generic ('reflective') setting of a POJO boolean field that is defined within the plugin.

lambda_set creation (defined within an application jar that is loaded by the system classloader):

private BiConsumer<POJO_Interface, Object> lambda_set = null;
Class[] parameter = new Class[1];

parameter[0] = field_clazz; // in this case it is boolean.class
set_method = pojo_class.getMethod(setter.trim(), parameter); // setter method name
set_method.setAccessible(true);

MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle target = lookup.unreflect(set_method);
MethodType func = target.type();
MethodType func1 = func.changeParameterType(0, Object.class);
if(func.parameterCount() >= 2)
    func1 = func1.changeParameterType(1, Object.class);

CallSite site = LambdaMetafactory.metafactory(lookup, "accept", 
    MethodType.methodType(BiConsumer.class), func1, target, func);
MethodHandle factory = site.getTarget();
lambda_set = (BiConsumer) factory.invoke();

When I call lambda_set.accept(pojo, value); I get a ClassNotFoundException for the POJOs superclass. Each POJO extends it's own parent abstract class that implements the POJO_Interface and contains its fields and getters/setters. This same function works fine when everything is loaded from the custom bootstrap classloader. I've verified that it is trying to load the POJO's parent class in the System classloader exclusively instead of the CustomClassloader which is wrong.

I've verified that the pojo.getClass().getClassLoader() == pojo_class.getClassLoader() == CustomClassloader.class However, the lambda_set.getClass().getClassLoader() == jdk.internal.loader.ClassLoaders$AppClassLoader. I'm not sure if this is the problem.

This behavior is the same in JDK8-JDK14.

Is there a way that I can make the lambda_set utilize my CustomClassloader when it needs to load a class? Any other insights would be appreciated!





Aucun commentaire:

Enregistrer un commentaire