Proposal: #IndirectQualifiedReflectiveAccess
mark.reinhold at oracle.com
mark.reinhold at oracle.com
Fri Nov 18 16:29:09 UTC 2016
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
More information about the jpms-spec-experts
mailing list