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.) > 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.) > 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. > 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. > 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. - Mark
