#CyclicDependences should be re-opened

David M. Lloyd david.lloyd at redhat.com
Mon Feb 27 13:43:25 UTC 2017


I propose that the #CyclicDependences issue should be re-opened.

This issue is primarily about modules which exist on the module path in 
a regular user application.  We have observed that it is quite possible 
for applications (even simpler ones) to require circularity in common 
cases.  It has been posited that ideologically, circularity is an 
anti-pattern.  However I find this position to be meritless, and 
experience shows that in practice, this position causes more problems 
than it solves.

The notion that all modules in a cycle are logically one is also flawed. 
  It makes the assumption that all the modules were developed by the 
same person, at the same time, which is rarely if ever the case. 
Modern, real world applications, even relatively small ones, now consist 
of dozens or hundreds of distinct pieces from dozens of sources.  Maven 
is a big part of this: by allowing application dependencies to be 
managed automatically, the friction of doing so has been greatly 
reduced, and I've correspondingly observed that including substantial 
dependency graphs in an application as a common practice has increased 
apace.

Maven forbids circularity during compilation.  This circularity 
prevention extends to cycles in the artifact graph being built, and 
building does not necessarily require the full graph to be present. 
There is nothing today preventing cycles at run time, and many, many 
users are taking advantage of this, either knowingly or unknowingly 
every time they use their favorite containers and frameworks.

Anecdotally, it took us fewer than four months from our initial JBoss 
Modules prototype in 2010 to discover that without circularity support, 
we were going to have a very hard time assembling even a basic 
application module set let alone our entire application server.

The primary JPMS remedy for circularity is services.  In some cases this 
can work, but there are limitations that prevent this from being 
sufficient as a general solution, particularly for existing cases.

The problems come in multiple forms.  We encountered the following two 
before we abandoned the idea of restricting circularity as a mistake:

1. A module is compiled against an API (often a spec API); at run time, 
a different API module is in use which consumes common code which in 
turn comsumes the module being compiled.  From the user perspective, 
they are not even aware that they have introduced a cycle, and will be 
surprised and often baffled by the problem at run time.  This is the 
most common case, and services cannot provide a complete solution, 
particularly with existing code.  Even if they could, avoiding cycles in 
this sort of situation can require extreme care and planning.

2. An API module is designed to be linked statically against a flexible 
implementation, by providing an ABI which must be satisfied.  The 
implementation must consume the API in turn; without circularity, the 
two must be joined as one module.  Even if somehow they did not, it's 
typical for implementations to consume libraries that in turn require 
the original API module.  This is the case with slf4j for example, which 
is generally regarded to be a better solution than its commons-logging 
predecessor which did things in an arguably more JPMS-friendly way, yet 
with critical disadvantages.

Neither of these cases constitutes a poor development practice, and in 
fact they often afford quite a lot of flexibility of the type which Java 
has previously excelled at.

It is possible for containers to enable circularity in a limited context 
by bypassing the JPMS resolver.  This is not a good solution, it is 
merely the minimum solution.  It leaves non-container users in the cold 
and will lead to stressful late nights for developers who are trying to 
assemble applications but are foiled by a restriction that has no 
benefit to them and that they may not even understand.  And, 
in-container modules which can manage circularity will, when examining 
their own module's descriptor, see incorrect information as at least one 
module in the graph necessarily must not explicitly reference its 
dependency (as a practical matter, it is difficult to come up with a 
system wherein only some modules are not expressed so the simple 
solution is to express none instead).  A value-added feature is one 
thing, but requiring a container for basic common-sense functionality 
(incomplete as it would be in this case) is too much pain to inflict on 
users for a flawed ideology used to justify an implementation choice.

I think that cutting off these use cases as an anti-pattern is poorly 
justified and is not in the best interests of the community or the 
expert group.

-- 
- DML


More information about the jpms-spec-experts mailing list