h2. Introduction
When we use an approach +service based+ we have to deal with interfaces (i.e. _contracts_), factories and implementations. We need a mechanism to discovery the right implementation for specific service. A commons solution is to use a +*system property*+ that contain the *full qualified name (FQN)* of the implementation class and then the factory will use this to instantiate the implementation. *Service Provider Interface* allow us to use a standard technique to discovery a right implementation on a desired interface (i.e. _contract_ )
h2. SPI
This technique consist of put a file, whose name is contract's name , within jar under path *META-INF/services*. For example if the interface is _org.bsc.spi.MyContract_ into the jar the file will be:
{noformat}
META-INF/services/org.bsc.spi.MyContract
{noformat}
and within this file must be put just one line containing the FQN of the implementation.
At this point the steps to create a SPI application will be:
# create a contract (i.e. project containing interface)
# create a provider that implements the contract and publish implementation through SPI service
# create a factory (consumer) that discovery implementation and return the instance
h2. Java Example
h3. Create a contract
{code:java}
package org.bsc.spi;
public interface MyContract
{
String test();
}
{code}
h3. Create a provider
As provider we mean an implementation of a contract. See example below:
{code}
public class MyProvider implements MyContract
{
public String test() {
return "hello world!";
}
}
{code}
To publish this provider through the SPI we have found a very useful project [https://metainf-services.dev.java.net/] that allow us to write the SPI infos simply using the annotation *@MetaInfServices*. See example below
{code}
@MetaInfServices
public class MyProvider implements MyContract
{
public String test() {
return "hello world!";
}
}
{code}
to use this project from maven you have to put the following dependency in your pom
{code:xml|title=pom fragment}
<repositories>
<repository>
<id>java.net</id>
<url>http://download.java.net/maven/2/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.kohsuke.metainf-services</groupId>
<artifactId>metainf-services</artifactId>
<version>1.1</version>
<optional>true</optional>
<type>jar</type>
</dependency>
{code}
Note: *optional* means that this dependency will be not include in project that refer to your provider project. Run *clean* & *install* (or *package*) and automatically the SPI information will be included in your jar
h3. Create a Factory
Finally we can develop the factory that discover provider and instantiate it
{code:java}
public class MyContractFactory {
public static MyContract createInstance() throws Exception {
java.io.InputStream is = MyContractFactory.class.getClassLoader().getResourceAsStream("META-INF/services/org.bsc.spi.MyContract");
java.io.LineNumberReader r = new LineNumberReader( new java.io.InputStreamReader(is));
String fqn = r.readLine();
return (MyContract)Class.forName(fqn).newInstance();
}
}
{code}
{code:java}
public class MyContractFactory {
public static List<MyContract> createInstance() throws Exception {
java.util.List<MyContract> result = new java.util.ArrayList<MyContract>();
Enumeration<URL> e = Consumer.class.getClassLoader().getResources("META-INF/services/org.bsc.spi.MyContract");
while( e.hasMoreElements() ) {
URL url = e.nextElement();
java.io.LineNumberReader r = new LineNumberReader( new java.io.InputStreamReader(url.openStream()));
String fqn = r.readLine();
result.add( (MyContract)Class.forName(fqn).newInstance() );
}
return result;
}
}
{code}
When we use an approach +service based+ we have to deal with interfaces (i.e. _contracts_), factories and implementations. We need a mechanism to discovery the right implementation for specific service. A commons solution is to use a +*system property*+ that contain the *full qualified name (FQN)* of the implementation class and then the factory will use this to instantiate the implementation. *Service Provider Interface* allow us to use a standard technique to discovery a right implementation on a desired interface (i.e. _contract_ )
h2. SPI
This technique consist of put a file, whose name is contract's name , within jar under path *META-INF/services*. For example if the interface is _org.bsc.spi.MyContract_ into the jar the file will be:
{noformat}
META-INF/services/org.bsc.spi.MyContract
{noformat}
and within this file must be put just one line containing the FQN of the implementation.
At this point the steps to create a SPI application will be:
# create a contract (i.e. project containing interface)
# create a provider that implements the contract and publish implementation through SPI service
# create a factory (consumer) that discovery implementation and return the instance
h2. Java Example
h3. Create a contract
{code:java}
package org.bsc.spi;
public interface MyContract
{
String test();
}
{code}
h3. Create a provider
As provider we mean an implementation of a contract. See example below:
{code}
public class MyProvider implements MyContract
{
public String test() {
return "hello world!";
}
}
{code}
To publish this provider through the SPI we have found a very useful project [https://metainf-services.dev.java.net/] that allow us to write the SPI infos simply using the annotation *@MetaInfServices*. See example below
{code}
@MetaInfServices
public class MyProvider implements MyContract
{
public String test() {
return "hello world!";
}
}
{code}
to use this project from maven you have to put the following dependency in your pom
{code:xml|title=pom fragment}
<repositories>
<repository>
<id>java.net</id>
<url>http://download.java.net/maven/2/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.kohsuke.metainf-services</groupId>
<artifactId>metainf-services</artifactId>
<version>1.1</version>
<optional>true</optional>
<type>jar</type>
</dependency>
{code}
Note: *optional* means that this dependency will be not include in project that refer to your provider project. Run *clean* & *install* (or *package*) and automatically the SPI information will be included in your jar
h3. Create a Factory
Finally we can develop the factory that discover provider and instantiate it
{code:java}
public class MyContractFactory {
public static MyContract createInstance() throws Exception {
java.io.InputStream is = MyContractFactory.class.getClassLoader().getResourceAsStream("META-INF/services/org.bsc.spi.MyContract");
java.io.LineNumberReader r = new LineNumberReader( new java.io.InputStreamReader(is));
String fqn = r.readLine();
return (MyContract)Class.forName(fqn).newInstance();
}
}
{code}
{code:java}
public class MyContractFactory {
public static List<MyContract> createInstance() throws Exception {
java.util.List<MyContract> result = new java.util.ArrayList<MyContract>();
Enumeration<URL> e = Consumer.class.getClassLoader().getResources("META-INF/services/org.bsc.spi.MyContract");
while( e.hasMoreElements() ) {
URL url = e.nextElement();
java.io.LineNumberReader r = new LineNumberReader( new java.io.InputStreamReader(url.openStream()));
String fqn = r.readLine();
result.add( (MyContract)Class.forName(fqn).newInstance() );
}
return result;
}
}
{code}