Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SPI + JDK 9 + module-info.java

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 modulerized 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 simmiliar 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

like image 887
Radosław Osiński Avatar asked Oct 08 '17 14:10

Radosław Osiński


People also ask

What are modules in Java 9?

Defining the Java 9 module. A module is a collection of code, data, and resources. It is a set of related packages and types (classes, abstract classes, interfaces, and more) with code, data files, and some static resources.

Which is correct about module system in Java 9?

Java Module System is a major change in Java 9 version. Java added this feature to collect Java packages and code into a single unit called module. In earlier versions of Java, there was no concept of module to create modular Java applications, that why size of application increased and difficult to move around.

Is module info .java mandatory?

Yes, module-info. java was introduced in Java 9, with the Project Jigsaw module system. A module is a build artifact (usually a Jar file) that contains a module descriptor that declares the name of the module, what other modules it depends on, what packages it exposes to other modules, and what services it implements.


1 Answers

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.

like image 65
Naman Avatar answered Nov 14 '22 23:11

Naman