Proposal: #IndirectQualifiedReflectiveAccess

Issue summary ------------- #IndirectQualifiedReflectiveAccess --- Provide a means by which a client module can grant qualified reflective access to a framework module that is not known at compile time, assuming that the name of some other module that represents the framework module is known. A canonical example of this case is a client POJO module compiled against a module that defines the JPA API. At run time the client module, or a container or some other code acting on its behalf, must grant qualified reflective access to its POJO packages to the JPA implementation in actual use. [1] Proposal -------- This proposal has three parts. As a running example we assume a JPA client POJO module declared thus: module foo.model { requires java.persistence; opens com.foo.model to java.persistence; } where `java.persistence` is the name of the JPA API module and `com.foo.model` is the package that contains the POJO classes. JPA is an example of an _abstract reflective framework_. Its API is defined in one module but its implementations, of which there can be more than one, are defined in other modules known only at run time. To address this issue we must provide a way for such frameworks, or the containers in which they run, to convey qualified access to the open packages of client modules to the framework implementation modules. * * * First, to support abstract reflective frameworks when used in ordinary Java SE applications, outside of a container environment, we revise the specification of `java.lang.reflect.Module::addOpens` so that if module A has a qualified `opens` of package P to module B, then code in B can invoke this method to open P to any other module. When running outside a container a JPA entity manager is created via one of the `javax.persistence.Persistence::createEntityManagerFactory` methods, which locates and initializes a suitable persistence provider. As part of that process it can use the `addOpens` method on the client module to open the `com.foo.model` package to the provider's module. This will work since the `foo.model` module opens that package to the `java.persistence`' module. * * * Second, to support abstract reflective frameworks when used inside a container, where the container itself rather than the framework module locates and initializes framework implementations, we extend the `Layer.Controller` class proposed for #ReadabilityAddedByLayerCreator [2] with an `addOpens` method: public final static class Controller { ... /** * Open a package in the source module, which must be in this * layer, to the target module, which can be in any layer. */ public Controller addOpens(Module source, String pkg, Module target); } This allows the container to load the client POJO module into a layer, find the packages of that module that are already open to the framework module, and then open those same packages to the framework implementation modules. (The container has the ability to open arbitrary packages to arbitrary modules, of course, but it should use this power with great care.) * * * Third, and finally, to allow framework authors to adopt method handles in preference to, or in place of, the `java.lang.reflect` core reflection API and its `setAccessible` method, we define a single new method in `java.lang.invoke.MethodHandles`: public class MethodHandles { ... Lookup privateLookupIn(Class<?> target, Lookup lookup); } If the given target class, say C, is in package P, and P is either open to the module in which the given lookup object's class is defined or P is in that module, then the result is a new lookup object for C with private access to all members of C. Typical usage in framework code would be: Object entity = ...; long id = ...; Lookup l = MethodHandles.privateLookupIn(entity.getClass(), MethodHandles.lookup()); l.findSetter(entity.getClass(), "id", Long.TYPE).invokeExact(entity, id); A new lookup object must be created for each client class manipulated in this way, but these are inexpensive so this should not be a problem. Notes ----- - This proposal requires maintainers of abstract reflective frameworks, and of container applications, to make modest changes in order to work well in a modular setting. If an existing framework module must be used without change then its client modules must open all of the relevant packages without qualification, or else other arrangements must be made to provide the requisite access. The same is true in the case of an abstract reflective framework that's purely an API, with no code to locate and initialize a suitable implementation, when running outside of a container. [1] http://openjdk.java.net/projects/jigsaw/spec/issues/#IndirectQualifiedReflectiveAccess [2] http://mail.openjdk.java.net/pipermail/jpms-spec-experts/2016-November/000456.html