Coupling modules and class loaders
David M. Lloyd
david.lloyd at redhat.com
Tue Mar 1 23:56:00 UTC 2016
Still awaiting a reply on the following. I still maintain that the
module API and class loader decoupling is a design issue.
On 12/17/2015 12:20 PM, David M. Lloyd wrote:
> On 12/17/2015 10:26 AM, mark.reinhold at oracle.com wrote:
>> 2015/12/15 9:50 -0800, david.lloyd at redhat.com:
>>> On 12/14/2015 01:44 PM, mark.reinhold at oracle.com wrote:
>>>> ...
>>>>
>>>> What I'm thinking of is an existing application with its own
>>>> non-trivial
>>>> class-loader architecture which, due to compatibility constraints,
>>>> cannot
>>>> be changed. If at least one of its class loaders loads classes from
>>>> logically-distinct components that are later converted into
>>>> corresponding
>>>> modules then upgrading the application to load those components as
>>>> modules would, under your approach, require replacing that one class
>>>> loader with many class loaders, which would break compatibility.
>>>>
>>>> Imagine, e.g., a Java EE application server that loads
>>>> implementations of
>>>> all the standard EE components using one class loader. Imagine further
>>>> that existing users (for good or for ill) depend upon that. If a
>>>> future
>>>> version of Java EE defines those components as modules then the only
>>>> way
>>>> to arrange for this app server to load them as such would be to change
>>>> its class-loader architecture in a way that would break existing uses.
>>>>
>>>> (This is, roughly, the situation with Oracle's WebLogic app server, as
>>>> I've mentioned previously.)
>>>
>>> With a multi-class-loader arrangement, it should be possible to emulate
>>> any single-class-loader arrangement, in two ways and with one caveat
>>> that I know of:
>>>
>>> * You can create modules which provide "cut down" views of another
>>> module, such that the module appears as multiple modules but with one
>>> /defining/ class loader for all classes actually loaded from them;
>>
>> I think you mean one defining loader, not one initiating loader. If
>> loader M actually loads a module, and loaders X and Y define "cut down"
>> views of M by delegating to M, then all three loaders can be initiating
>> loaders of classes in M but only M will be the defining loader of such
>> classes.
>
> You're right, I used the wrong term, I don't know what I was thinking
> about. Sorry about that, I've fixed the text to hopefully reduce
> confusion...
>
>>> * You can create a module which aggregates multiple modules into one, as
>>> long as no code relies on the the specific relationship between the
>>> /defining/ class loaders of classes within the module (as the /defining/
>>> class loader will reflect the module in which the class was defined;
>>> this is the caveat I was referring to above).
>>
>> Again, I think you mean defining loader. If loaders M1 and M2 load
>> modules, and loader X aggregates those into a single apparent module,
>> then M1 and M2 will be defining loaders and X will be an initiating
>> loader.
>
> Fixed...
>
>>
>>> In the JDK itself, there's the special case of the null return of
>>> Class.getClassLoader() being used for legacy security checks, for code
>>> that depends on this, though there are multiple ways that I know of in
>>> which this could potentially be mitigated, if this is a problem that you
>>> are thinking of and you want to discuss it.
>>>
>>> What do you think about these options in terms of WebLogic? Are there
>>> additional cases that cannot be covered by fixes in these two areas?
>>> Are there other compatibility cases beyond these that you have in mind
>>> where using multiple class loaders for either platform components or for
>>> Java EE specification modules could break existing behaviors, or does
>>> the combination of the change of /defining/ class loader and behavior of
>>> Class.getClassLoader() summarize the potential issues that you see?
>>
>> To make an approach along these lines work for the JDK, for WebLogic,
>> and for any other application with a non-trivial, non-loader-per-module
>> class-loader architecture, I think you're going to have to fundamentally
>> redefine the meaning of the Class::getClassLoader method.
>
> ...because WebLogic depends on the defining class loader being something
> in particular? Looking at the WebLogic class loading documentation, it
> seems like it is less important what the defining loader is, and more
> important what each (EE) module can "see". In fact there are a number
> of restrictions which actually seem to play in favor of per-classloader
> modules: limited nesting depth, a clear delegation pattern (because
> traditional classloader hierarchy trees are just a subclass of the
> overall directed-graph relationship of modularity), restriction of
> customization to web and EJB modules, call-by-value in certain
> situations, etc.
>
> It would appear to me (just from reading the documentation) that
> WebLogic should already be well-positioned to adopt a
> classloader-oriented modularity strategy for Java EE, unless there is
> some problem that stems directly from having differing defining class
> loaders.
>
>> It would no
>> longer return the defining loader of a class but, rather, an initiating,
>> aggregating loader as you describe above. Given the roles of class
>> loaders in both VM-level access checking and SecurityManager-level
>> permission checking, this is far from being a simple change.
>>
>> To take just one case, suppose a class C is loaded and defined by a
>> module class loader M1, which itself is aggregated together with some
>> other module class loader M2 by an aggregating loader X, so if I have
>> C's Class object then its getClassLoader method will return X. Suppose
>> further that X is an instance of an end-user-supplied subclass of a
>> ClassLoader subclass provided by the application, long ago, as part of
>> its supported API. When one of the defineClass methods of X is invoked
>> to define some new class D, what will be the defining loader of D?
>>
>> If the defining loader is not X, then X must somehow delegate class
>> definition to either M1 or M2, but which one? If D's package is already
>> defined to just one of those loaders then maybe defining it with that
>> loader would (often) be the right thing. What would X do if D's package
>> is split across M1 and M2, or would you disallow that? What would X do
>> if D's package is not yet defined in either M1 or M2, just define D with
>> X? That might work in some cases but not if some code later on depends
>> upon D having been defined by M1 specifically.
>>
>> If the defining loader is X, rather than M1 or M2, then D would have a
>> different defining loader relative to classes now in M1 or M2 than it did
>> when run on pre-9 releases. If D is meant to have package-private access
>> to some class E in the same package, whose defining loader was X but is
>> now M1 due to modularization, then it will fail, since D's run-time
>> package will be different from that of E, since its defining loader is
>> different.
>>
>> (You could consider an even deeper change and say that X is the defining
>> loader, not just an initiating loader, of all classes in both M1 and
>> M2.
>> Module-private types in those modules would then, however, no longer be
>> strongly encapsulated.)
>>
>> So, that's just one case of one family of methods, defineClass, in the
>> existing ClassLoader API. To change the meaning of Class::getClassLoader
>> would require a deep analysis of all the methods in ClassLoader, not just
>> this one. Maybe there's some super-clever way to make them all work in a
>> manner that's both sensible and compatible, but I don't know what that is
>> and I can't believe it'd be straightforward. This kind of complexity is
>> exactly why we abandoned the module-per-loader approach implemented in
>> the first Jigsaw prototype.
>
> Yeah I didn't mean to imply that we should make a fundamental shift in
> what getClassLoader means; however, I would definitely consider the idea
> of making small changes to it to be reasonable, if it were just for
> compatibility with things that use e.g. ClassLoader.getSystemResources()
> or Class.forName(x, y, null) for example, or which use getClassLoader()
> == null for security checks; this kind of behavior could optionally be
> deprecated & removed in the long term.
>
> The thrust of my question was intended to be regarding platform and
> application class loaders. As a hopefully better approach, I'll break
> it into a few (more specific) hypothetical situations which are centered
> around 1:1 modules/classloaders. Some seem reasonable, some seem more
> unlikely but might still be worth exploring for the purposes of thought
> experimentation.
>
> 1. Premise: The Java EE 9 specification establishes a rule wherein each
> Java EE API is required to be available statically to applications by a
> module name which corresponds to the API in use. Existing application
> servers currently present all these APIs as one large unit. Question:
> is it a problem to present each API as a "cut down"/filtered view of one
> module which consists the of all those APIs in the same large unit?
>
> 2. Premise: Same as #1, except the Java EE 9 specification also requires
> that each API be actually encapsulated in a separate module. Question:
> is it a problem that the defining class loader of these classes has
> changed to be per-API rather than a single one?
>
> 3. Premise: Java EE 9 requires that each deployment item presently
> defined in the EE platform spec as a "module" be established as a
> module, while otherwise maintaining the existing neutrality about the
> "types and arrangements" of class loaders (meaning for example that it
> is the app vendor's choice as to whether each transitive Class-Path
> dependency becomes a (potentially shared) module, or whether those
> classes are folded in to the EE module, or some other strategy).
> Question: does this present a compatibility problem that you can foresee?
>
> 4. Premise: Same as #3, however it also introduces a means to determine
> whether other JARs in a deployment should be established as a module, as
> well as a means to establish dependencies to and from such modules.
> Question: does this mitigate or exacerbate any problems from #3?
>
> 5. Premise: Java EE 9 requires that every single archive within every
> application or module be established as a module. Question: how big a
> disaster would this be, and why?
>
--
- DML
More information about the jpms-spec-experts
mailing list