Preemptively rejected issue: Modules, ClassLoaders, and compatibility
David M. Lloyd
david.lloyd at redhat.com
Mon Feb 27 13:44:30 UTC 2017
Since the early days of this JSR, in fact since 2012 (if not earlier),
long before it was officially formed (which wasn't until the end of
2014), the design decision in Jigsaw of creating a new concept for
encapsulation has been the subject of friction and something we've been
fighting with both technically and politically. We still hold that this
decision was in error, and that it is becoming more apparent as time
wears on and more problems arise with the fundamental architecture of
The justification of this design decision was chiefly made around
compatibility: having the same types and arrangements of class loaders
was something that was assumed that many applications would count on.
Splitting the JDK means that some modules that used to have a "null"
class loader now no longer would. JDK modules which had elevated
privileges would lose them, resulting in breakage and other problems.
Over time, more new rules were introduced regarding reflection, service
loader, and other behaviors. These rules have broken compatibility in
many ways which are, by any evidence, far more widespread than those
which would result from changing the class loader arrangement of the
core platform or even class path applications.
Since that time, a laudable initiative has been undertaken to
de-privilege the core platform, module by module, in the interest of
security. This change inherently breaks the previously established
compatibility criteria in a much more direct manner; each of these
modules has a class loader value that it did not have before. The
logical conclusion of this effort is in fact that every module would be
de-privileged up to, but not including, java.base. Any program
depending on null class loader values would be broken; any JDK code
relying on this as a security check would need to be updated.
I believe that it was also suggested that forward compatibility for Java
EE containers which rely on a special class loader structure was a
justification for this as well. However, other issues we have raised in
the name of meeting what we view as the bare minimum for our Java EE
container's compatibility have been closed, which would invalidate this
justification as far as I can tell.
Several existing issues, resolved and unresolved, arise solely as a
consequence of this design choice, including #MultipleModuleVersions and
#ConcealedPackageConflicts, both of which are still open. Neither of
these issues would necessarily exist had this choice been made; on the
contrary, the question would have been about supporting such
restrictions as a matter of policy, rather than as a consequence of the
implementation. I don't think I am overstepping when I suggest that in
such a hypothetical case, most developers would express that such
restrictions should not be added, or that they should only apply on an
opt-in basis, intuitively seeing the necessity of such things. In
addition, there would have been no need to have disparate behavior
between named and unnamed modules, as this division has clearly arisen
as an implementation artifact, not a first principle of design (who
would imagine such a thing themselves?). Each of these things
represents a cognitive friction between existing and future code.
Millions upon millions of lines of code of Java exist. In fact it's
probably not even countable anymore at this point. By introducing a
separated abstraction for the module concept when a largely equivalent
concept already exists in the form of class loading, every single one of
these things that relies on traditional class loading behavior is
needlessly compromised in ways that seem hard to predict and harder to
justify. A layer of complexity is added wherein there are now yet more
different ways in which classes and resources can be found and resolved,
with more rules layered on top, when the old rules were already
sufficient as they were. The Java community deserves a simple, clean
design that grows intuitively from what they are familiar with.
Allowing "old" code and "new" code to coexist is not a good substitute
when we could have easily had a new system wherein the vast majority of
"old" code would have been able to function without change, even were it
using service loading, class loading, and reflection.
Our opposition to this design choice persists, despite the preemptive
dismissal of our original and subsequent objections to it. At least one
issue that was just recently closed, #LayerPrimitives, existed as part
of a mitigation strategy to cope with this problem.
More information about the jpms-spec-experts