[External] : Re: provides and requires static ... runtime error

Rob Bygrave robin.bygrave at gmail.com
Tue Apr 18 22:00:30 UTC 2023


*> source of the problem:*

I'll have a crack.

As I see it, I think this case is that some modules have "split
personalities" in that they don't exist to provide a service as their *main
goal* but it happens that they can also additionally provide a service IF
they are used in an app configuration that uses and loads implementations
of that service [and that service implementation would be left unused if it
is used in a standalone configuration that does not include the service
interface].

Y (service provider) has a main goal of doing "Y good stuff".
It really does not want a *hard* dependency to X, it can be used by itself
completely standalone but IF it is used in an application configuration
that includes X then ... it additionally happens to provide an
implementation of an X service interface. Some people might call this "An
optionally provided service" that might not be loaded.

e.g.

Module X has an interface x.spi.Plugin and will service load all the
implementations.

Module Y exists to do "Y good stuff", exports y; and can be used completely
by itself standalone. An application configuration that only includes Y is
good and expected.
Module Y can also (as a secondary reason to exist) implement x.spi.Plugin
to provide useful functionality to an application configuration that
includes Y + X.

module y {
  exports y; // Main reason to exist

  // As a secondary reason to exist
  requires static x;
  provides x.spi.Plugin with ... <internal implementation>
}


Application configuration using just Y standalone fails at startup based on
the existence of "provides x.spi.Plugin".



*> if they choose not to use X, Y would just sit there, unused.*

Y still provides its main reason to exist, it's "Y good stuff". What is not
used is that 1 class which is the implementation of x.spi.Plugin (and with
module-path this implementation can be hidden / not exported and not open
to abuse / accidental usage - yay!!).




Fixing a module with a split personality:
-----------------------------------------------
We could split the Y module into 2 modules - Y and Y-X-Plugin. The only
classes in Y-X-Plugin are the implementation of x.spi.Plugin + module-info
with the service loader configuration etc.  Now we have 3 application
configurations to consider:

- Y (now works)
- X + Y (might be confusing for users)
- X + Y + X-Y-Plugin (needs documentation)


What is mooted is to be able to just have the 2 configurations to consider:

- Y (standalone)
- X + Y (y provides its "Y good stuff" + acts as a x.spi.Plugin for X)


The reason why this could be considered acceptable is because the
implementation of x.spi.Plugin that would exist in Y can be hidden / not
exported and not open to abuse / accidental use.


Cheers, Rob.


On Wed, 19 Apr 2023 at 04:05, Ron Pressler <ron.pressler at oracle.com> wrote:

>
>
> On 18 Apr 2023, at 16:01, Josiah Noel <josiahnoel at gmail.com> wrote:
>
> On Tue, Apr 18, 2023 at 8:46 AM Ron Pressler <ron.pressler at oracle.com>
> wrote:
>
>>
>> Which makes me wonder, what is the root of the optionality in your code?
>> I.e. how does io.avaje.inject come to be resolved?
>>
>
> So avaje jsonb/config/http implements SPI interfaces exported by avaje
> inject(which is added as a maven optional dependency). The idea here is
> that the plugin implementation would be loaded by avaje-inject to add to
> the DI scope.
>
> Outside of avaje inject, these service implementation classes have no
> meaning and are not meant to be instantiated. In some cases, the service
> implementation package may not even be exported by the module, so even if
> you tried you couldn't instantiate outside of a service loader.
>
> Does this help answer your question? Or did I misread it?
>
>
> I think so, thank you. But when the application runs in a particular
> configuration, the application deployer knows whether or not that
> configuration uses the avaje inject module. If it does not, why is the
> configuration including the service module?
>
> The class path has a loosy-goosey attitude, but modules are all about
> creating a proper configuration. Ideally, there shouldn’t be any modules
> that could not  possibly be used.
>
> We could discuss making modules more laid back in some specific
> situations, but generally, they exist so that we could have a strict
> configuration. So I guess my question now is, why would you want to allow a
> particular application configuration to include a module that could not
> possibly be used in that configuration?
>
> I assume the answer is to avoid the need to document to the user that if
> they choose to use some capability offered by module X (avaje inject) they
> should also add module Y (the service provider). If we were more lenient,
> you could just give them module Y, and then all would work whether or not
> they choose to use X. But the flip side of that is that if they choose not
> to use X, Y would just sit there, unused. Best case scenario, it would just
> increase their image size; worst-case scenario is that it could preclude
> some future optimisations that may require a full program analysis.
>
> To do this properly, as Alan alluded, `requires static` is insufficient
> because the module doesn’t know where the service interface is supposed to
> come from. Rather, we would need a new specific mechanism that says “if
> module X is readable, then provide this service”. To know how important
> such a feature  is we need to understand the source of the problem: why is
> it hard to exclude unused modules?
>
> — Ron
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/jigsaw-dev/attachments/20230419/34d7e048/attachment.htm>


More information about the jigsaw-dev mailing list