7

I am experimenting with SPI on JDK 9. Entire example works on JDK 9 without "module-info.java". After adding "module-info.java" ServiceLocator is not finding implementing class. I am confused and I cannot find working SPI example in modularized JDK 9 project.

So my example project looks like this:

/spidemo
├── apiModule
│   ├── pom.xml
│   └── src
│       └── main
│           └── java
│               ├── eu
│               │   └── com
│               │       └── example
│               │           └── text
│               │               └── spi
│               │                   └── TextAPI.java
│               └── module-info.java
├── applicationB
│   ├── pom.xml
│   └── src
│       └── main
│           ├── java
│           │   └── eu
│           │       └── com
│           │           └── example
│           │               └── spi
│           │                   └── b
│           │                       └── application
│           │                           └── DemoB.java
│           └── module-info.java
├── applicationCommon
│   ├── pom.xml
│   └── src
│       └── main
│           └── java
│               ├── eu
│               │   └── com
│               │       └── example
│               │           └── spi
│               │               └── application
│               │                   └── TextAPIProvider.java
│               └── module-info.java
├── implementationB
│   ├── pom.xml
│   └── src
│       └── main
│           ├── java
│           │   └── eu
│           │       └── com
│           │           └── example
│           │               └── implb
│           │                   └── text
│           │                       └── TextB.java
│           ├── module-info.java
│           └── resources
│               └── META-INF
│                   └── services
│                       └── eu.com.example.text.spi.TextAPI

I have introduced interface:

package eu.com.example.text.spi;
public interface TextAPI {
    String getHelloWorldText();
}

This interface is implemented by:

package eu.com.example.implb.text;
import eu.com.example.text.spi.TextAPI;
public class TextB implements TextAPI { 
    public String getHelloWorldText() {
        return "Text from B implementation";
    }
}

The implementation is searched by code similar to:

package eu.com.example.spi.application;
import eu.com.example.text.spi.DefaultTextAPI;
import eu.com.example.text.spi.TextAPI;
import java.util.ServiceLoader;
public class TextAPIProvider {

    public static TextAPI getProvider(String providerName) {
        ServiceLoader<TextAPI> serviceLoader = ServiceLoader.load(TextAPI.class);
        for (TextAPI provider : serviceLoader) {
            String className = provider.getClass().getName();
            if (providerName.equals(className)) {
                return provider;
            }
        }
        throw new RuntimeException(providerName + " provider is not found!");
    }
}

And now is the fun part. When I am executing class below without:

  • /implementationB/src/main/java/module-info.java
  • /applicationB/src/main/java/module-info.java

then implementation class is found and text is printed out.

package eu.com.example.spi.b.application;
import eu.com.example.spi.application.TextAPIProvider;
public class DemoB {
    public static void main(String[] args) {
        System.out.println("---> " + TextAPIProvider.getProvider("eu.com.example.implb.text.TextB").getHelloWorldText());
    }
}

After introducing this two "module-info.java" files implementation class is not found by ServiceLocator. Content of /applicationB/src/main/java/module-info.java:

module eu.com.example.applicationB {
    requires eu.com.example.apiModule;
    requires transitive eu.com.example.applicationCommon;
    uses eu.com.example.text.spi.TextAPI;
}

Content of /implementationB/src/main/java/module-info.java:

module eu.com.example.implb.text {
    requires eu.com.example.apiModule;
    exports eu.com.example.implb.text;
//    provides eu.com.example.implb.text.TextB with eu.com.example.text.spi.TextAPI;
}

When I uncomment:

provides eu.com.example.implb.text.TextB with eu.com.example.text.spi.TextAPI;

line then compilation error occurs:

.../implementationB/src/main/java/module-info.java:[7,74] the service implementation type must be a subtype of the service interface type, or have a public static no-args method named "provider" returning the service implementation
.../implementationB/src/main/java/module-info.java:[7,5] service implementation must be defined in the same module as the provides directive

I have tried to change package names as compilation error sugests, but then I have introduced "split package" issues.

What I should do to use ServiceLocator in fully modularized JDK 9? Is it possible? Have anyone seen working example? Code can be also seen here: https://github.com/RadoslawOsinski/spidemo

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459

2 Answers2

5

You can change to using:-

provides eu.com.example.text.spi.TextAPI with eu.com.example.implb.text.TextB; 
// you provide a service through its implementation

instead of

provides eu.com.example.implb.text.TextB with eu.com.example.text.spi.TextAPI; 

Services in the document provides a sample around the implementation.

Naman
  • 27,789
  • 26
  • 218
  • 353
0

A module can specify that it provides a service with a specific type of service providers. It is declared using provides and with keyword.

syntax : provides serviceType with implementationTypes;

( Can specify multiple implementations types as a comma-separated list )

Therefore in your module eu.com.example.implb.text should add following statement.

provides eu.com.example.implb.text.TextB with eu.com.example.text.spi.TextAPI;

NOTE : provides does not equal exports. So any other modules that requires eu.com.example.implb.text will not access eu.com.example.implb.text.TextB if it doesn't exports.

Yuresh Karunanayake
  • 519
  • 1
  • 4
  • 10