Click here to Skip to main content
15,395,290 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Why is this syntax not working as expected? it compile and can run, however it did not give me the expected result
Java
ServiceLoader<ServiceMod> sl = ServiceLoader.load(ServiceMod.class);
for(ServiceMod sm : sl)
    System.out.printf("status: %d\r%n", sm.getStat());
It did not give me any response and it looks like an empty list.

The overview structure of the given project is shown below; it includes the build script, modular system, and synopsis src-code/brief syntax.
I have these multi-module project; driver-mod, provider-mod, & service-mod
DIRECTORIES
[DIR-TREE]
└───driver-mod
    │
    ├───build.gradle.kts
    ├───settings.gradle.kts
    │
    ├───provider-mod
    │   ├───build.gradle.kts
    │   └───src/**/
    │       ├───module-info.java
    │       └───com/mod/provider
    │           ├───ProviderModOnee.java
    │           └───ProviderModTwo.java
    │
    ├───service-mod
    │   ├───build.gradle.kts
    │   └───src/**/
    │       ├───module-info.java
    │       └───com/mod/service/ServiceMod.java
    │
    └───src/**/
        ├───module-info.java
        └───com/mod/driver/DriverMod.java


And this is the illustration/diagram of the my SERVICE PROVIDER MODULE looks like.

──────> requires
======> exports
----------->uses
++++++> provides

    _______________[ nope | optional ]________
    |                                         |
    |                                         V
driver-mod ──────> service-mod <────── provider-mod
driver-mod <====== service-mod ======> provider-mod
driver-mod -----------> service-mod <++++++ provider-mod

driver-mod/src/.../module-info.java
module driver.mod {
    requires service.mod;
    uses com.mod.service.ServiceMod;
}

service-mod/src/.../module-info.java
module service.mod {
    exports com.mod.service;
}

provider-mod/src/.../module-info.java
module provider.mod {
    requires service.mod;
    provides com.mod.service.ServiceMod with 
        com.mod.provider.ProviderModOne,
        com.mod.provider.ProviderModTwo;
}


In Gradle build tool/script
driver-mod/settings.gradle.kts
rootProject.name = "driver-mod"
include(":service-mod")
include(":provider-mod")

driver-mod/gradle.build.kts
dependencies {
    implementation(project(":service-mod"))
}

provider-mod/gradle.build.kts
dependencies {
    implementation(project(":service-mod"))
}


What I have tried:

driver-mod/src/.../com/mod/driver/DriverMod.java
public class DriverMod {
    public static void main(String[] args) {
        ServiceLoader<ServiceMod> sl = ServiceLoader.load(ServiceMod.class);
        for(ServiceMod sm : sl)
             System.out.printf("status: %d\r%n", sm.getStat());
    }
}

service-mod/src/.../com/mod/service/ServiceMod.java
public interface ServiceMod {
    public abstract int getStat();
}

provider-mod/src/.../com/mod/provider/...
public class ProviderModOne implements ServiceMod{
    @Override
    public int getStat() {
        return 1;
    }
}

public class ProviderModTwo implements ServiceMod{
    @Override
    public int getStat() {
        return 2;
    }
}
Posted
Updated 26-May-22 18:50pm
v10
Comments
Phoenix Liveon 29-Mar-22 4:42am
   
I also tried this one, but it did not work either.
java.util.List<servicemod> lsm = ServiceLoader.load(ServiceMod.class)
                .stream()
                .map(ServiceLoader.Provider::get)
                .collect(Collectors.toList());
for(ServiceMod sm : lsm)
     System.out.println(sm.getStat());
Phoenix Liveon 30-Mar-22 0:15am
   
Oh! The issue was that I needed the consumer to configure and add a dependency, which is the PROVIDER via "runtimeOnly" or "implementation.". in other words, the gradle build tool needed to know the PROVIDER, because we use this build tool/script to run our project.

1 solution

The issues were caused by a lack of specification in our build tool/dependencies script's configuration, which we need to add/configure via "runtimeOnly" or "implementation," So then we can tell our build tool that before running/executing or building, we need to first add those dependencies, in this case the "provider-mod" project or artifact must be specified in the build script dependencies configuration to explicitly inform the build tool that it exist; see the code/syntax below;

driver-mod/build.gradle.kts
dependencies {
      implementation(project(":service-mod"))
       
      //REM: add this dependency via; "runtimeOnly" or "implementation"
      runtimeOnly(project(":provider-mod"))
      //implementation(project(":provider-mod"))
}
Why? : use "runtimeOnly" config, because the consumer/"driver-mod" doesn't need "provider-mod" at compile time or we don't want to create an instance/object of it inside of the consumer codes and also the "provider-mod" does not exports anything, hence why do we need to compile it, but we do need it at runtime because we require an artifact/jar of "provider-mod", the build tool will produce it as we run our project that how the gradle build tool works, there's an hierarchical execution;

Important: don't forget to include the following syntax at the same or consumer build script, as shown below:
This will notify/configure your build tool (gradle) and inform java/JVM, if any of your artifacts/project has/was "named-module", "automatic-module", or has a filename "module-info.class" then it will put/contained/inferred it to as a java named-module configuration. (basically it will put it to the "—module-path"), so that it will will do the correct execution, it will do the JPMS features. If we do not do this, it will by default insert it at the "classpath" container/thing, and then the Service Provider features or any JPMS features that must be inside of a "—module-path" will not work as expected to be.
driver-mod/build.gradle.kts
...
java {
    this.modularity.inferModulePath.set(true); 
}
...
The "implementation", on the other hand, will handle both configuration (at compile time and runtime config)
   
v5
Comments
Phoenix Liveon 31-Mar-22 8:39am
   
Or, as a producer, I could simply include it first as a Gradle metadata or a maven-metadata, just add it inside of the "service-mod" gradle build script of dependencies config; then let the build tool create it metadata or publish it via maven publication, so the build tool automatically create metadata or pom file.

service-mod/gradle.build.kts
service-mod/gradle.build.ktsExpand ▼   Copy Code
plugins {   
	`java-library`
	`maven-publish`
}

group = "com.mod"
version = "0.0.1"

repositories {   
	this.maven {       
		this.url = uri("path/to/your/repo") 
	}
}

dependencies {    
	runtimeOnly(project(":provider-mod"))
	//REM: or
	//runtimeOnly("com.mod:provider-mod:0.0.1")
}

publishing {
	this.publications {
		this.create<MavenPublication>("mv-service-mod") {
			this.from(components["java"])        
		}
	}    

	this.repositories {        
		this.maven {
			this.name = "repo"
			this.setUrl("path/to/your/repo")        
		}
	}
}


Then at the consumer hand;
driver-mod/build.gradle.kts
driver-mod/build.gradle.ktsCopy Code
repositories {
	//REM: we can use this, if it is an external module or binary or library
	maven {
		url = uri("https://com.mod/maven/early-release")
	}    
	//REM: or    
	ivy {
		url = uri("https://com.mod/ivy/early-release")
	}
}

dependencies {    
	implementation(project(":service-mod"));    
	//REM: or    
	//implementation("com.mod:service-mod:0.0.1")
}
And this will read the metadata of that project or artifact, with the scope runtime of "provider-mod" artifact.

We can also use the "java-platform" plugin/features of gradle build tool, where we could create a some kind of BOM file to control or to avoid compatibility issues. SEE: https://docs.gradle.org/current/userguide/java_platform_plugin.html

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900