Suppose you want to implement some kind of plug-in architecture. Java has simple mechanism for that. First of all, you have to think of an interface which will provide the functionality of a plugin, in Java terms it's called
a service:
package com.example;
public interface CodecService {
Encoder getEncoder(String charset);
Decoder getDecoder(String charset);
}
This interface should somehow allow to discover if specific instance supports specific request. For example, it can return
null if it doesn't.
Next, you have to create concrete instances of the service, called
service providers:
package com.example.jp;
// ...
public class JapaneseCodecServiceImpl implements CodecService {
public Encoder getEncoder(String charset) {
if (! "EUC-JP".equals(charset)) {
// not supported
return null;
}
return new JapaneseEncoder(charset);
}
// ...
}
Supposedly this class will reside in a different JAR than the service itself. Also, to enable automatic discovery of the service provider you'd have to create a file named
META-INF/services/com.example.CodecService (obviously, the name will change from service to service). This file should contain one line for each provider with the provider fully qualified class name in it:
# comments are allowed
com.example.jp.JapaneseCodecServiceImpl
com.example.cyr.CyrillicCodecServiceImpl
A common practice is to bundle these files with the service providers into a single JAR.
Now you have to create
an access point to your service. This includes using
java.util.ServiceLoader class. This class is parametrized with a service interface. You should create one single instance of that class for each service in your access point code:
public class CodecManager {
private static ServiceLoader serviceLoader = ServiceLoader.load(CodecService.class);
// ...
}
After that, you can add a static method which will serve user's request using discovered service providers. This method will use
ServiceLoader.iterator method (or make use of
ServiceLoader being
Iterable), which firstly iterates over cached providers and then, if you didn't stop iterating, will
lazily load discovered providers one by one:
public class CodecManager {
private static ServiceLoader serviceLoader = ServiceLoader.load(CodecService.class);
public static Encoder getEncoder(String charset) {
synchronized (serviceLoader) {
for (CodecService cs : serviceLoader) {
Encoder e = cs.getEncoder(charset);
if (e != null) {
return e;
}
}
}
}
}
Note that, according to the documentation,
ServiceLoader is not thread-safe. Although this same documentation shows an example without any thread safety means in place.
No comments:
Post a Comment