The .jar file has always been a great unit of modularity on the Java
platform. Unfortunately, it also comes with the classpath baggage, and
.jar files were never treated as first class components. OSGi
is the next generation component platform that will bring greater modularity to the Java platform. In my previous blog
I showed the simplest OSGi components imaginable. Now I want to expand
on that slightly by introducing a third component that exposes a key
architectural and design benefit enabled by OSGi.
A classic design statement in the GOF book states that we should separate interface from implementation.
Java provides first class support for this heuristic through it’s use
of interfaces, and we use it all the time. We know it’s good design.
But another important design heuristic less often used states that interfaces should be placed separate from the classes that implement them.
By placed, I mean the modules containing those interfaces. The result
is subtle, yet has an important impact on design quality. The
HelloWorld example serves as a good illustration.
The HelloService interface is the specification implemented by the
HelloServiceImpl. It decouples HelloClient from the implementation, and
adheres to the GOF heuristic of separating interface from
implementation. But bundling the interface in the same component as the
implementation compromises design quality, and is the basis for the
modularity heuristic above. Why? Because while I can still gain
extensibility through new implementations of the interface, I lose
flexibility in how I manage the implementations if the interface and
implementation are deployed in the same component. For instance, in my previous example
where the interface and implementation are deployed in service.jar, I
don’t have the ability to dynamically replace the existing
implementation with another because I can’t remove the implementation
from the OSGi run-time if the interface and implementation are deployed
But if I move the interface to a separate bundle, call it spec.jar,
while leaving the implementation in service.jar, I do have the ability
to swap implementations at run-time when using OSGi. In my Google Code repository
exists the HelloWorldSpec project. There are four bundles - client.jar,
spec.jar, service.jar, and service2.jar. Still a pretty simple example,
but it illustrates the key design heuristic.
In my initial deployment to Felix,
I install client.jar, spec.jar, and service.jar. The results are the
same as in my previous blog entry. But now, if I deploy and start
service2.jar, stop service.jar, and stop client.jar, the OSGi run-time
has managed to dynamically adapt to the changing conditions and
discovers the service2.jar implementation of HelloWorldService. To
experiment for yourself, do something like the folllowing after
checking out the project from Google Code:
- Build all four projects using ant build.all from the HelloWorldSpec root directory.
- Start felix (you may have to modify the script - .bat or .sh - to find the felix.jar).
- Use HelloWorldSpec as the profile if you want to use the bundles
already installed, or create your own profile if you want to install
- If you used HelloWorldSpec as the profile, uninstall the
service2.jar bundle. If you created your own profile, install spec.jar,
service.jar and client.jar.
- Stop client.jar and notice the message. Start client.jar back up.
- Install and start service2.jar
- Stop service.jar.
- Stop client.jar again. Notice the message. Cool - an environment adaptable to changing conditions.
As OSGi adoption continues, a new set of design principles are going
to emerge surrounding modularity. I show but one example here of the
benefit of placing interface in a module separate from implementation.
An example of another important design principle enabled by OSGi is a PublishedInterface
- public classes in a bundle’s exported packages. Something not
supported by the JVM, but enabled by OSGi. In fact, I’ve long been an
advocate of the .jar as a first class Java component, and some time ago
launched Extensible Java to capture many of these modularity patterns. It’s also why I created JarAnalyzer. It’s time I revisit those ideas.