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

Gregg Wonderly greggwon at cox.net
Sat Apr 22 17:55:48 UTC 2023


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.spangenberg at 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



More information about the jigsaw-dev mailing list