How to Create Extensible Java Applications
Many applications benefit from being open to extension. This post describes two ways to implement such extensibility in Java.
Extensible Applications
Extensible applications are applications whose functionality can be extended without having to recompile them and sometimes even without having to restart them. This may happen by simply adding a jar to the classpath, or by a more involved installation procedure.
One example of an extensible application is the Eclipse IDE. It allows extensions, called plug-ins, to be installed so that new functionality becomes available. For instance, you could install a Source Code Management (SCM) plug-in to work with your favorite SCM.
As another example, imagine an implementation of the XACML specification for authorization. The “X” in XACML stands for “eXtensible” and the specification defines a number of extension points, like attribute and category IDs, combining algorithms, functions, and Policy Information Points. A good XACML implementation will allow you to extend the product by providing a module that implements the extension point.
Service Provider Interface
Oracle’s solution for creating extensible applications is the Service Provider Interface (SPI).
In this approach, an extension point is defined by an interface:
package com.company.application; public interface MyService { // ... }
You can find all extensions for such an extension point by using the ServiceLoader class:
public class Client { public void useService() { Iterator<MyService> services = ServiceLoader.load( MyService.class).iterator(); while (services.hasNext()) { MyService service = services.next(); // ... use service ... } }
An extension for this extension point can be any class that implements that interface:
package com.company.application.impl; public class MyServiceImpl implements MyService { // ... }
The implementation class must be publicly available and have a public no-arg constructor. However, that’s not enough for the ServiceLoader
class to find it.
You must also create a file named after the fully qualified name of the extension point interface in META-INF/services
. In our example, that would be:
META-INF/services/com.company.application.Myservice
This file must be UTF-8 encoded, or ServiceLoader
will not be able to read it. Each line of this file should contain the fully qualified name of one extension implementing the extension point, for instance:
com.company.application.impl.MyServiceImpl
OSGi Services
The SPI approach described above only works when the extension point files are on the classpath.
In an OSGi environment, this is not the case. Luckily, OSGi has its own solution to the extensibility problem: OSGi services.
With Declarative Services, OSGi services are easy to implement, especially when using the annotations of Apache Felix Service Component Runtime (SCR):
@Service @Component public class MyServiceImpl implements MyService { // ... }
With OSGi and SCR, it is also very easy to use a service:
@Component public class Client { @Reference private MyService myService; protected void bindMyService(MyService bound) { myService = bound; } protected void unbindMyService(MyService bound) { if (myService == bound) { myService = null; } } public void useService() { // ... use myService ... } }
Best of Both Worlds
So which of the two options should you chose? It depends on your situation, of course. When you’re in an OSGi environment, the choice should obviously be OSGi services. If you’re not in an OSGi environment, you can’t use those, so you’re left with SPI.
But what if you’re writing a framework or library and you don’t know whether your code will be used in an OSGi or classpath based environment?
You will want to serve as many uses of your library as possible, so the best would be to support both models. This can be done if you’re careful.
Note that adding a Declarative Services service component file like OSGI-INF/myServiceComponent.xml
to your jar (which is what the SCR annotations end up doing when they are processed) will only work in an OSGi environment, but is harmless outside OSGi.
Likewise, the SPI service file will work in a traditional classpath environment, but is harmless in OSGi.
So the two approaches are actually mutually exclusive and in any given environment, only one of the two approaches will find anything. Therefore, you can write code that uses both approaches. It’s a bit of duplication, but it allows your code to work in both types of environments, so you can have your cake and eat it too.
Reference: How to Create Extensible Java Applications from our JCG partner Remon Sinnema at the Secure Software Development blog.
It was difficult to understand this from the Oracle documentation.