Search code examples
javajava-modulejava-platform-module-system

What does a module.a binds module.b mean during the resolution?


While performing a proof of concept for a problem - JPMS ServiceLoader does not work for me as expected.

I reached a state to understand the difference in how the two modules were resolved when providing a jar versus target classes to the module path. The console had a one-line difference that of:

base.service binds user.service file://.../user-service/target/classes/

What does this effectively mean? Couldn't really find such reference of this term in the spec or the conceptual draft.

Additionally, when could such behavior differ during automatic module resolution? (refer to this answer)


Solution

  • This most likely refers to the bind operation of resolveAndBind processing usesprovides relationships.

    This method works exactly as specified by resolve except that the graph of resolved modules is augmented with modules induced by the service-use dependence relation.

    More specifically, the root modules are resolved as if by calling resolve. The resolved modules, and all modules in the parent configurations, with service dependences are then examined. All modules found by the given module finders that provide an implementation of one or more of the service types are added to the module graph and then resolved as if by calling the resolve method. Adding modules to the module graph may introduce new service-use dependences and so the process works iteratively until no more modules are added.

    So, binding takes place after resolving, but still before any code of the involved modules has been invoked. Namely, it doesn’t matter whether the ServiceLoader is actually used within the module(s) or not. But when it is used, it will utilize the already available information. So a lot of potential problems have been precluded at this point already. This is also the reason why we could build optimized module images pre-linking such services.

    This, however, doesn’t work with automatic modules, as they don’t have uses directives. Without that information, service lookups can only be done when an actual use of ServiceLoader happens, just like with classes loaded through the old classpath.


    Note that the issue in the linked Q&A is a bit different. According to the OP’s information, a module declaration has been used when compiling. But then, the OP ran the application using the -jar option which puts the specified jar file on the classpath and loads it from there, creating an unnamed module, rather than an automatic module. This ignores the compiled module-info and in absence of old-fashioned META-INF/services/… resources, no service implementation was found at all. The OP’s code was designed to fall back to the default service implementation if none had been found, which is indistinguishable from the scenario of finding the default service through the ServiceLoader.

    The crucial differences of your answer are the start method -m base.service/base.service.ServiceUser, which will locate the base.service through the module path and launch its base.service.ServiceUser, as well as --add-modules …user.service, to ensure that the module providing the service will be added to the runtime environment. The latter is needed since there is no direct dependency from the launched module base.service to the user.service, so it wouldn’t be loaded automatically.

    So in your answer’s setup, both modules are loaded as such. When they have a module-info, its provides directive will be processed to find the service implementation, otherwise, it’s an automatic module and must have a META-INF/services/… file. As said above, there is no equivalent to the uses directive for automatic modules, so you will never see a “binds” log entry for them. But they may still use the service lookup.