The rationale for runtime modularization
David M. Lloyd
david.lloyd at redhat.com
Fri Dec 4 13:13:58 UTC 2015
On 12/03/2015 06:19 PM, mark.reinhold at oracle.com wrote:
> 2015/12/3 1:36 -0800, Rafael Winterhalter <rafael.wth at gmail.com>:
>> I am writing this after looking into Jigsaw for about two months and after
>> running my open-source libraries and several enterprise applications of my
>> current employer using the EA builds.
> Glad to hear it!
>> In the context of these experiences, I wanted to ask for the rationale of
>> project Jigsaw to enforce modularization at runtime.
> Short answer: Strong encapsulation at run time greatly improves both
> security and maintainability. (Strong encapsulation at compile time
> makes it much easier to prepare for strong encapsulation at run time.)
...however, it's also not the only way to do so. I have separately
proposed to the JSR 376 EG (re-proposed, really, as this is not my
original idea) the addition of a new module-private access level for
Java 9+ code (or rather, replacing package-private with this new access
level) which allows existing code to continue to work, and new code to
"phase in" to such a protection scheme.
>> In the software I have searched, reflection is predominantly used for
>> interacting with code that is unknown during compilation. Of course,
>> sometimes reflection is used for abstracting code from types but this is
>> extremely rare as generic types pretty much cover this need.
>> In practice, this implies that any reflective invocation requires an
>> explicit module check. This check can be easily forgotten.
> The failure in this case should be easy to diagnose, and the remedy
> would be to invoke the Module::addReads method. (If the target module
> doesn't export the relevant package then you'll need to arrange for that
> too, either via a command-line flag or possibly by some more convenient
> means yet to be devised.)
The disadvantage here is that under the Jigsaw system, a module cannot
have a class which is available to frameworks (e.g. implements an
interface or is a JavaBean-like or etc.), yet is not a linkable part of
the API of that module. AFAICT, if a framework can see it, you can
compile against it and link against it at run time.
>> Furthermore, the
>> compiler does not remind of missing edges when migrating software either.
>> It seems to me that the assumption for the runtime checks is that
>> invocations might accidentally cross module boundaries.
> The checks performed by the reflection APIs themselves are intended to
> match those performed by the compiler and the VM for non-reflective
> operations, so whether accidental or not a reflective attempt to break
> encapsulation will be caught.
>> From the code that
>> I looked at, I argue that this is already the primary intention when using
> Yes, it's probably more common to use reflection to access types in
> other modules rather than in the same module.
>> One might argue that the same holds for reflection on non-public types.
>> However, in this case I believe that security concerns are the main reason
>> for enforcing accessability where access can be denied by a security
> That concern also applies to public types which are not meant to be used
> in unintended ways -- and there are lots of those, both in the JDK and
> in general.
To clarify though, the Jigsaw approach favors breaking *all* public
classes that are expected to be universally accessible, rather than
changing the few public classes which are not intended to be universally
available (but are due to a lack of a better alternative) to be
non-public, which is the solution I advocate.
>> Modules on the other hand cannot provide additional security as
>> there is always an opt-out for non-modularized code to avoid such barriers.
> Non-modularized code can only break through module barriers if explicit
> permission is given for that, on the command line. So yes, there's an
> opt-out mechanism, but it forces the end user to be aware that the
> system as a whole will run with compromised integrity.
This might more accurately be described as "the same level of integrity
that we've had in Java to date".
>> I am sure this possibility was considered and I wonder why it was not
>> implemented. Looking at the very little practical relevance of heap
>> pollution caused by type-erasure, I believe that compile-time
>> modularization would work well in this case, too. The Java compiler could
>> enforce module boundaries while users of reflection would not be bothered
>> with the boundaries they intended to cross in the first place. As the most
>> important benefit, migration would be much easier. Libraries could add
>> module descriptors to pre-Java-9 bundles without needing to alter code
>> since runtime behavior does not longer depend on the bundling format.
> If strong encapsulation is to mean anything then it must be enforced at
> run time, so a purely compile-time approach is just not viable. (One
> could argue that we already have compile-time modularization today, with
> build tools such as Maven, in which case if compile-time modularization
> were sufficient then we never would've started this project.)
> It's become clear from all the recent feedback that the current design
> might be making life a bit too hard for framework libraries that make
> heavy use of reflection, so as I indicated in a nearby thread we're
> definitely going to look into ways to address that.
More information about the jigsaw-dev