On 12/03/2015 06:19 PM, [email protected] wrote:
2015/12/3 1:36 -0800, Rafael Winterhalter <[email protected]>:
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
reflection.

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
manager.

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.

Excellent.
--
- DML

Reply via email to