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