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