Mandatory service requirement (was: runtime changes for services)
Jesse Glick
jesse.glick at oracle.com
Thu Feb 16 07:07:16 PST 2012
On 02/16/2012 05:21 AM, Alan Bateman wrote:
> "requires service S" is essentially treated by the resolver as:
>
> requires optional S1;
> requires optional S2;
> requires optional S3;
Consider just dropping mandatory service requirement, i.e. let a module which declares that it requires a particular service just load whatever providers happen to be
available for other reasons, such as because an overarching "application" module required the providing module directly (I suppose like what "requires optional service"
would do).
The NetBeans module system permits modules to declare that they provide and require named tokens, where the token is often by convention a service class name. Initially
it seemed like a good idea for a module which loads a service to declare that it requires that some provider of this service be present. In practice this has proven to be
more harmful than helpful, leading us to start removing these require clauses:
1. The section of a module which loads the service might never actually be called for a given use case. For example, one package might use a service but the application
only uses another package which does not. Or the module might be referenced merely for some utility method.
2. It is generally just as easy to write the module to behave gracefully when no provider is found (and you may need to do this anyway for the benefit of tests). If all
providers of a given service are iterated, an empty loop usually has sane semantics. If just the first one (*) is used, the calling module can often substitute a skeletal
no-op implementation; or at worst throw a documented error or exception.
3. A given application probably does not need every provider of a service to be loaded; there is likely to be a set of known and tested providers. The decoupling at the
module level is beneficial as it makes the modules reusable, and it ought to be easy for an app developer or user to add a module with an extra provider, but mere
presence in the repository should not magically trigger this.
4. Token-based dependencies like this can be difficult to translate into other module or packaging or repository systems.
5. Disjunctive dependencies complicate resolver semantics. In the NetBeans module system if there are several providers of a token and a module requires it, all the
providers which _could_ be loaded _are_ loaded (together with their own dependencies), and then the requirer is loaded so long as at least one provider was as well. This
behavior has been responsible for a lot of bugs and corner cases when combined with other constraints.
6. Token requirements will often lead to cycles in the dependency graph, as a module defining an SPI and matching API declares that it requires some provider of the SPI.
In NetBeans module enablement order follows a topological sort and cycles are forbidden, leading to the introduction of an alternate form of the require clause ("needs")
which does not affect order (and more complexity in the resolver). Jigsaw permits cycles but this ought to be limited to modularization of legacy code; if you ever want
to support things like dynamically unloading modules it is much easier if there are no cycles.
For that matter, you could probably drop the need to declare that a service is used at all: if and when ServiceLoader is called, determine the providers - if not
precomputed in the repository - and load them. Of course you cannot compatibly reverse this decision, since older modules might "unexpectedly" load a service and there is
no way of upgrading them except to grep the sources for calls to ServiceLoader.load(Literal.class) under the assumption that this is the only calling pattern.
(*) Though ServiceLoader has no means of imposing an ordering on services. You can add a method returning a sort key to the service interface itself if you do not mind
eagerly loading all implementations. The NetBeans equivalent (MetaInfServicesLookup) supports a static sort key to avoid this.
More information about the jigsaw-dev
mailing list