Manmeet Vaseer
April, 2007
Few months back when I was doing my routinely R&D work, I stumbled upon JAR File Specification page. It got me interested when I started to read about Service Provider section of the page. Basically, it provided a simple & light way to create replaceable components. You can add & remove the components at runtime. Java automatically recognized the addition or removal of the components by scanning the classpath for the JAR files at runtime.
Let's go step-by-step creating a service provider.
Service Provider Interface
First, we need to define a Service Provider Interface (SPI), which will be implemented by different service providers. SPI is basically a service that your application wants to utilize. The implementation of the service is left up to the service provider. You can pick any desired implementation of the service.
Let's define a service for our example application. We will create a Print service, which could be used by our application to print text to any device such as console, file, printer, etc.. The following defines the Print service:
package com.vaseer.print.spi;
abstract public class Print {
abstract public void print(String text);
}
Notice, it is an abstract class. So, technically speaking its not an interface. From Java 1.3-1.5, a service is represented by an abstract class. This has been changed in Java 1.6 and we will also see the example in later section of this article.
We will JAR our Print service definition separately so that we can distribute it to any service provider who is interested in implementing it.
Service Provider
Let's create a service provider, which will implement the Print service to print the contents on console (more specifically on STDOUT). Let's call it StdoutPrint.
package com.vaseer.print;
import com.vaseer.print.spi.Print;
public class StdoutPrint extends Print {
public void print(String text) {
System.out.println(getClass().getName() + ": " + text);
}
}
Different vendors could implement the service in different way and
Technically speaking, it is not an interface.
declares a collection of methods that your application is interested in using it.
since Java 1.3,
Few days back when I was doing research on SPI (Service Provider Interface), I stumbled upon this link. In a nutshell, you can use Java Service Provider functionality to create replaceable components. There are other/better ways to achieve this behavior but one of the benefits of using Java Service Provider is that your service is recognized automatically by the Java runtime initialization system. There is no configuration required to make Java aware of your service. Simply, make sure that your service is in classpath. Moreover, you can also make your service available at runtime. Java scans the providers on the classpath every time a request is made to query a service.
http://www.logemann.org/2006/11/30/what-i-am-missing-from-the-jar-service-provider-spec/
http://www.theserverside.com/news/thread.tss?thread_id=44603
http://www.talios.com/using_serviceloaders_a_build_a_modular_applications.htm
http://java.sun.com/javase/6/docs/api/java/util/ServiceLoader.html
Implementing Service Provider
Let's create a simple service, called Print. As the name suggests, this service will provide the printing functionality.
The following shows the Print service code, which is basically an abstract class:
package com.vaseer.spi;
abstract public class Print {
abstract public void printIt();
}
Let's create a service provider (called StdoutPrintProvider) that will provide a concrete implementation of this service. The following shows the StdoutPrintProvider code:
package com.vaseer.provider1;
import com.vaseer.spi.Print;
public class StdoutPrintProvider extends Print {
public void printIt() {
System.out.println("Hey...I am a STDOUT Print Provider...");
}
}
Notice, StdoutPrintProvider extends Print service, which basically print a message on stdout. Now, all needs to be created is service provider configuration file in META-INF/services directory to let Java aware of this newly created service provider. The provider configuration file follows a naming convention, which consists of the fully-qualified name of the service class. In our case, it is com.vaseer.spi.Print. The configuration file contains a list of unique concrete provider class name, one per line. In our case, it would be com.vaseer.provider1.StdoutPrintProvider.
META-INF/services/com.vaseer.spi.Print
com.vaseer.provider1.StdoutPrintProvider