Add multiplicity declaration to use clause of service consuming module

Stefan Dollase stefan.dollase at rwth-aachen.de
Mon Apr 4 15:54:38 UTC 2016


Hello EG,

the other day, I sent this message to jigsaw-dev. However, I was told to
send it to this list, because it is more appropriate.

Quoting my original message:

Hello,
>
> I read the article in [1], which describes the problem that jlink cannot
> figure out which service providing modules should be included into the
> custom modular run-time image. In [2] there is a statement about this:
> Previously, jlink included all service providing modules. Now, none of them
> is included. Since I am not sure whether this was already discussed, I
> would like to suggest a third approach:
>
> My suggestion is to add a multiplicity declaration to the module-info.java
> file for each uses clause. Here is an example:
>
> module com.socket {
>     uses one com.socket.spi.NetworkSocketProvider;
> }
>
> This declares that the module `com.socket` requires exactly one
> implementation of the service `com.socket.spi.NetworkSocketProvider`. Here
> are further suggestions for multiplicity keywords:
> * optional     - requires either zero or exactly one implementation
> * one          - requires exactly one implementation
> * at least one - requires at least one implementation (this needs a better
> keyword)
> * any          - any number of implementations is sufficient, even zero
>
> I suggest to require a multiplicity declaration, so the intent is always
> clear. However, it will also work to choose a default when no explicit
> declaration is given. Currently, any is the default.
>
> Adding a multiplicity declaration to the uses clause of the service
> consuming module allows to nicely solve the problem that is described in
> the article. Now, the jlink tool can check whether the multiplicity
> declaration is fulfilled. Then, it can include only the required service
> providing modules into the custom modular run-time image.
>
> Besides this declaration, one might add convenience methods to the
> ServiceLoader class. Currently, there is only ServiceLoader::iterator. I
> suggest to add the following methods:
> * loadOptional()   - checks that there is exactly zero or one
> implementation and returns an Optional<> containing a new instance of the
> implementation, if any, throws a RuntimeException or Error if there is not
> exactly zero or one implementation
> * loadOne()        - checks that there is exactly one implementation
> available and returns an instance of the implementation, throws a
> RuntimeException or Error if there is not exactly one implementation
> * loadAtLeastOne() - checks that there is at least one implementation
> available and returns a Set of instances of the implementations, throws a
> RuntimeException or Error if there is not at least one implementation
> * loadAny()        - returns a Set of instances of the available
> implementations
>
> I think these methods should throw an Error, since the condition can and
> should be checked at compile-time. Thus, it is a fatal error if the
> constraint is not met at run-time.
>
> Finally, it might be helpful to be able to explicitly declare which
> service implementations are provided to which service consumers. For
> example, given a service `S` with the implementations `S1`, `S2` and `S3`
> provided by the modules `M1`, `M2` and `M3`. `S` is used by the modules
> `SC1` and `SC2`. `SC1` requires exactly one implementation of `S` while
> `SC2` requires any number of implementations. Now, it is unclear which
> implementation should be used by which service consuming module. Currently,
> the service consuming module will decide this manually in the code by
> iterating over all provided implementations. I think this should be
> explicitly declared. For example, one might declare that for `SC1`, `S` is
> provided with the implementation `S1`, but for `SC2`, `S` is provided with
> the implementations `S2` and `S3`. The great thing about this explicit
> declaration is, that tools like jlink can use this information, so it
> becomes even more clear which modules are required at run-time. Provided
> that `SC1` and `SC2` are included in the custom modular run-time image, the
> service configuration given above implies that also `M1`, `M2` and `M3`
> have to be included in the custom modular run-time image. If the service
> configuration is changed so for `SC2`, `S` is only provided with the
> implementation `S1`, then only `M1` has to be included into the custom
> modular run-time image, but `M2` and `M3` can be omitted.
>
> Also, my suggestion is that this is an interpreted document instead of a
> compiled one, so it can easily be changed after a release. For example, if
> an application uses a logging service it would be helpful to be able to add
> a custom logging service implementation to the module path and simply
> change the implementation which is used by the application by changing the
> service configuration file. This also implies that there should be a tool
> to validate the service configuration via the command line.
>
> Here is a summary of the proposed changes:
> * add a multiplicity declaration to the uses clause
> * use the multiplicity declaration to validate the current configuration
> of service consumers and service providers
> * include all required service provider modules when executing jlink
> * add convenience methods to the ServiceLoader class which match the
> introduced multiplicity declarations
> * add an (interpreted) document to explicitly declare the service
> configuration
> * add a command-line tool to validate the service configuration
>
> I am happy to hear any feedback. If you have trouble understanding my
> suggestions, please don't hesitate to ask for clarification.
>
> Again, I am not sure whether this topic was already discussed. I was
> unable to find it. If it was, please feel free to ignore this mail and send
> me a link to the discussion.
>
> [1]: https://blog.codecentric.de/en/2016/01/java9-jigsaw-missing-piece/
> [2]: https://twitter.com/mreinhold/status/665122968851382273
>
> Regards


Quoting the answer by Alan Bateman:

 On 26/03/2016 02:45, Stefan Dollase wrote:
>
>> Hello,
>>
>> I read the article in [1], which describes the problem that jlink cannot
>> figure out which service providing modules should be included into the
>> custom modular run-time image. In [2] there is a statement about this:
>> Previously, jlink included all service providing modules. Now, none of
>> them
>> is included. Since I am not sure whether this was already discussed, I
>> would like to suggest a third approach:
>>
> JEP 282 [1] lists services as an open issue.
>
> Yes, it was the case, at least for a brief period, that service providers
> were linked in automatically. It immediately leads to calls for ways to
> keep providers out and it gets very complicated once you have a
> command-line that it try to add and exclude modules at the same time.
>
> So as things stand, jlink is an advanced tool and you do need to know
> about service providers when creating a custom runtime image. If you want
> specific crypto support or a specific dynamic language then you need
> specify the names of the modules to -addmods when creating the runtime
> image.
>
> On extending the `uses` clause then this may be something to send to
> jpms-spec-comments for consideration if you feel it is important. One thing
> to be aware of is that the early exploratory phase of Project Jigsaw had
> `requires service S` and `requires optional service S`. The former could
> cause resolution to fail if there weren't any modules providing
> implementations of S. The latter is more like what we have today with
> `uses`.
>
> At least for usages in the JDK, then we almost always used the latter in
> the prototypes at the time. One reason is the consumer modules often have a
> default implementation to use when there aren't any implementations
> deployed. The java.xml module has a default implementation of each of the
> XML parser types for example. There are a few cases, like the
> java.scripting or java.naming modules where there isn't any notion of the
> default implementation but it's not an error in either case to link or run
> without any service providers. The reason is that service provider modules
> may be loaded at runtime, maybe into module layers that a container creates.
>
> -Alan
>
> [1] http://openjdk.java.net/jeps/282


As I said before, I am happy to hear any feedback and provide
clarifications. Also, if there was already a discussion about the topic,
please point me in the right direction. Thanks.

Regards
Stefan Dollase



More information about the jpms-spec-comments mailing list