Thursday, 28 March 2013

Java ServiceLoader Example

Defining an API and developing the corresponding implementation has become an uber mainstream practice when developing large Java applications. Modularization is such an useful design principle, especially to avoid spaghetti code, for testing and debugging, and for re-implementation of old code.

Many developers seek to separate API interfaces and abstract classes from their implementation in separate packages. Yet, this almost always leads to cycles between java packages (api refers to impl, and vice-versa). There has been many frameworks, such as OSGi, supporting modularization, but the documentation has not always been good and complete. Many developers have struggled with class loading issues too.

Fortunately, Java has delivered the ServiceLoader utility since release 6. The example described in this post is available from Github in the Java-ServiceLoader directory. It is inspired from here.

Service & Implementation

We define a simple interface and its implementation:
package com.service.API;

public interface MyService {
    long getTime();
}


package com.service.API.Impl;

import com.service.API.MyService;

public final class MyServiceImpl implements MyService {

    public MyServiceImpl() { };

    @Override
    public long getTime() {
        return System.currentTimeMillis();
    }

}
The API and the implementation are located in different packages. Only the implementation refers to the API. The implementation must have a public parameterless constructor.

Service Loading

Next, we declare the implementation in a file having the fully qualified name of the API under the META-INF/services directory in the .jar:


The file contains fully qualified name of the implementation:

    com.service.API.Impl.MyServiceImpl

Next, we load the service using the ServiceLoader:
public class MyApp {

    public static <T> T loadService(Class<T> api) {

        T result = null;

        ServiceLoader<T> impl = ServiceLoader.load(api);

        for (T loadedImpl : impl) {
            result = loadedImpl;
            if ( result != null ) break;
        }

        if ( result == null ) throw new RuntimeException(
            "Cannot find implementation for: " + api);

        return result;

    }

    public static final MyService myService = loadService(MyService.class);

    public static void main(String[] args) {

        System.out.println("Time is: " + myService.getTime());

    }

}
The service implementation is loaded with the loadService static method. The main method prints the current time in milliseconds. It is that simple!