Eliminate silent dependencies was: Mandatory service requirement

David M. Lloyd david.lloyd at redhat.com
Mon Feb 20 06:06:19 PST 2012


On 02/20/2012 03:49 AM, Jaroslav Tulach wrote:
> Jesse,
> is your suggestion to force the my.corp.app module to always have a direct
> dependency on explicit implementation of a parser (e.g. saxon)? If so, I
> believe it would be unfortunate.
>
> It may be valid for my.corp.app to select and prefer one implementation of a
> parser. If the whole application is tested with saxon parser, it is perfectly
> OK, to request that the saxon parser is used when my.corp.wsparser needs SAX.
>
> However it is important for jaxp module or my.corp.wsparser module (as a
> reusable units) to properly indicate the need for implementation of a SAX
> parser too. Otherwise others, reusing jaxp or my.corp.wsparser will find out
> only during runtime that the execution environment is insufficient.

I understand this and sympathize however I believe this doesn't actually 
work in reality.  Implementing our latest app server release as a fully 
modular run-time environment has given us some insight into the 
realities of this problem.

You have essentially these scenarios when it comes to implementation 
discovery:

1) There is only one implementation which is used system-wide (this is 
your "services as singletons" scenario).
2) There are multiple implementations, however normally only one 
implementation is selected for the entire application.
3) There are multiple implementations, but each module may need to 
choose a different implementation.
4) There are multiple implementations, however implementations carry 
semantic meaning and generally must therefore be selected by some 
identifier.

The ServiceLoader API (or its moral equivalent) is pretty widely used 
today for all four scenarios.  However I do not believe that there is a 
single declarative construct which adequately solves all these cases. 
Of these scenarios, only #1 is adequately solved by declaring a that a 
module requires some unspecified implementation of a given API.

I think instead that these cases can and should be solved by the 
existing dependency mechanism.  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.

If a service is application-wide (case #2), the API would search the 
boot module's class loader or TCCL (there are pros and cons of each), 
which would express the dependency.  A good example which fits for both 
#1 and #2 is NIO, where multiple implementations (per system) are 
certainly possible but also somewhat unlikely.

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.

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 (granted the security APIs 
already have an alternative global registry based method, however this 
is not an ideal solution for this problem).  In this case, the API would 
generally use the caller's class loader or TCCL to search for an 
implementation with a matching "type".  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.

All of these solutions have in common that the API to locate the 
implementation requires some notion of what class loader to search. 
Based on our experience, the best existing APIs in this regard are those 
which have a preferred search order which includes an API variant which 
uses a default class loader (sometimes TCCL, sometimes the API's own, 
depending on which general case as described above) and a variant which 
accepts an explicit class loader argument to search.  Failing more than 
one API method, the best overall interface is to accept a class loader 
as an argument, as the caller can easily explicitly specify the TCCL or 
the callee's class loader.

-- 
- DML



More information about the jigsaw-dev mailing list