provides and requires static ... runtime error

Rob Bygrave robin.bygrave at gmail.com
Mon Apr 17 23:40:17 UTC 2023


*> Optional dependencies are meant to support annotations and other limited
use cases.*

FWIW to me *maven* *optional true* dependencies map to requires static

  <dependency>

    ...

    *<optional>true</optional>*

  </dependency>


When we are in the mode of translating from *'classpath only'* to*
'classpath + module-path'* support, whenever we see maven optional
dependencies we expect to use requires static.


*> A class that `implements` an interface is not an optional dependency
situation -- loading the class *always* causing loading of the interface,
which will fail, so we make module resolution fail fast to ensure the
loading failure never happens.*

It is an optional dependency for classpath.

In terms of classpath we do not have any fail fast for this, IF something
tries to new up the service implementation AND the p.S interface type is
not in the classpath we get an exception (with the cause being
ClassNotFoundException for p.S). This is expected and all good.

With module-path the *module fail fast resolution  *actually prevents this
pattern / use case that works with classpath from working. Code using this
pattern with classpath will no longer work with module-path due to the
fail fast resolution error. The effect of the fail fast resolution on p.S
is that we have turned an optional dependency (in classpath) into a
non-optional dependency (the p.S interface *MUST* be in the module-path).


To be clear, the pattern with classpath and maven is that:
We have a maven dependency with <optional>true</optional> that has the p.S
interface type. The p.S type exists at compile time so we can compile the
implementation of the service interface but we understand that p.S might
not exist at runtime as we have explicitly stated that the dependency that
has p.S is a maven optional dependency. We have an implementation of p.S
that we expect to be optionally created via ServiceLoader. Yes this
implementation is only expected to be created by ServiceLoader. We see an
exception at runtime if code tries to instantiate the implementation when
the p.S type is not in the classpath and that is expected and all good.

With module-path this now does not work (due to the fail fast on p.S
interface type).

With module-path, IF module resolution was changed to tolerate a `provides`
that specifies a missing service interface, I'd argue that we'd get the
same behaviour that we get with classpath. That is, if this was allowed
then if something tried to create a new instance of the implementation we
would expect the same behaviour that we see in classpath and the same or
similar error would be thrown as what is thrown when running with classpath
(cause ClassNotFoundException for p.S).


Cheers, Rob.



On Tue, 18 Apr 2023 at 02:20, Rob Bygrave <robin.bygrave at gmail.com> wrote:

> *> really a request to allow more usage of `requires static` and
> specifically to allow `provides p.S with ...` *
>
> Yes, that's my case.
>
>
> *> If Foo isn't available, then (so the thinking goes) the `provides` is
> moot, and the implementation's dependence on Foo should be ignored.*
>
> Yes!!.
>
>
> *> However, this desire is not clearly communicated by the implementation
> module expressing `requires static <interface-module>` -- the
> interface-module could export many packages, not just one package
> containing one interface Foo, so the `requires static` is not flagging
> anything about the service interface per se.*
>
> Hmmm, my thinking was going the other direction. With the provides  *p.S*
> uses ...  the p.S is *ONLY* visible to the module at compile time via the
> requires *static *[so at compile time we can infer that the p.S type is
> optional and might not exist at runtime based on the requires *static*].
>
>
> *> Optional dependencies are meant to support annotations and other
> limited use cases.*
>
> Ok ... umm.
>
>
> *> All of this boils down to: if the implementation module says
> `provides`, then is it reasonable to consider that ServiceLoader is the
> *only* vector by which the implementation class will be instantiated? If
> yes, then module resolution should perhaps be tolerant of a `provides` that
> specifies a missing service interface. If no ...*
>
> Yes, I follow this and agree.
>
>
> > *is it reasonable to consider that ServiceLoader is the *only* vector
> by which the implementation class will be instantiated? *
>
> In my view this is expected and imo I get there by thinking in the
> opposite direction from the provides p.S type to the requires static rather
> than the other way around. That is, the p.S type is only available via
> requires static hence it is expected to potentially not exist at runtime in
> the module-path. That is, if p.S was expected to exist at runtime it would
> be "read" via a requires or requires transient clause and not via a
> requires static - *the use of requires static for this case is explicit
> and intentional*.
>
> In using requires static ... imo we are explicitly going out-of-our-way to
> say "the types here might not be available at runtime" and the classic case
> for this as I see it is this case of providing an optional service, that
> will only be service loaded if the user of that service is in the classpath
> / module-path. *IF* the module that is the user of a service is in the
> classpath / module-path then that module will ensure that the p.S type is
> in the module-path.
>
>
> Cheers, Rob.
>
> Would a real world example help?
>
> - avaje-inject has a Plugin interface, and will look to load Plugin
> implementations at runtime.
> - avaje-config is a module that has its own functions (external
> configuration) but can also provide a Plugin for avaje-inject (provide
> external configuration for dependency injection).
> - avaje-jsonb is a module that has its own functions (json marshalling)
> but can also provide a Plugin for avaje-inject (provide default json
> marshalling implementation for dependency injection).
>
>
>
> On Tue, 18 Apr 2023 at 01:23, Alan Bateman <Alan.Bateman at oracle.com>
> wrote:
>
>> On 17/04/2023 14:14, Rob Bygrave wrote:
>>
>> I'm seeing a runtime error I was not expecting.
>>
>>
>> *Error occurred during initialization of boot layer
>> java.lang.module.ResolutionException: Module io.avaje.config does not read
>> a module that exports io.avaje.inject.spi*
>>
>>
>> JDK-8299504 [1] captures a number of comments on this scenario.
>>
>> -Alan
>>
>> [1] https://bugs.openjdk.org/browse/JDK-8299504
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/jigsaw-dev/attachments/20230418/7715c40b/attachment-0001.htm>


More information about the jigsaw-dev mailing list