Still driving off the cliff

David M. Lloyd david.lloyd at redhat.com
Wed Feb 22 09:39:25 PST 2012


I disagree entirely with this entire line of thinking.  A Java module 
system should NOT be about writing a container which resolves 
dependencies and injects values!  The Java world does not need yet 
another OSGi or yet another IoC container.

And the Java world definitely DOES NOT need a module system that 
requires special tools and indexes in order to install and run anything! 
  What is the precedent in other languages?  Who would possibly want 
this kind of API?  End users just want to be able to say "I need these 
modules".  They don't want to detect service implementations from their 
capabilities, they just want to say "give me this implementation".  And 
they definitely don't want their application blindsided by globally 
installed services!

This reminds me so much of JUL, where a complete API was designed and 
implemented without even talking to anyone who actually has experience 
with logging frameworks.  The end result is an API that nobody uses and 
everyone has to work around.  Guys, *think* about the end user.

The end user wants to be able to install and use modules without any 
special tools required, just like most other languages.  The end user 
wants to be able to overlay their own modules in their home directories, 
just like most other languages.  The end user wants to be able to have 
application-specific module collections for their application, just like 
most other languages.

The user does NOT want to do a lot of configuration to get a module off 
the ground.  The user can *understand* the concept of a class's 
initiating class loader if you *let* them.  Service loading should 
normally be relative to the module that is doing the loading, or the 
application which is being run, *at the user's option*.  Let them 
specify the class loader they want to load relative to.  It works well, 
we've used that technique extensively and people seem to understand it.

We have *never* found an effective and robust use for provides/requires 
capabilities in the real world - there's always a price to be paid which 
could have been avoided using a simpler solution.

Think simplicity, think determinism, think bottom-up design.  We don't 
need more abstractions and rules, we need fewer!  By having each module 
import only the modules it uses (including, if desired, implementation 
modules for services), ServiceLoader continues to work excellently *as 
it is today* with no modifications whatsoever!  In other words, yes 
ServiceLoader handles that just fine as it is today!

Let the *user* decide the scope for searching for service 
implementations.  Even TCCL, much maligned as it is, is still effective 
(arguably a hell of a lot more so) in a simple modular environment, if 
only as an indicator of what application module is being executed.  This 
is completely and perfectly aligned with its use in JavaEE.  Having API 
variants which use TCCL and explicit class loader specification is 
perfectly reasonable and entirely regular.

It's one thing to acknowledge that certain design decisions were 
possibly mistakes.  But it's another to deal with that fact by 
pretending they never happened.  JavaSE as it is right now is not 
perfect but existing infrastructure can be leveraged a lot more than it 
is by Jigsaw, and to much better effect I might add.

I know none of you guys really take JBoss Modules seriously but *please* 
do so.  It is a very realistic, powerful, performant, and effective 
depiction of what an SE module system could be.  It's out there in the 
wild, and it works very well, leveraging existing concepts such as TCCL 
and explicit class loader specification.  No new service loader 
implementations are wanted or needed.  At least, not with a module 
system as its excuse.

Would my ideal SE module system look exactly like JBoss Modules?  No way 
- there are definitely things I would want to do differently in the JDK. 
  But the *concepts* are proven to be powerful and effective.  And those 
critical concepts do not include provides/requires resolution OR global 
module or package registries OR special installation tooling OR any 
language changes whatsoever!  So lets not pretend that *any* of that is 
needed to make a module system that is fast and effective.  In fact you 
can add to the features list "easy to use", "manageable without special 
tooling", and "completely customizable".

You don't have to throw away every existing concept every time you add a 
new one.  It's far better to find what already exists that fits in to 
your new idea - and let me tell you, the more that fits, the better, 
because you'll have happier users who have a better idea of what the 
hell is going on.  This service loader thing is just the latest example 
of you guys running amok with unnecessary new concepts.  Why reinvent 
this particular wheel, when a module system is all you need to make the 
old one work to its fullest potential?

On 02/22/2012 11:14 AM, Jaroslav Tulach wrote:
> Hello David.
>
>> ## 20. 2. 2012 15:06:19 ##<
>> 1) There is only one implementation which is used system-wide (this is
>> your "services as singletons" scenario).
>
> Given your following comment ...
>
>> If a service is indeed a true singleton
>> in every sense (which corresponds to case #1), then the simplest
>> solution is to have the API import its implementation (or at least
>> import its services resources in order to be able to locate it) and use
>> its own class loader to locate it.
>
> ... I don't think this is injectable singleton example. Of course if you have
> a singleton and it is supposed to have just a single implementation, then let
> the API instantiate the implementation using "new". You can even put the
> singleton API and its implementation into the same module. No reason to have
> anything modular, imho.
>
>> 2) There are multiple implementations, however normally only one
>> implementation is selected for the entire application.
>
> This is probably what I call injectable singleton:
> http://wiki.apidesign.org/wiki/Injectable_Singleton#Initialize
>
> In contrast to case #1 it makes sense to use modularity here and separate the
> singleton API into one module and put the implementation into other(s).
>
>> 3) There are multiple implementations, but each module may need to
>> choose a different implementation.
>
>> If a service is typically per-module (case #3) then each module would
>> import (at run time) its preferred implementation and use its own class
>> loader to search for it.
>
> It will be better if the classloading tricks are eliminated as Jesse and Alan
> wrote. Imho it may be sufficient to get all instances in the system and
> somehow select the right one by querying its capabilities (e.g.
> getSupportedXYZ(), etc.).
>
>> 4) There are multiple implementations, however implementations carry
>> semantic meaning and generally must therefore be selected by some
>> identifier.
>
>> Note that this case is unique in
>> that the conceptual service dependency is not just a base type to be
>> implemented, but it also has a "flavor" component to specify behavior
>> characteristics.
>
> Actually NetBeans has an example of this style as well. The editor guards API
> http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-editor-
> guards/overview-summary.html
> benefits from the fact that requires/provides are just string tokens (not a
> class names). One registers the service capable to work for Java and then uses
>
> provides token org.netbeans.api.editor.guards.Java;
>
> Users of the editor guards API wanting to deal with Java then use:
>
> /* guarantees editor guards API is on classpath */
> requires org.netbeans.api.editor.guards;
> /* guarantees the Java implementation will be found using ServiceLoader */
> requires token org.netbeans.api.editor.guards.Java;
>
>> If a service's behavior varies by implementation (case #4), as is the
>> case for cryptographic, digest, and other security APIs (where the
>> algorithm is significant to determining the behavior of the
>> implementation) and several third-party APIs, then the application must
>> import the implementations which it uses
>
> A cryptography module providing SHA implementation would use something like
>
> provides token java.security.MessageDigest.SHA
>
>> All of these solutions have in common that the API to locate the
>> implementation requires some notion of what class loader to search.
>
> I don't think so. You can enumerate all implementations. Sure, it would be
> more efficient to prevent instantiating unneeded classes and select just the
> right one, but that is something ServiceLoader does not handle[1] in general,
> as far as I can tell.
>
> -jt
>
> [1] One of the reasons NetBeans use Lookup which can handle declarative
> services as demonstrated at http://wiki.apidesign.org/wiki/CompileTimeCache
>


-- 
- DML



More information about the jigsaw-dev mailing list