Yes, we can keep tightening the split package scenario to ensure safety without scanning the classpath. First, you have to promise that the JAR on the classpath never has classes that overlap with a module in the system image. Second, the named module of interest in the system image can't be a framework that reflects over classes in the unnamed module (thus sidestepping bugs about "Where did this class in my package come from? It's not my codesource! I can't load it starting from my loader!").

To put it mildly, this isn't what we had in mind for "reliable dependencies".

You also have to consider why the split package occurs at all. Your classpath JAR might simply be adding public types that you feel a module in the image should have had all along. Other people will offer up a classpath JAR that adds public types to access and return package-private content of a module in the image. This won't work for most JDK modules, since they're defined by a different loader than the unnamed module, but it would work for some JDK modules and for all user modules.

To put it mildly, this isn't what we had in mind for "strong encapsulation".

Alex

On 3/9/2016 7:14 PM, Russell Gold wrote:
This should, however, be completely safe in the case when one of
those modules is part of the JDK itself, isn’t it? It is not clear to
me how you could ever get a circular dependency in that case.

In fact, this should be true of any library as well - the only
scenario that I can think of where you could get this mess is if a
developer compiles a bunch of classes, and then deliberately creates
circularity between modules by putting some of those classes into a
named module and some into the unnamed module, which seems incredibly
unlikely.

If there an actual plausible scenario from which you are hoping to
protect developers with this restriction?

- Russ

On Mar 9, 2016, at 8:56 PM, Alex Buckley <alex.buck...@oracle.com>
wrote:

Yes, a ClassCastException could only arise when (sticking with
Paul's scenario) the same class exists in both the named module and
the unnamed module.

It would be "safe" for the module system to allow a package that is
split perfectly between modules: no overlap of classes and no
cyclic references between members. This is checkable when a package
is split between two named modules that the resolver can observe in
great detail. (You don't even need to check no-cyclic-references,
since it's dominated by the no-cyclic-dependencies rule for the
modules -- basically the split has to be perfect for the package to
work at all.)

But in the case of a package split between a named module and the
unnamed module, the check is basically impossible, since the
resolver can't enumerate the A.* types in the unnamed module
without scanning the classpath, which sounds bad.

tl;dr We don't want a lot of gymnastics in the module system to
support a known-bad idiom which most Java developers will never
come across. ("most" means "in the millions".)

Alex

On 3/9/2016 5:16 PM, Russell Gold wrote:
Doesn’t this kind of error only happen when a second module
exports the same _class_? What is the problem with another class
being defined in the same package, given that package B isn’t
going to access that new class at all?

- Russ

On Mar 9, 2016, at 4:37 PM, Alex Buckley
<alex.buck...@oracle.com> wrote:

Presumably you would count the equivalent scenario on JDK 8 --
my package A is in Alex.jar on the classpath and your package A
is in Paul.jar on the classpath -- as a security issue too,
because some of my classes may substitute for yours (or some of
yours for mine, depending on how the classpath is
constructed).

On JDK 9, we do the "substitution" cleanly. Package A is not
split. That avoids one category of error (ClassCastException).
What about poor package B that finds itself accessing a
different package A than it was compiled with? Well, since
package A is exported by a named module, it's reasonable to
assume that the named module "owns" package A [*], and that the
developer of package B co-bundled some version of package A
without renaming it. Dangerous in JDK 8, dangerous in JDK 9.
(We're trying to encapsulate the internals of a module, which
is different from trying to isolate modules from each other.)

[*] Advanced scenario: the named module exporting A is actually
an automatic module which happened to co-bundle package A. By
placing this JAR on the modulepath to form an automatic module,
it dominates the JAR left on the classpath which also
co-bundled package A.

Alex

On 3/9/2016 1:17 PM, Paul Benedict wrote:
But isn't what your proposing a security issue? Let's say my
package A is in the unnamed module and your package A is in
a named module. You basically took over my code; your classes
will be substituted for mine.

Cheers, Paul

On Wed, Mar 9, 2016 at 2:38 PM, Alex Buckley
<alex.buck...@oracle.com <mailto:alex.buck...@oracle.com>>
wrote:

On 3/9/2016 10:36 AM, Paul Benedict wrote:

From the doc: "If a package is defined in both a named module
and the unnamed module then the package in the unnamed module
is ignored. This preserves reliable configuration even in the
face of the chaos of the class path, ensuring that every
module still reads at most one module defining a given
package. If, in our example above, a JAR file on the class
path contains a class file named
com/foo/bar/alpha/AlphaFactory.class then that file will
never be loaded, since the com.foo.bar.alpha package is
exported by the com.foo.bar module."

I would like some clarification. Correct me if wrong, but I
think this entire paragraph is really meant to be about the
perspective from a modularized JAR? If a module has package
A, and the unnamed module has package A, then of course the
module's package A should win.

However, if it is meant to be absolute language, then I
disagree.

The unnamed module should be coherent among itself. If the
unnamed module has package B and relies on classes from
package A, it should still be able to see its own package A.
I don't think modules should be able to impact how the
unnamed module sees itself. That's a surprising situation.


The unnamed module is not a root module during resolution.
If your main class is in the unnamed module (i.e. you did
java -jar MyApp.jar rather than java -m MyApp), then the
module graph is created by resolving various root modules
(what are they? separate discussion) and only then is the
unnamed module hooked up to read every module in the graph.

Hope we're OK so far.

If some named module in the graph exports package A (more
than one module exporting A? separate discussion), then since
the unnamed module reads that named module, the unnamed
module will access A.* types from that named module.

It's hard to imagine the unnamed module NOT accessing A.*
types from that named module. Primarily, we need to avoid a
split package situation where code in the unnamed module
sometimes accesses A.* types from the named module and
sometimes from the unnamed module.

You might say, OK, let code in the unnamed module
exclusively access A.* in the unnamed module rather than
exclusively access A.* in the named module. Then you have two
problems:

1. There are issues for named modules in the same class
loader as the unnamed module -- such named modules MUST get
A.* from the named module rather than the unnamed module, and
the class loading mechanism is incapable of switching based
on accessor. It'll be common for named modules to exist in
the same class loader as the unnamed module, as modular JARs
on the modulepath and non-modular JARs on the classpath all
end up in the application class loader (modular JARs as named
modules; non-modular JARs jointly as the unnamed module).

2. While the module system is sure that package A exists in
the named module, how would the module system possibly know
that package A exists in the unnamed module? Scanning every
class file in every non-modular JAR on the classpath at
startup sounds bad.

Alex




Reply via email to