The critical missing pieces and a path forward

David M. Lloyd david.lloyd at
Tue May 9 13:19:20 UTC 2017

On 05/08/2017 09:57 AM, mark.reinhold at wrote:
> 2017/5/5 11:15:47 -0700, david.lloyd at
>> Fellow experts,
>> We and other EC members and community members have, on several
>> occasions, communicated the various deficiencies we see in the
>> specification.  This is an update to make sure that it is very clear
>> which few technical, objective criteria are being missed by the current
>> specification that actually pushes it over the line to unacceptability
>> to Red Hat in its current form.  I can't speak for other EG or EC
>> members, but I believe this accurately summarizes our position.
> Thank you for the summary.  We've discussed all of these issues before.
> Since then I've seen no new information that persuades me to change my
> views on them, but I'll summarize those views here for the sake of the
> public record.
> First, however, a broader point.
> Part of our collective role here is to act as responsible stewards of
> the Java Platform.  If we are to take that at all seriously then we must
> think as carefully as we can about every change or addition that we make.
> Every change to existing functionality carries compatibility risk.  Every
> addition places hard constraints upon how the platform can evolve in the
> future.  That a change or addition appears useful to some is just the
> start of what must be a thorough analysis of its wider and long-term
> consequences.  That a change or addition is "quite easy" to implement
> is completely irrelevant.
> History bears witness to this.  In the early years of Java we sometimes
> chose to expose low-level functionality, or violate fundamental language
> invariants, or leave APIs open for extension without having designed them
> for that purpose.  In each case it was convenient and expedient and, yes,
> "quite easy", but twenty years later we're still paying for many of those
> decisions.
> To cite just one example, consider the fact that `final` does not really
> mean "final", which I've mentioned before [1].  The core reflection API
> introduced in Java 1.1 brought the ability to change the value of `final`
> fields.  This seemed useful, and implementing it was "quite easy" at the
> time, and it did not have any obvious impact on the rest of the system.
> In the long run, however, it has greatly complicated if not prevented JIT
> compiler optimizations based upon or enabled by constant folding -- which
> is an awful lot of them.
> In the context of a library or an application the consequences of any
> particular design choice are often apparent, and it's often possible to
> correct a bad choice over time.  We're working here, however, on the Java
> Platform itself.  Millions of developers and users and customers depend
> upon it, every day.  In that context the consequences of any particular
> design choice are often difficult to predict -- and bad choices are
> sometimes impossible to correct.
> If responsible stewardship means anything then it means that we must
> reject any proposal that merely appears useful to some and is "quite
> easy" to implement.
> To act otherwise would be the height of irresponsibility.
> Now, on to your criteria.
>> The first criterion is to allow cycles to exist among modules at run
>> time.  Our experience with deploying graphs which include third-party
>> modules leads me to believe that this is going to be real problem that
>> people encounter, which will be surprising and (in some cases) very
>> difficult to fix or work around.
>  From previous conversations my understanding is that your experience is
> based upon deploying graphs of existing unchanged non-modular JAR files,
> with all of their messy flaws, in a Java EE application server.  From a
> purely technical perspective, it's impressive that you can do that.  If
> we were aiming in this JSR only to describe and standardize that practice
> then I would agree that cycles should be allowed.
> A primary goal of this JSR is, however, to create a module system that's
> approachable by all developers -- not just developers of application
> servers -- for use in the construction, maintenance, and deployment of
> libraries and large applications.  This demands that we think more
> broadly about how to help all developers write programs "in the large".
> One result of that thinking is that this module system imposes some
> constraints that are intended to make working systems easier to reason
> about, and to guide developers toward better practices.  Adapting to
> these constraints will sometimes require changes to existing code, but
> we've assumed from the start that modularization is an active process
> undertaken by the maintainer of a component rather than something done
> after-the-fact by someone else who cannot modify the component.
> Forbidding cyclic module relationships is one of these new constraints.
> That such relationships are generally a bad idea is not just my personal
> view; see, e.g., Kirk Knoernschild's compelling arguments against them
> in his fine book, _Java Application Architecture_ [2], specifically in
> §4.4, "Cyclic Dependencies -- The Death Knell".

I think this argument is deliberately missing the point.  Today, it is 
relatively difficult to successfully build a piece of Java code which 
has cyclic build dependencies.  But when you're talking about a 
potentially large graph, a cyclic dependency is not always a design 
choice: sometimes, it's an accident of assembly.  A harmless accident, 
which should not be punished.

> As I wrote previously [3], if real experience with this module system as
> used directly by developers reveals that cycles should be allowed then in
> a future release we can revise the specification to allow them.  If we do
> so now, however, then we can never go back on that choice.

Conceptually speaking, why would we ever need back out on the choice? 
The only possible presentation of this problem is that all module builds 
had succeeded, but a run-time cycle is unexpectedly introduced when the 
final application is assembled.  There is no design principle being 
violated in this case; it's a far more obscure happening.  To enforce at 
this level doesn't really help users in any way.  If they hit this error 
at run time - maybe even during final or near-final testing - now our 
users must potentially go back to the design stage to completely 
refactor one or more modules.

Think of, say, an enterprise environment where different components are 
developed by different teams, and they later decide over time that each 
must use APIs from one another, for various reasons.  They should not 
have to fight over which group must refactor their module to split API 
and implementation.  Sometimes, particularly in an enterprise 
environment, such a split just doesn't make any sense.

If a module has no cycles at build, the run time should always accept it 
without error.  I think this makes far more sense as an ideology than 
the idea that modules must have no cycles at build time (where design 
has an impact) and also at run time (where design takes a back seat to 
assembly and deployment).

I think we have concrete reasons to allow cycles, and abstract reasons 
to disallow them.  And maybe I'm wrong and this kind of thing just won't 
happen with Java 9 modules; but if it does, it could potentially cause 
an unreasonably disproportionate amount of pain in comparison to the 
(AFAICT zero) benefit of enforcing what can only be described as an 
arbitrary restriction.


More information about the jpms-spec-observers mailing list