In this two-part article, Aslam Khan [1], technical director of PBT Group [2], a software consultancy based in South Africa, introduces you to OSGi and discusses its relevance specifically in the context of the enterprise. What is the exact problem domain of OSGi in this area? In this first part, he uses code snippets to show how the issues surrounding dynamic class management can be tackled. -- Geertjan Wielenga, JavaLobby Zone Leader
OSGi – The Dynamic Module System for Java has been receiving a fair bit of attention in recent times. Given that high profile products such as Eclipse, IBM WebSphere, JOnAS, the Spring Framework are all using OSGi or providing support for OSGi, it is hardly surprising at all. What is surprising is that OSGi is currently in its fourth release implying a significant degree of maturity. Even more surprising is that OSGi was originally intended for small footprint, embedded devices running Java. So why are we giving attention to a technology (actually, it's just a specification) that was originally targeted to solve problems on the opposite end of the continuum from enterprise applications?
On the enterprise application front, we have been tackling the typical enterprise problems such as scalability, transaction management, security, high availability, manageability and the like for a long time. Perhaps it is a stereotypical statement, but we do understand these problems and have successfully crafted solutions for each of them to an acceptable degree. OSGi is not intended to solve such problems and these problems are not discussed in this article.
Tougher, though, are the more abstract problems that we face in designing and building enterprise applications. For many of these abstract problems, we have abstract solutions, i.e. patterns. Interestingly, these abstract problems lie less in the realm of enterprise applications and more in realm of software architecture, object orientation and general software development challenges. Indeed, we have many patterns for many problems, but we also have problems that have no pattern based solutions.
OSGi has gained prominence because it is being used to address some tough problems in Java enterprise application development and software architecture in general. There are several problems that can be tackled using OSGi.
These problems are certainly not the only problems that are largely unsolved, nor are they the only problems addressed by OSGi. However, for large-scale enterprise applications, that have long-living projects, this is certainly high on the agenda of any software architect (or it soon moves high up the priority list). Let us look at each of these three in turn.
Dynamic Class Management
The productivity of POJO based development has been highly touted in recent times. The popular Spring Framework, Hibernate object relational mapper and other open source projects have proven that POJO based development can reduce complexity and increase simplicity. The Spring Framework, with its Inversion of Control/Dependency Injection container, has also shown that interface driven development leads to good separation of concerns and loose coupling.
Certainly, interface driven development and a POJO based development paradigm is highly productive. However, once these POJO based applications are deployed in the JVM, there is littlein terms of run-time management thereof. Writing or generating JMX MBeans does offer some
form of management, but a managed ecosystem similar to that of EJB containers, is lacking.
With OSGi, POJO based solutions finally have a managed ecosystem; and the OSGi ecosystem is dynamic. Packages (i.e. bundles, in OSGi terminology) can be dynamically loaded and unloaded at run-time with absolutely no need to bounce the JVM, or the OSGi run-time. In order to achieve this, the OSGi class loader is really strong and watertight. No weird class loading trickery is tolerated and even reflection cannot peek inside the bundles.
More importantly, OSGi R4 introduces a service registry in which bundles register the interfaces they wish to expose (i.e. export) to everyone else in the run-time environment. Any other bundle can search the registry to find the interfaces that it needs. Consider the following code fragments which registers a service class that finds a data access object class that it needs to fulfill its tasks.
package org.samples.service;
import java.util.List;
public interface MyService {
public List findAllObjects();
}
package org.samples.dao;
import java.util.List;
public interface MyDao {
public List retrieveAllObjects();
}
package org.samples.dao;
// ...
public class MyDaoImpl implements MyDao {
// ...
public List retrieveAllObjects() {
// ...
return objectList;
}
}
package org.samples.dao;
// ...
public class MyDaoActivator implements BundleActivator {
private ServiceRegistration registration;
public void start(BundleContext context) { MyDao dao = new MyDaoImpl(); Dictionary props = new Properties(); // ... registration = context.registerService(MyDao.class.getName(), dao, props);
}
public void stop(BundleContext context) {
registration.unregister();
}
}
package org.samples.service;
// ...
public class MyServiceImpl implements MyService {
private MyDao myDao; protected void bindDao(MyDao dao) { myDao = dao; } protected void unbindDao(MyDao dao) { myDao = null; } public List findAllObjects() { // ... return (myDao == null) ? null : myDao.retrieveAllObjects(); }
}
package org.samples.dao;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.ServiceTracker;
// ...
public class MyDaoTracker extends ServiceTracker {
private final MyServiceImpl myService = new MyServiceImpl();
private int daoCount = 0;
private ServiceRegistration registration = null;
public MyDaoTracker(BundleContext context) {
super(context, MyDao.class.getName(), null);
}
private boolean registering = false;
public Object addingService(ServiceReference reference) {
MyDao myDao = (MyDao) context.getService(reference);
myService.bindDao(myDao);
synchronized (this) {
daoCount++;
if (registering)
return myDao;
registering = (daoCount == 1);
if (!registering)
return myDao;
}
ServiceRegistration reg = context.registerService(MyService.class
.getName(), myService, null);
synchronized (this) {
registering = false;
registration = reg;
}
return myDao;
}
public void removedService(ServiceReference reference, Object service) {
MyDao myDao = (MyDao) service;
myService.unbindDao(myDao);
context.ungetService(reference);
ServiceRegistration needsUnregistration = null;
synchronized (this) {
daoCount--;
if (daoCount == 0) {
needsUnregistration = registration;
registration = null;
}
}
if (needsUnregistration != null) {
needsUnregistration.unregister();
}
}
}
package org.samples.service;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
// ...
public class MyServiceActivator implements BundleActivator {
private MyDaoTracker tracker;
public void start(BundleContext context) {
tracker = new MyDaoTracker(context);
tracker.open();
}
public void stop(BundleContext context) {
tracker.close();
}
}
Apart from the OSGi specific code for the Bundle Activators and Service Trackers, the rest of the code is interface driven and POJO based development style.
The Spring Dynamic Modules project eliminates the need to write the above boilerplate code for registering services, finding services and tracking services. The same example above is easily wired together in a Spring Framework application context descriptor.
Loading and unloading of these bundles is absolutely trivial. On the open source OSGi implementations such as Apache Felix and Eclipse Equinox, a text console is available to manage the environment.
It is clear that OSGi is an environment in which dependencies appear and disappear without warning. What you do when your dependencies are not present, or disappear, is up to you. The obvious options are to ignore the missing dependencies, wait for them or raise an exception.
Another twist in the tail is that there could be more than one service that satisfies the dependencies of a bundle. Therefore, cardinality of the dependencies is significant. The manner in which cardinality can be dealt with is beyond the scope of this article.
Next week: In the final part of this two-part series, Aslam discusses class versioning and modularity. How does class versioning relate to backward compatibility? How can modules discover each other? Aslam tells us about Domain Driven Design and closes by answering the question "Can you use OSGi in your applications today?" For the answer to this question, read part 2, this time next week...
| Attachment | Size |
|---|---|
| AslamKhan_000.jpg [3] | 9 KB |
| osgi.png [4] | 63.31 KB |
Links:
[1] http://aslamkhan.net/
[2] http://www.pbt.co.za/
[3] http://eclipse.dzone.com/sites/all/files/AslamKhan_000.jpg
[4] http://eclipse.dzone.com/sites/all/files/osgi.png