jlink: service binding links many unnecessary modules
Asked Answered
U

2

8

I face the problem that the service binding option of jlink links many, many modules, none of them seems to be necessary. These modules aren't linked when the service binding option is omitted.

Questions:

  • Q1: Do you see the same behavoir in your environment ?
  • Q2: Is this a bug or a desired behavoir ?
  • Q3: Why all these modules are linked ?

My application: The application is a simple service consisting of an interface, a provider and a consumer, each packed into a separate module, called modService, modProvider, modConsumer (details below).

OS: Windows 10

Jlink without --bind-services yields the expected result:

jlink --module-path "mods;%JAVA_HOME%\jmods"
    --add-modules modConsumer
    --output myRuntime

java --list-modules
java.base@9
modConsumer
modService 

When the --bind-services option is applied, I would expect that in addition the module modProvider should be linked. However, see what happens (the three custom modules are at the end):

jlink --module-path "mods;%JAVA_HOME%\jmods"
    --bind-services
    --add-modules modConsumer
    --output myRuntime

java --list-modules
java.base@9
java.compiler@9
java.datatransfer@9
java.desktop@9
java.logging@9
java.management@9
java.management.rmi@9
java.naming@9
java.prefs@9
java.rmi@9
java.scripting@9
java.security.jgss@9
java.security.sasl@9
java.smartcardio@9
java.xml@9
java.xml.crypto@9
jdk.accessibility@9
jdk.charsets@9
jdk.compiler@9
jdk.crypto.cryptoki@9
jdk.crypto.ec@9
jdk.crypto.mscapi@9
jdk.deploy@9
jdk.dynalink@9
jdk.internal.opt@9
jdk.jartool@9
jdk.javadoc@9
jdk.jdeps@9
jdk.jfr@9
jdk.jlink@9
jdk.localedata@9
jdk.management@9
jdk.management.cmm@9
jdk.management.jfr@9
jdk.naming.dns@9
jdk.naming.rmi@9
jdk.scripting.nashorn@9
jdk.security.auth@9
jdk.security.jgss@9
jdk.unsupported@9
jdk.zipfs@9
modConsumer
modProvider
modService

I have no clue why all these modules are linked because the provider just returns a string so that no other jdk module than java.base should be needed.

Below are the Java artifacts:

package test.service; 

public interface HelloService { 
  public String sayHello(); 
}

package test.provider; 
import test.service; 

public class HelloProvider implements HelloService { 
  @Override public String sayHello() { return "Hello!"; }
}

package test.consumer; 
import test.service; 
import java.util.ServiceLoader; 

public class HelloConsumer { 
  public static void main(String... args) { 
    ServiceLoader.load(HelloService.class).forEach(s -> System.out.println(s.sayHello())); 
  }
}

module modService { 
  exports test.service; 
}

module modProvider { 
  requires modService; 
  provides test.service.HelloService with test.provider.HelloProvider; 
}

module modConsumer { 
  requires modService; 
  uses test.service.HelloService; 
} 

Any help is appreciated.

Umbilication answered 26/1, 2018 at 19:18 Comment(0)
P
8

Short version

  • Q1: Yes.
  • Q2: Desired behavior
  • Q3: Because you told jlink so with --bind-services πŸ˜‰

Long Version

By default jlink does not bind services, to keep the created runtime as small as possible. That changes with --bind-services, about which the documentation says

Link service provider modules and their dependencies.

That mirrors the behavior during regular module resolution where, after all dependencies have been resolved, all modules that provide a service used by those modules are included in the readability graph.

The same happens in your case, so all modules providing services used by java.base, modConsumer, and modService are included in the image. As you have found out, that are quite a lot.

Solution

If you want to avoid that, you have to forego --bind-services and instead explicitly list the providers that you want to see in your image:

jlink --module-path "mods;%JAVA_HOME%\jmods"
    --add-modules modConsumer,modProvider
    --output myRuntime
Pilaf answered 26/1, 2018 at 21:28 Comment(1)
I think the main problem here is the name of the option, it should be more like "--add-all-service-providing-modules" because the service binding will happen even if you dont specify the option (but of course only for the contained providers). I BTW think --limit-modules is good if you want to hand-optimize the image content. – Curarize
B
5

As stated in the jlink documentation. The

--bind-services 

Link service provider modules and their dependencies.

further, a sample in the same illustrates that the

option will link the modules resolved from root modules with service binding; see the Configuration.resolveAndBind method.

From your previous command the root module and the ones resolved in your module graph by default are :

java.base@9
modConsumer
modService

and further, the other modules listed while making use of --bind-services flag are resolved via the java.base module.

I would expect that in addition the module modProvider should be linked

As suggested by nicolai, you can add the provider module and ensure that it is resolved as well in the module graph.

 --add-modules modConsumer,modProvider

Thinking out loud. 1. The current process for finding providers is iterative. 2. Is it possible to specify modules for which one to look for while finding service providers explicitly?

Bergen answered 26/1, 2018 at 19:37 Comment(5)
So, the observed behavior (i.e. many modules linked with services binding option) is normal ? What makes me wonder: One reason for using jlink is to build a minimal runtime image. However with --bind-services the result is far from minimal. Is it therefore preferable to link service modules directly via --add-modules ? – Umbilication
@Umbilication The --bind-services used by you states that you desire to make use of the modules that provide service implementation for the current module services and their dependencies. If that's not true you must not make use of the flag and rely on the root module classes simply. I would IMHO prefer to make use of the service provider modules when I am supposedly making use of their implementations as well or else rely on simply the modules added using the --add-modules. – Bergen
@nullpointer Only modConsumer is a root module, not the other two. – Pilaf
@Nicolai Yup, true. Thanks for correcting, the other two are resolved into the graph though while adding the root module. – Bergen
There's a lot more to say on this topic. A good start is to run java --show-module-resolution -version to get a better understanding of why service providers are resolved at runtime. Moving to jlink, then the --suggest-providers option can be useful to list the modules that provide services. If you know the names of the service provides then you can specify them to jlink, alternatively you can let jlink figure it out by specifying the service type to the --bind-services option. – Grouper

© 2022 - 2024 β€” McMap. All rights reserved.