View Source

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}