The dependency injection, like this, performed loosely, at runtime, is a 
problem only when the structure of association is loose.  Modularity has 
injected this detail by suggesting that the “huge jar file” solution is a 
problem.  Jars inside of Jars and other mechanisms have become forbidden due to 
licensing issues and other blind attempts at control of “use” as something that 
provides value to the owner.  Instead of providing sanity, we’ve opened to door 
to hugely complex paths of reference and association that are nearly impossible 
to understand and manage for large software systems.


> On Apr 21, 2023, at 7:19 PM, Johannes Spangenberg 
> <johannes.spangenb...@hotmail.de> wrote:
> 
> Hello, I couldn't quite follow some of the arguments regarding this topic. I 
> wasn't sure if I should interfere, but I hope it will be helpful now that I 
> did. :/
> 
>> 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.
> 
> Not sure about the worst-case. The current implementation already checks 
> during initialization whether the service is usable. After all, it throws an 
> exception when it is not. Any optimization can just ignore the service 
> provider in such case. There is no full program analysis required, which is 
> not already part of every module initialization.
> 
>> 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”.
> 
> Why do you need the information which module provides the service interface? 
> I currently don't see how this information would be helpful. I could also 
> imagine cases where you want to provide an optional service without using 
> `requires static`. For example, imagine a framework introduces a new service 
> interface. You may want to provide an implementation for the service 
> interface while keeping support for previous versions of the framework.

What I would prefer the most, is a very structured mechanism akin to what 
.Net’s HostBuilder mechanism provides.  Much like a docker-compose.yml file 
provides the details of how your application environment is assembled, the 
HostBuilder mechanism provides a very clean mechanism to allow a chained 
calling structure that would provide the opportunity for injection of services 
needed.  In essence, it really feels liked we need some kind of IModuleContext 
and ContextBuilder that would provide the ability to inject the things that you 
intend to use in the way that you need them to be.

A configuration file at the manifest level could allow outside specifications, 
but inside of the ContextBuilder use, there could be explicit choices that 
provide the intended modules and the context of their use so that static and 
dynamic assemblies could both happen.

> 
>> The module system then sees that Y wants to provide a service, but the 
>> module system has no idea where the service is, so fails fast at startup. 
>> This is a feature, not a bug. The classpath behavior, of only discovering 
>> that a service provider can't provide a service during run time (because the 
>> service is missing), is not what we wanted for modular services.
> 
> Does this issue really exist? The module system would already discover that 
> during initialization. When the service interface is not available, the 
> service provider will be ignored, there is no error (or anything else) later 
> during runtime. Besides that, if the service interface is not available, you 
> are unable to invoke the service loader anyway. Invoking the service loader 
> requires a class instance of the service interface, which is not available. 
> If you would still try to invoke the service loader, you would get a NCDFE at 
> the call site of the service loader, the provides clause wouldn't make a 
> difference.
> 
> I am not quite sure about the behavior if you stack multiple module layers on 
> top of each other. I think the service loader should only load instances that 
> implement the class from the same module layer as the class instance given to 
> the service loader. Everything else would cause a runtime error independent 
> of the issue we are currently discussing. In this case, ignoring the provides 
> clause would be the right solution, even if X would be available on another 
> module layer.
> 
> I currently see only two reasons for keeping the current behavior. The first 
> one is to discourage service providers to be casually added to modules. You 
> could argue that service providers should have their own module, so that 
> users can add and remove them individually. The second reason might be the 
> desire to fail early when the service interface was renamed or removed after 
> updating X. This could be addressed by adding a modifier like `provides 
> static`, there is no need to correlate the provides cluase with a specific 
> `requires` clause.

Service loader has a problem of not being able to readily stack dependencies 
around services/modules very readily.  It seems like the struggle here is about 
being specific enough and the mechanisms of today are too much on the 
suggestion or guessing side instead of being specific enough.

> 
>> So I think a more general solution than adding a way to describe “this 
>> service is conditional on the presence of X” would be to allow multiple 
>> modules in a single JAR
> 
> I guess that would work as well, but it would probably be more complex to set 
> up.
> 
> Best regards
> Johannes

Gregg Wonderly

Reply via email to