The proposal is to resolve module-->packages at *build* time, not
deploy. This way, the mapping is guaranteed to be valid.
And yes, this does make the sysadmin job a bit more difficult, but that
should be relatively easy to address with tooling.
The main point is that relying on import-by-module at runtime guarantees
that refactoring is problematic. My proposal seems like a reasonable way
to deal with this, but I wanted to start this discussion to explore
others as well.
Any thoughts on how to solve this problem?
// Bryan
Adrian Brock wrote:
Refactoring:
Aren't there cases where resolving a module import
into its packages during deployment into the repository
will break with some refactorings?
I'm not talking about the more stable module refactorings
where modules are broken into other modules (which
you seem to be addressing).
This is more development time practices where somebody
is refactoring module contents. i.e. the importing
module could end up with a "stale" view of what it
should be importing.
A not very good example would be my module B imports a module A
where A exports com.acme.a.interfaces and
com.acme.a.implementation
I don't use com.acme.a.implementation explicitly so it
shouldn't really be one of my constraints.
Somebody then refactors module B such that the
implementation is now in com.acme.a.impl
but my module wants to import the now non-existant
(or at least stale) com.acme.a.implementation.
Overrides in the repository:
I know we avoid these discussions on the list since it
is really a tooling issue.
But it is a lot easier for a sysadmin to change
a single version constraint for a module import
than it is to have to figure out
what package versions they need to change on the
package imports and change each one individually.
Basically, the convenience of import-by-module
extends beyond the development/source code phase.
OSGi/291
There's still going to be a kind of module import
somewhere to handle OSGi's bundle import constraint.
i.e. OSGi bundle B issues a request to import bundle A
which is really a 277 module.
On Tue, 2008-06-10 at 18:27 -0700, Bryan Atsatt wrote:
(FYI: I've discussed this with Stanley but wanted to ensure it was
visible to everyone.)
Proposal
1. Eliminate runtime use of import-by-module in the JAM system:
a. Support the (very convenient) import-by-module at the source level.
b. jam tool transforms import-by-module to a list of
import-by-package statements.
2. Add APIs to fully support import-by-package in the JAM system:
a. Support Version annotation in package-info.java (or in
module-info.java). If a
package does not declare a version, it "inherits" that of the
enclosing module.
b. Add ImportPackage annotation with version constraints.
3. Add APIs to fully support import-by-package in the abstract framework:
a. Add methods to Query to produce import-by-package nodes.
b. Replace Query.getIndexableNames() with fully generic variants (I
proposed
a solution here previously which I will re-post).
Rationale
Module refactoring is inevitable, particularly during the transition
from the current, effectively flat class space to a fine-grained space
provided by module systems. We have significant experience with this
issue at Oracle (with the transition to our own module system), and OSGi
best-practices for conversion include starting with everything in one
bundle and then separating out pieces as experience is gained.
A very common pattern, in our experience, is for developers to start
with many extra jars in their initial module (a mini version of
class-path hell). As that module is put into wider use, someone
discovers that package X is also contained in their module, and that
duplication either leads to runtime conflicts (very bad), or just plain
footprint bloat. The obvious answer is to put package X in a separate
module, and have everyone share it via imports.
But... not so fast. If there are consumers of that module who import it
by module name alone, then pulling X out of it will cause those
importers to break. And if it is possible for your module to have been
imported by *anyone* by name alone, then you are stuck: either you break
them or you live with the incorrect granularity (which just isn't an
option in the conflict scenarios). Not a happy choice.
Originally, I had proposed to do away with import-by-module altogether,
both to avoid this problem and to eliminate the conceptual disconnect.
Your code does not today contain import statements that name *jars*, it
names packages and/or specific classes in those packages. Why invent a
new system that takes such a large step backwards?
The answer is simply convenience. Imagine a module that contains 100
packages and it is obvious that writing a single import statement is far
easier than discovering and explicitly writing all the package imports.
Yes, IDEs will likely mostly eliminate this issue, but it still makes
sense to be able to do this by hand.
This proposal is an attempt to maintain the convenience while adding the
crucial ability to safely refactor: step 1b is the central idea.
// Bryan