graal icon indicating copy to clipboard operation
graal copied to clipboard

ServiceLoader based on module-info.java not working

Open codepitbull opened this issue 2 years ago • 7 comments

Describe the issue I am currently experimenting with JPMS on Java 21 and GraalVM.

I discovered using services defined in a different module than the main one aren't picked up.

Steps to reproduce the issue Clone the reproducer: git clone --depth 1 https://github.com/codepitbull/graalvm-bug-java21.git

There is an app module with a dependency on the dependency module defined:

module javatest.app.main {
    requires javatest.dependency.main;
    uses MyService;
}

The dependency defines the interface and the service implementation:

module javatest.dependency.main {
    exports de.codepitbull.module.example;
    provides MyService with de.codepitbull.module.example.MyServiceImpl;
}

The expctation would be that the app sees the defined service when a native image is built.

Build java only

  • Run ./gradlew :app:build
  • Run the application: `java --module-path ./app/build/libs/app.jar:./dependency/build/libs/dependency.jar -m javatest.app.main/java21.App``

Output:

Found a service: MyService!
Called Dependency.hello()

This shows the service is discovered running regular Java.

Build with native-image

  • Run ./gradlew :app:nativeBuild
  • Run the app app/build/native/nativeBuild/app

Output:

Called Dependency.hello()

This shows that the service is not discovered when using the native build.

Build image natively (to avoid possible Gradle-plugin issues)

  • Run `native-image --module-path ./app/build/libs/app.jar:./dependency/build/libs/dependency.jar --module javatest.dependency.main -m javatest.app.main/java21.App``
  • Run app `java21.app``

Output:

Called Dependency.hello()

Again, service not resolved.

Describe GraalVM and your environment:

  • GraalVM version:
native-image 21 2023-09-19
GraalVM Runtime Environment GraalVM CE 21+35.1 (build 21+35-jvmci-23.1-b15)
Substrate VM GraalVM CE 21+35.1 (build 21+35, serial gc)
  • JDK major version:
openjdk version "21" 2023-09-19
OpenJDK Runtime Environment GraalVM CE 21+35.1 (build 21+35-jvmci-23.1-b15)
OpenJDK 64-Bit Server VM GraalVM CE 21+35.1 (build 21+35-jvmci-23.1-b15, mixed mode, sharing)
  • OS: 13.5.1 (22G90)
  • Architecture: Applie Silicon M2

codepitbull avatar Oct 19 '23 22:10 codepitbull

Hi @codepitbull , You need to register the service interface so that the ServiceLoader can identify and load them. The configuration files need to be created in the folder META-INF/services. In order to resolve the issue create a sub directories inside the dependency folder like this dependency/src/main/resources/META-INF/services and inside the services folder create a file named de.codepitbull.module.example.MyService, that file will have all the implementation of that service (one in each line) in this case only 1 like this de.codepitbull.module.example.MyServiceImpl

hamzaGhaissi avatar Oct 20 '23 13:10 hamzaGhaissi

Thanks for your answer!

I am aware of the old way, but I was looking at doing this using JPMS. According to this issue it is supposed to work: https://github.com/oracle/graal/issues/4141

So, is JPMS not fully supported on Graal? Do you have any pointers where this might be documented?

codepitbull avatar Oct 22 '23 20:10 codepitbull

Thanks, Issue reproduced and we are looking into it

hamzaGhaissi avatar Oct 24 '23 09:10 hamzaGhaissi

With 21.0.2, even if the META-INF/services files are there, they are not recognized and ServiceLoader does not work. JVMs work fine, native-image only works using classpath instead of module-path.

PeteSL avatar Mar 11 '24 11:03 PeteSL

even if the META-INF/services files are there, they are not recognized and ServiceLoader does not work. JVMs work fine, native-image only works using classpath instead of module-path.

I am also seeing this issue, has any progress been made?

SentryMan avatar Sep 24 '24 13:09 SentryMan

Yes this seems like it is pretty bad and hurts module-info uptake.

Someone developing a CLI might want to have a JLink option which would use the modulepath and then a native option for supported platforms and they would have to run it on the classpath which would actually change behavior.

agentgt avatar Sep 24 '24 13:09 agentgt

~~https://github.com/Mechite/test-graal-nativeimage~~ ~~Created a test repository which was used to demonstrate~~ EDIT - No need for one to use my repository, one above can also be used for reproducing what I have stated below the same:

Older version of GraalVM (CE 11) is able to successfully produce a native image, where services defined in a module-info are correctly read and loaded, without META-INF/services

Newer version (latest current Oracle GraalVM 21 as of 24/09/2024) produces an image that does not discover the services.

I have not checked which release of GraalVM has actually broken this behaviour.

mechite avatar Sep 24 '24 14:09 mechite

Hi @codepitbull,

thanks for your reproducer. I have looked into the issue and we now know where the problem is. https://github.com/oracle/graal/issues/9952#issuecomment-2442086616

I will keep you updated.

olpaw avatar Oct 28 '24 16:10 olpaw

Older version of GraalVM (CE 11) is able to successfully produce a native image, where services defined in a module-info are correctly read and loaded, without META-INF/services

Yes, exploiting the LazyClassPathLookupIterator at image-runtime to do the lookup worked until we actually started to implement module-system more correctly at image runtime. Now that we do, LazyClassPathLookupIterator is able to detect that it is actually not supposed to do the lookup for a Service Provider implementation Class that comes from a named module. So the hack from back then does not work anymore. See https://github.com/oracle/graal/issues/9952#issuecomment-2442086616 for more info.

olpaw avatar Oct 28 '24 16:10 olpaw

Hi @codepitbull! The fix that relates to this ticket has been merged (#10202) and will be part of GraalVM 24.2

ivan-ristovic avatar Dec 05 '24 13:12 ivan-ristovic