Alternative mechanism for reflective access control (#ReflectiveAccessToNonExportedTypes / #AwkwardStrongEncapsulation)

David M. Lloyd david.lloyd at redhat.com
Wed Sep 21 16:39:45 UTC 2016


In our internal discussion of the proposal for 
#ReflectiveAccessToNonExportedTypes, we discussed the ins and outs of 
various behaviors and have come up with a few ideas or starting points 
for solutions that we think would be more workable in conjunction with 
existing middleware (ours and others').

For reasons previously explained, we do not think that weak modules are 
a good way forward; I won't go into that again here.  But the logical 
re-starting point is: If not weak modules, then what?

I will boil it down to a few basic requirements that we have 
established.  This list is probably non-exhaustive but hopefully 
complete enough to go on for now:

• A module definition must be able to establish that a dependent has (or 
all modules have) access to one or more (or all) packages for public 
reflection only.
• A module definition must be able to establish that a dependent has (or 
all modules have) access to one or more (or all) packages for public or 
private reflection only.
• A module definition must be able to establish that a dependent has (or 
all modules have) access to one or more (or all) packages for public 
reflection and compilation/linkage (i.e. it's an export by today's 
terminology).
• A module definition must be able to establish that a dependent has (or 
all modules have) access to one or more (or all) packages for public or 
private reflection and compilation/linkage (i.e. it's a "private" export 
by today's terminology).
• As today, any packages not declared in one or more of the above 
categories is inaccessible outside of the module in any way (note that 
as I showed previously we have also concluded that it should continue to 
be impossible to export a package for compilation/linkage without public 
reflection, as we have not discovered any use for such a mode).

More generally:

• The syntax for all of the above has no particular constraint (in fact 
I will try to actively avoid touching what could be a very 
bikeshedding-rich discussion), except that it should not be construable 
as being pejorative against the usage of reflective frameworks; rather, 
it should be clear what level of trust is being established without 
raising undue warning.
• Applications should not need gratuitous amounts of declarations in 
their module(s) in order to utilize frameworks.
• As previously established, it should not be possible for one 
declaration to reduce the scope of access of another declaration in a 
module definition.
• Access to a module (for reflective purposes only) must not cause 
conflicts if multiple such modules which contain identical packages are 
accessible to a single consumer; in other words, reflection-only access 
into non-dependency modules is not bound by duplicate package 
restrictions as long as each package is unique per class loader, as per 
the current (Java 8) class loader rules.

The above cover the useful access modes that we have identified.  This 
is _nearly_ adequate to cover the use cases that we are currently 
concerned about; for example, I could export all packages for public 
reflection only to a specific framework, if only I know the module name 
of the implementation.

Unfortunately, this does not work well in the case where a module may 
consume a framework whose specification is separate from the 
implementation.  An application module may need to use (say) EJB and 
JPA; there is presently no clean way to do so without either (a) relying 
on a container environment to rewrite the descriptor or (b) opening up 
the module and defeating the security mechanism (e.g. "weak").  Without 
either of these workarounds, the application developer must have a good 
deal of knowledge about what modules provide what services within a 
framework-rich environment, possibly resulting in a very verbose (and 
error-prone) descriptor; none of these options is really satisfactory.

Thus, apart from the option of redesigning (to an extent) the security 
mechanism (thereby eliminating the need to seal off access to public 
reflection, which is definitely still an attractive option for various 
reasons from our perspective, but which is also a very different 
discussion), we need some sort of mechanism which decouples the literal 
dependency system from access permission (much like uses/provides does).

For example if I could declare that my module uses "javax.ejb", and, in 
so doing, automatically grants public and private reflective access to 
the module that provides that service, this would be a good outcome.  A 
module which answers to that service name could be responsible for 
reflective access to the application module, providing that information 
privately to any other framework modules which require it.

The migration story looks much better in this light: module descriptors 
still can be quite terse and specific.  Applications which use 
reflective frameworks do not need gratuitous exports; in fact it's much 
more fluid for a user to say "I require these helper libraries; I use 
EJB; that's it" which means they don't have to worry about the details 
of whatever particular environment they run in.  This also has the 
advantage of allowing new Java 9-generation specifications to stipulate 
standard service names for each specification (e.g. "javax.ejb", 
"javax.cdi", that sort of thing).

While this doesn't cover 100% of our remaining issues with Jigsaw (of 
course; we'll all continue moving through the issues list as we have 
been to get us there), meeting these requirements would go a long way 
towards at least having a reflection story that is more practical for 
present-day frameworks to move forward with.  So the last requirement 
would be:

• A module definition must be able to establish that an "indirect" 
dependency exists on an otherwise unknown module providing a capability, 
wherein that module may require public or public+private reflection 
access to some or all packages without compile/link access.  This could 
possibly exist in conjunction with, or as an evolution of, the current 
services mechanism, however a complicating factor is that the current 
mechanism is based specifically on types, whereas a purely symbolic 
relationship might be better for this purpose (this is not a requirement 
though if it can be made to work as-is).  Note that any symbolic 
relationship system would need some in-code discovery mechanism such 
that consumers of the capability are made available to the provider 
and/or vice-versa, in order to make practical use of the relationship.

The following example syntax is meant to be unambiguous and 
illustrative; no specific attempt is made to reuse existing keywords 
(for example), or even to imply an endorsement of the current descriptor 
mechanism at all, but to clarify how this might look in practice and 
provide a practical application of the ideas herein.

Example 1: A contrived provider of the fictional framework 
"javax.fictional.orm" illustrating provides/uses-based access granting

module org.foo.orm.provider {

       // Require a module dependency, and give it private reflection 
access to everything
       requires org.apache.commons.beanutils with private reflection on *;

       // Require a module dependency with no reflection
       requires org.apache.commons.logging;

       // Provide the framework
       provides javax.fictional.orm.ORM
           using private reflection
           with org.foo.orm.provider.ORMImpl1,
                org.foo.orm.provider.ORMImpl2;
}

Example 2: A contrived consumer of #1

module com.mycompany.application {
       uses javax.fictional.orm.ORM; // automatically gives private 
reflection
}

Example 3: Grant reflection access to a couple of packages to a named 
non-dependency module

module com.mycompany.application {
       grant public reflection on
           com.mycompay.application.package1,
           com.mycompay.application.package2
       to org.foo.framework;
}

Example 4: Behave like Java 8

module com.mycompany.application {
       grant private reflection on * to *;
}

Example 5: Behave like Java 8, but restrict private access without 
requiring a security manager

module com.mycompany.application {
       grant public reflection on * to *;
}

Example 6: An example of using CDI and EJB with symbolic capabilities

module com.mycompany.application {
       uses capability javax.ejb, javax.cdi
}

Example 7: An example of providing EJB with symbolic capabilities

module org.foo.ejb.provider {
       [...]
       provides capability javax.ejb using private reflection;
}


--
- DML


More information about the jigsaw-dev mailing list