How OSGi Changed My Life
The promises of the Lego hypothesis have yet to materialize fully, but they remain a goal worth pursuing.
PETER KRIENS, AQUTE
In the early 1980s I discovered OOP (object-oriented programming) and fell in love with it, head over heels. As usual, this kind of love meant convincing management to invest in this new technology, and most important of all, send me to cool conferences. So I pitched the technology to my manager. I sketched him the rosy future, how one day we would create applications from ready-made classes. We would get those classes from a repository, put them together, and voila, a new application would be born.
Today we take objects more or less for granted, but if I am honest, the pitch I gave to my manager in 1985 never really materialized. The reuse of objects never achieved the levels foreseen by people such as Brad Cox with his software-IC model, and many others, including myself. Still, this Lego hypothesis remains a grail worth pursuing.
What We Needed
In the late ’90s, while working for Ericsson Research, I got the chance to explore the Lego hypothesis further. I was asked to participate in a software standardization process for home automation. This effort was initiated by a group of companies—including IBM, Ericsson, Nortel, Sybase, Sun, France Telecom, Motorola, and Philips—which later turned into the OSGi Alliance, a nonprofit organization that promotes an open dynamic component platform.
The key problem we had to address in 1999 was how to let applications from different vendors work reliably together and share resources in an embedded computer in the home. Sounds simple enough, but a home can contain a number of heterogeneous networks connecting many devices. It was clear from the beginning that we needed a model where applications could (re)use each other’s resources and functions.
A model that forced every application to contain its drivers and libraries would not work (e.g., the way Java phone software works today); nor would a closed API set (called a profile in Java) because the diversity in the target market was too large. More important, we wanted these applications to collaborate without having a priori knowledge of each other. We also wanted hot-pluggability. The idea that you had to bring down the home server to add a new function was not very enticing. Remember, at the time of the conception of the OSGi Alliance in 1999, Windows 95 was mainstream and all of us in the technical group were fed up with rebooting our PCs whenever some configuration had changed. “No reboot” became our mantra in the early days.
Another mantra was that each member company of the OSGi Alliance could implement the specifications in its own way—not just in the large, but also in the small. That is, different parts could come from different suppliers, and those parts from different suppliers had to be able to collaborate.
In such an environment, security is not optional. It was therefore clear from the beginning that security had to be built into the architecture. The ability to run applications from different sources with different trust levels was a key requirement, although a nonsecure system should also be possible if the home servers were managed in a fully controlled environment. We had to support fully closed systems, walled gardens, and completely open systems.
The environment for the home server was the first decision we had to make. This was not too hard because Java really had no competition. Sun Microsystems, famous for this object-oriented language, was an OSGi member. So, Java was more or less a given, with no objection from any of the other participants.
The best part of Java is the VM (virtual machine) architecture. This makes it easy to support the specifications in many different devices, from small to large—a clear must. Today, the VM allows us to pick from a wide variety of languages (PHP, Scala, Groovy, Ruby, Python, etc.) and run on an even wider variety of hardware.
At that time, objects were clearly useful, but their reuse was limited because of object entanglement problems. Most software practitioners know the desperate feeling they get when they use class A, it drags in library B, which needs library C, ad nauseam. In the early years of OOP, we were so enthusiastic about encapsulation that most of us never really paid attention to coupling. Interestingly, the predecessor of OO was structured programming, and its mantra was: low coupling, high cohesion. I guess we were too busy with inheritance, polymorphism, and other object-oriented novelties, so who wanted to learn from these old geezers? In 1998, however, it was clear that a Java framework for home automation applications required something with a larger granularity than classes.
What we needed was a component framework where you could dynamically manage the components in a robust and reliable way. We wanted to be able to install new components, update existing components, start/stop components, and uninstall them—and all of this without reboot, completely dynamic. If component A used component B, this dependency had to be managed and not discovered with an exception when the component was started.
If you have components, then these components need to find each other. Even trickier, since we did not allow reboot, the components had to detect when their dependents went away as well. To address these dynamics, we developed a service model. A component should be able to register a service object so that another component can find this service and bind to it, as well as unbind if the service goes away. Each service must have a contract that specifies its behavior. This architecture could have been called SOA (service-oriented architecture) long before this term became popular. In contrast with most popular SOAs today, however, the OSGi model does not require Web services or any other kind of communications layer. An OSGi service is a POJO (plain old Java object).
The OSGi Specifications
Sun not only provided the environment, but also gave us a product called JES (Java Embedded Server). JES allowed the installation and uninstallation of bundles (ZIP/JAR files) in a running Java Virtual Machine. That is, the VM was shared among many different applications. A bundle was more or less the component we were looking for. As good developers, however, we felt that we had to change it, and so we did—extensively. We have taken about eight years, and the clock is still ticking.
The OSGi specifications, now at release 4, are definitely mature. Though the home automation market collapsed along with the Internet hype in 2000 (just when release 1 came out), the OSGi specifications have been adopted by the phone, automotive, and industrial automation industries, and have recently become quite popular in the enterprise computing world, where OSGi technology is intended to augment or replace the heavier J2EE containers and other application servers.
An Example System: Idéfix
Industrial automation is my favorite area for OSGi technology. I just like embedded programming and Java. Unfortunately, consumer electronics companies have an extreme focus on the BOM (bill of materials) and tend to ignore the higher software development cost in languages such as C and C++. This often means no Java, or in the best case, a crippled Java (CLDC for connoisseurs), because of VM license costs and the additional CPU and memory required. This problem is more or less absent in the industrial automation market because the lower volumes make the software cost more critical.
As an example, let’s say that a company produces an industrial measuring device, called Idéfix, that sells for about a half-million euros. (This is a hypothetical device but the information is collected from actual projects that cannot be published because of nondisclosure agreements.) Idéfix has a large number of hardware extensions to feed it with samples from transport lines, position the samples correctly, inject special fluids, handle disposal, and perform a range of complex measurements requiring extreme accuracy.
Obviously, such a device is not sold in large quantities. Most sales will require extensive customization to make the devices fit into an existing environment. A software architecture for these devices must be very flexible and focused on reducing the cost of making (and maintaining) adaptations.
While adaptations are necessary to make a sale in this market, another key driver for Idéfix is remote management. World trade has exploded, and devices such as Idéfix are used all over the world, sometimes in uninhabitable places. The low volume of sales makes it hard to have technicians close enough to the customer to make visits when something goes wrong. This makes reliability important because machines will fail. The ability to perform remote diagnostics and software updates is therefore paramount.
Idéfix was developed over the past few years in conjunction with a consultancy company that brought up the idea of using Java with the OSGi specifications as the base. The OSGi specifications are an excellent match to the requirements for devices such as Idéfix. The specifications provide a dynamic component model that simplifies the hard problems of standard customer adaptations, extensiable hardware architecture, remote management, and myriad complex measurement algorithms. This is useful when you need some form of pluggability. And who doesn’t today?
Though Java can be used for the realtime aspects of the device, the Idéfix architecture separates the hard realtime code from the main application code. These hard realtime aspects are provided by auxiliary processors, sometimes embedded in the hardware extension itself, sometimes provided with plug-in cards. The reason for separating the hard realtime tasks from the main control software was to make the largest part of the software less complex and critical. This choice put the Java VM in control but not in a realtime-sensitive way, simplifying the overall design.
Loading Device Drivers
The designers of Idéfix used 1-wire devices to identify the extensions. This is a simple protocol that can run over the same wire as the power line, allowing very cheap and simple networks of (tiny) 1-wire devices connected over a ground wire and a power/protocol wire. Each 1-wire device has a unique 64-bit address that can be discovered through the 1-wire protocol. Once the extension is properly connected, the unique code in the 1-wire device can be used to identify the type of extension just seated. This is actually an example for which the OSGi device-access specification was designed. This specification elucidates the OSGi dynamic service model rather well. Let’s see how this works in detail.
The 1-wire bus requires a controller that detects the presence of 1-wire devices on the network. In an OSGi system, this controller is programmed and managed through a base driver, which is a bundle in itself. When the base driver detects a new 1-wire device, it registers a service that represents the detected 1-wire device with the OSGi service registry. An OSGi service is a POJO, together with a set of properties that provide information about the service. In this case, one property describes the device category, which is 1-wire (other types could be USB, Firewire, RFID, Ethernet, ESB, etc.). The other property is a unique ID, in this case the 1-wire unique code that is embedded in all the 1-wire chips.
The device-access subsystem listens to the registrations of services with these specific device properties and will receive a notification from the OSGi framework when such a service is published. It will use the properties to consult a database of bundles through one or more driver locator services, which can be implemented with a simple file lookup, local database lookup, or by consulting a remote management system, whichever is suitable. The device locator service returns a list of matching driver locations (URLs). The device-access subsystem will load these bundles in the framework if they are not already present.
Each of these driver bundles registers a driver service when started. This driver service is used to rate suitability of the driver; the best one is then picked by a driver selector service. This winning driver is subsequently used to attach the device service. The driver will then normally refine this device service by registering a new service that reflects the actual functionality of the connected device. The process is shown in figure 1, in which circles are bundles and triangles are services. Registering is connecting to the sharp point of the triangle (service), getting is connecting to the straight side, and listening is connecting to the angled side. The actions take place according to the sequence numbers.
For example, a device with the following 1-wire ID:
can thus be refined to a sample tray with a five-centimeter diameter. If the 1-wire base driver detects that the device is unseated, the appropriate device service is immediately unregistered. The device-access bundle will be notified about the unregistration and will cause the sample-tray service to be unregistered.
This example shows how decoupled these bundles really are. The 1-wire receiver base driver has no knowledge of the standardized device-access bundle, nor has it any knowledge of the sample-tray service. The device-access bundle knows about the device properties but has no clue about the base driver for the 1-wire controller, nor does it know any specifics about sample trays. All that these three components know about is services that come and go. For the sample tray it does not make a difference if the 1-wire controller base driver bundle is stopped for a software update or if the sample tray is removed. Both events are treated identically. This pattern of decoupling through the service registry is a key OSGi design pattern that has many applications. It provides a robust model in the light of failures and/or dynamic changes to the environment, something that traditional environments can handle only with complex and specialized code.
Installing a device-driver bundle sounds straightforward, but in practice a large part of the OSGi specifications is taken up by the module layer, which is responsible for managing the bundles in the VM. The key problem is that these bundles share classes and resources. That is, one bundle can import classes from another bundle. As we first find out in kindergarten, sharing is hard! In a computing environment, sharing is even harder when things are not only coming but also going.
Modularization for Java
Sharing classes between bundles is handled through OSGi metadata contained in a manifest that is part of the bundle’s ZIP file. A manifest is a list of header names and associated values. To understand how the sharing works, you first need to understand Java packages. A Java package is a directory with related classes and resources. Classes can have the same name in a different package. In OSGi systems, packages are exported to other bundles, imported from other bundles, or completely hidden from other bundles. Each exported package has a version; imports can take place on a range of versions. Being able to hide packages from other bundles has the advantage that the classes in these packages can be modified in future releases without impacting any client code. This is fundamentally different from plain Java where there is no import, no export, and no hiding of classes, nor are there any versions that guide a resolving process.
In an OSGi system, each bundle has its own class space, which is a consistent set of classes. Consistency means that classes with the same name are identical and cannot reach classes that are not consistent. Different bundles can belong to different class spaces, however, allowing different version of the same application or library to reside in the same VM simultaneously. The OSGi framework automatically separates bundles in different class spaces so that they do not collide.
Resolving the imports to the exports while managing the class spaces is a very hard problem, with no guarantee of a solution in each possible case. The resolving algorithm is one of the few areas in the OSGi specifications where the implementers have significant freedom to allow innovative solutions that optimize differently. Fortunately, the bundle developers are completely shielded from this really hard problem. All this complexity is hidden behind an easy-to-use API. Therefore, in our example, the driver bundle has only to provide an URL for which the OSGi framework can find the appropriate bundles; the bundle can then reside on the file system, a Web server, a database, or any location with a Java URL.
Bundles are ZIP files and can therefore contain more than just Java code. Combine this with a well-defined life cycle that is introspective and generates events at important times, and you have a system to deliver content other than just Java code to a device. Take, for example, the code that needs to run on the auxiliary processors in Idéfix. The task of installing this code could be left to the device driver, but it would be very similar code for all different devices. Also, for reliability reasons, it would be better if another bundle managed this code for all the auxiliary processors. Therefore, the Idéfix device has a bundle that looks in a specific directory in a bundle when it gets installed (notifications are sent out for all life-cycle events). If this directory contains code images for the auxiliary processor, this bundle will automatically install it. If the bundle gets updated, the image on the auxiliary processor will be updated. If the bundle gets uninstalled, the code will be directly removed from the auxiliary processor. Matching the (managed) life cycle of a bundle to something else is so popular that it has gotten its own name: extenders. Extenders are used to register help resources, install database tables, provide Web services, and much more.
Service-Oriented Business Models
The clear trend in industries that make complex products is to move away from selling products and instead to focus on selling services—that is, real-life services or functions. For example, GE stopped selling engines (products) to airlines but instead today sells them thrust (a service). Another example is Heidelberg, a company that manufactures printing presses (products) but now gets paid by copy printed (a service). The business service model makes the customers more agile because they require fewer different skills, and it places the responsibility and expertise in the hands of the manufacturer, allowing better optimizations.
The Idéfix product was intended to be used in a service-based business environment. Clearly, the bundle model with its remote management is a good match because the devices will end up all over the world. A strong requirement for this business service model to succeed, however, is that the devices installed on the customer’s premises are secure and tamper proof. If the customer could reset counters or use the devices in an unauthorized way, the manufacturer would lose money. The security model is therefore paramount.
The OSGi security model starts with the bundles. Bundles can be signed using a public-/private-key scheme for authentication using X.509 certificates. Though we had to set a few extra rules, this is basically the model provided by Java. A key problem in the Java security model, however, is configuration management, because the model has such a fine granularity. Trying to manage Java security is so hard that many systems end up having just two static levels: no security or all permissions.
OSGi simplified security management by providing a clear model based on the bundles. Each bundle has a set of permissions that can be changed on the fly with the conditional permission admin service. Additionally, each bundle can contain a permission file that further restricts the permissions that the bundle gets from the environment. The idea is that signers can verify the security scope of that bundle before they sign. This model simplifies security handling, though the management of the security permissions remains a complex problem.
OSGi and Open Source
Another advantage that made Idéfix easy to sell to the software developers is Eclipse, a free open source development environment. The developers at Eclipse had originally created their own plug-in model, but in 2003 they were looking for a more dynamic model. After evaluating several modularity systems, OSGi technology came out on top (OK, it might have helped that I was part of the team). The Eclipse team then did an amazing job of open-heart surgery by removing the old plug-in engine, replacing it with a brand new framework compatible with OSGi, and creating a compatibility layer that made all existing plug-ins compatible with the new system. To top it off, Eclipse provided an excellent environment in which to develop bundles. Though it is not a key requirement, there is definitely synergy if bundles for a device can be developed and tested in the same environment as your desktop. This is a key advantage of OSGi and why we sometimes call it universal middleware. Properly designed bundles run almost anywhere.
Idéfix’s architecture allowed for a lot of use of open source projects. For the OSGi framework, Idéfix chose Apache Felix, one of the key open source OSGi implementations. It also used bundles from Knopflerfish (an open source project with commercial backing) and Equinox (the Eclipse OSGi implementation). These projects implement most of the service specifications of OSGi release 4. Idéfix could also have chosen from one of the commercial implementations available from companies such as Makewave, ProSyst, IBM, and others.
Unfortunately, not all Java-based open source projects provide the right metadata for OSGi frameworks, even though the necessary headers are quite easy to add when a project is built with make, ant, maven, or the bnd tool. These headers do not cause any overhead or problems when the JARs (Java archive files) are used without OSGi. We hope that the increasing adoption rate of OSGi in our industry will help improve this situation dramatically in the next year. In the meantime, there are two projects, one in Eclipse and one in Apache Felix, that provide bundle-ized JARs for many open source software projects.
Application Programming Models
One of the great software insights of the past decade is the concept of a POJO. POJO-style programming means that we write Java code that is coupled to the Java environment but not to a specific container. We have learned the hard way that coupling our application logic to a framework is a bad idea because it locks us in. We always need some code that bridges our container with the POJO, however. This code injects any dependencies into the POJO so it can remain oblivious to where those dependencies come from. Though the OSGi framework is lightweight, it is also some kind of container and the same rules therefore apply.
Several application frameworks based on dependency injection have been developed to allow POJO programming in an OSGi environment. These frameworks hide the complexity of the dynamic dependencies, add extra layers such as transactions and remoting transparently, and simplify programming of the application logic.
The OSGi Alliance specified the declarative services application model for the release 4 service platform. The declarative services model is a small but effective dependency injection model based on the OSGi service model. Apache Felix took an interesting approach with iPOJO. This model reduces many programming chores by analyzing the bytecodes and inferring the intentions for many common tasks.
The best-known application programming model for OSGi is Spring. SpringSource, the Spring guardian, extended the Spring bean configuration model to connect to the dynamic OSGi services. This turned out to work extremely well because Spring and OSGi had little overlap and were extremely complementary. Spring-OSGi is highly popular in the enterprise software market at the moment. In the near future, the SCA (Service Component Architecture), an effort from OASIS with all the major players in the enterprise software market, will map SCA to OSGi, a promising combination.
Five different programming models, and counting, is quite a lot for a technology such as OSGi, because the collaboration among these models could be severely impaired. All these application models, however, use the OSGi service registry at the base. Through the registry, a Spring-OSGi application works seamlessly with an iPOJO application or a declarative services application. Developers can therefore choose which model provides the best solution for them but still reuse components written in other application models.
The future of the OSGi technology looks pretty rosy. It is a mature standard, proven in many markets, has many implementations, and has shown to scale well. It provides the foundation for a true universal middleware standard that could have a huge impact in our industry, if we can explain the technology to a wider audience.
We have done the groundwork, but we are far from finished. We need more companies that are willing to take the standard in different directions so we can get closer to fulfilling the promises of the Lego hypothesis: a model where components are wired together to create advanced applications. On that day, I can finally say that my original pitch to my manager has come true.
PETER KRIENS is the OSGi director of technology and CEO of aQute. He has worked as consultant for a large number of international companies introducing object-oriented techniques. In 1994 he moved to Sweden to work for Ericsson. In 2001, he was hired part-time by OSGi to act as its technical director. His responsibilities include editing the specifications and serving as the OSGi evangelist.
Originally published in Queue vol. 6, no. 1—
see this item in the ACM Digital Library