Still driving off the cliff
David M. Lloyd
david.lloyd at redhat.com
Fri Feb 24 09:26:36 PST 2012
On 02/23/2012 06:16 AM, Alan Bateman wrote:
> On 22/02/2012 20:36, David M. Lloyd wrote:
>> Just to clarify I was talking about the selector provider API, not the
>> filesystem API (I'm old school like that).
> SelectorProvider is a case where the default provider is rarely (or
> ever) replaced but I don't mind using it as an example.
> Like other APIs it defines a way to override the VM-wide provider (via a
> system property in this case). Assuming the override mechanism is not
> used then it uses ServiceLoader in an attempt to find a provider
> implementation. If someone "installs" several implementations into the
> extensions directory then one of them will be used, exactly which one is
> not defined. An alternative would be to put them on the system class
> path, in which case it's more predictable. If ServiceLoader doesn't
> locate any provider implementations there is a fallback/default
> implementation. In the case of the JDK then we typically have a default
> implementation anyway and ServiceLoader is just used for cases where
> folks want to use alternative implementations.
Sure, in many cases there absolutely should be a configured global
default implementation for a given API. JAXP is a great example.
> Now move to the modules world when there isn't a boot or system class
> path or extensions class loader. I've packaged my SelectorProvider
> implementation into a module and I'd like it to be used at runtime
> without using the override mechanism. The way it works currently in
> Jigsaw is that you install the module with the service provider and it
> will be linked as a service supplier to modules that require this
> service. In this case, it's the jdk.base module that requires this
> service. Clearly if someone installs another module that also provides a
> SelectorProvider implementation then it's like installing into the
> extensions directory in the current world; one will be chosen and
> there's a way to override it.
I think it's inaccurate to say "there isn't a boot or system class path
or extensions class loader" - yes that's true *but* there is an
equivalent concept in the form of the initially executed application
module (or in the Java EE case, the currently executing application or
Having the corresponding API module directly import the implementations
it uses is fine, for some cases (especially for specifying default
implementations). However it doesn't suffice for the cases where the
user might (rarely or frequently) want to choose their own implementation.
> In the implementation then the order is
> stable and doesn't change between runs.
This is secondary - it is possible to have stable ordering by many
>> But the modular world *is* class loader centric, that's my whole
>> point. Java is already 80% of the way towards being modular as of Java
>> 7. All it is missing is a multiple-delegate base class loader and a
>> module loader concept which creates class loaders for modules. A 1:1
>> module:class loader mapping is completely natural on many levels, and
>> I have yet to see a compelling argument to the contrary. And in this
>> model ServiceLoader works like a champ.
> I don't get your comment on being 80% there. The work we did in JDK7 was
> mostly on cleaning up dependencies in the implementation in preparation
> for the modularize effort.
I mean that Java has had modules for years. They provide several types
of isolation and encapsulation and are widely used, if not widely
understood - they're called "class loaders". The only thing a robust
module system needs to do is provide a clean way to construct them.
Using the platform's getClassLoader() methods as an excuse to ignore
this existing concept is a major error.
> With Jigsaw then it's usually a 1-1 mapping between module and class
> loaders but we do have complications in the platform and it remains to
> be seen where that goes. In addition I'm not sure where the class loader
> relationship will be specified or not.
The *only* reason I can see that Jigsaw doesn't mandate that a module is
a class loader is a module is so that platform classes can return "null"
to getClassLoader and yet still be called "modules".
This is such a very thin foundation on which to base decisions such as
redefining what a module *is*, and consequently requiring two *modes* of
operation (doesn't that seem really awful to anyone else?). If the
platform *must* be considered a single class loader then it shouldn't be
considered to be multiple modules - more like a single module with some
optionally available "features". With this simple change you don't need
to have a "compatibility mode" to run old-style applications, and you
can consequently allow users to leverage a *huge* body of prior work.
>> My point is that by recognizing that a class loader *already* is a
>> module by another name, you should realize that no new API is needed
>> and no dual-mode situation should even arise.
>> We already have that today. Granted TCCL is considered ugly by some,
>> but all it's really missing is a clear definition of what it *means*.
>> That's what most users (and classically, framework developers) don't
>> get. It *means* your current application module. That's the only
>> meaning that makes any sense. And there's no way that TCCL is going to
>> go away just because you deprecated it and created "new" mode versus
>> "old" mode. It just needs to be formalized. On app startup, set TCCL
>> to the main application module's class loader. In JavaEE, set it to
>> the currently executing deployment. Consistent and simple. And it fits
>> the existing java.util.ServiceLoader like a glove.
> As I said, I think ServiceLoader has to change. The loadInstalled(Class)
> method is specified to use class loaders that don't make sense with
> modules. The load(Class) method is specified to use the
> threadContextClassLoader but what if that has been set to null or
> ClassLoader.getSystemClassLoader (we have many of these in JDK). Same
> thing with load(Class,ClassLoader). For now these methods are using the
> module class loader of the caller. If the caller's module declares that
> it requires the service then it gets back a ServiceLoader to iterate
> over the implementations of that service.
It's not ServiceLoader that really needs to change in this case. The
consumer of ServiceLoader should (and often already does) use the class
loader variant in this case to load the default implementation (if any).
It's the APIs that need to change, not ServiceLoader. The contract
for ServiceLoader is already quite clear and can be leveraged to solve
all modular use cases. But it's up to the API to know whether there is
a default implementation, and how to find it, and whether it is
appropriate to first check the TCCL or caller class loader or whatever.
> On the thread context class loader then my view is that it was a
> mistake. It's been a continuous source of problems, security issues
All it does is denote the current application. It's what APIs do with
that information which is relevant. Pretending that TCCL doesn't exist
is a mistake; better to clean up its contract and document the
behavioral expectation of APIs which utilize it.
I think it's entirely appropriate to use TCCL to search for
implementations in some cases. It's also appropriate to use TCCL for
the purpose of identifying the currently running application in some
cases. The security issues around TCCL can be mitigated with a security
manager, unless you're referring to something specific that I'm not
BTW, somewhat unrelated, if you want to see some platform class loader
related code that completely and utterly fails in a modular environment,
look at how ObjectInputStream resolves classes by default, and imagine
what happens if you have a readObject() from some random module on the
call stack... and ask yourself, is it really more worth retaining weird
and obscure platform behavior than it is to fix a few glitches and as a
result keep well-understood and widely accepted existing concepts?
More information about the jigsaw-dev