days
0
5
hours
1
7
minutes
0
2
seconds
2
1
Part 1 - The story of OSGi enRoute

Introducing enRoute 1.0 – the new OSGi Framework

Peter Kriens
OSGi
© OSGi Alliance

Version 1.0 of OSGi enRoute framework was launched in September 2015. enRoute inventor and OSGi mastermind Peter Kriens explains the project in a two-part article.

OSGi enRoute’s goal is to make it easy to get started with OSGi using best practices. It is an API, a toolchain (bnd, Bndtools, Gradle, Git,and Travis), and a distro based on open source bundles to quickly build OSGi (web) applications for different target environments. It is an excellent environment to learn how OSGi is intended to be used. The best way to get your hands dirty is the OSGi enRoute Quick Start tutorial.

The following sections provide details about what OSGi enRoute offers today and our planned enhancements, as well as an excellent introduction to OSGi best practices.

Background

When Ericsson sent me to Raleigh NC in November 1998 I had no idea what I was getting myself into. Open source was in its infancy, embedded meant 256K, WiFi was a glimmer in someone’s eyes, Mark Zuckerberg was only 14 and only mean to girls in his high school, Apple introduced the iMac, Windows 95 was dominant, command lines were on their way out, and a ‘cloud design’ was only used in a derogatory sense. And something almost unimaginable for the youngest readers: there were no smartphones!

That day in Raleigh we got together with Ericsson, IBM, Sun and other major companies to talk about standards for Home Gateways. Small computers that were soon (!) going to be in everybody’s home, operated by the telcos. At least that is what they told us.

The rather violent dot com implosion in 2001 (right at the time that OSGi v1 was released, though I do not think we were to blame), basically removed the motivation for continuing to develop these specifications. However, the OSGi Alliance founders had discovered other usages for the specifications and still wanted to continue the technical work because of its quality. The OSGi specifications have rarely taken shortcuts. In almost all situations we chose to do it right; unlike some other specifications that shall not be named. So we went on and released version 2 and 3 over the following years.

Some people will now indicate that the success of OSGi in the enterprise world was mixed. Though all the major application servers adopted OSGi, the bundle never replaced the WAR. There are reasons for this. Open source definitely destructed the economic landscape that the founders of OSGi were used to; which changed their incentives. It took time to embrace open source and collaborate with them, and even today specifications and open source do not seem to be completely comfortable with each other.

There was also a technical reason. The fact that the specification still exists today is largely because we were too far ahead when we started. (Several of us came from research, not development.) To show exactly how close we were to the edge: At Ericsson Research we got Java and OSGi’s predecessor running on an 8Mb Linux box. Cool, except for this small detail: there was no space for the applications. Same happened as far as dynamics, modularity, and services were concerned. Very cool but uncomfortably far out for developers in the trenches at the time.

But the most important reason was probably an impedance mismatch. When OSGi wandered into the enterprise space it bumped right into Java Enterprise Edition (EE). While Java EE had lots of domain APIs which OSGi lacked, it did not have two important architectural concepts that OSGi actually did have. We thought it was a match made in heaven.

The obvious concept that OSGi could provide was modularity. OSGi provided strong encapsulation and control of the class path. Java EE applications generally ended up on long and winding class paths that were utterly unprotected, hard to get right, and mostly brittle. OSGi bundles provide solid modules that are properly fenced off and enough metadata to verify consistency before anything runs. This part is loved by almost all Java developers when they first learn of it. Even fervent OSGi haters have to admit that the class loader model is outstanding.

However, the disadvantage of strong modules is that they are, eh, strong. Using the modules in classic Java environments exposed the fact that most applications were (ab)using class loaders to create their own extension/wiring mechanisms. (And Java already had a large number of class loader hack variations built-in for this purpose.)

SEE ALSO: OSGi enRoute – a new framework for OSGi applications

A bit of background about this. Object-oriented technologies became prevalent in the early 90s. They were initially highly successful, though the promise of reuse was not really materialized due to coupling. In real life systems almost any object was transitively reachable from any other object. Java’s main innovation, interfaces, changed this because interfaces broke the coupling between the implementer and the client. Now even though they were not dependent on each other, they could still be used separately in a type safe way. The interface was only the contract.

The interface concept was a highly needed and very desirable solution but it did create a new problem. Since the implementation and client were no longer coupled, someone had to wire them together in runtime. The client could not just ‘new’ the implementation class because this would recreate the coupling. This created a cotton industry of solutions in Java based on class loaders. First there were properties, then we started looking for class name patterns, then we got XML with Spring, and one day Java 6 offered the service loader and I guess today we have CDI with annotations.

Unfortunately, all these tricks were not very modular as OSGi was telling people with Class Not Found Exceptions. If someone is telling you that your baby is ugly then it is a lot more comfortable to blame the internet than to modify your code to use a proper solution. The fact that modularizing your application is hard work is clearly not well understood. Too often OSGi is unjustly blamed for this.

As I said earlier, OSGi provided two missing pieces to the Java EE architecture. The second part was the OSGi service model. This service model was designed to solve the runtime wiring problem in a truly modular way. Truly because it created a peer-to-peer broker model. Services are offered by the implementers and are bound to the clients when they request such a service. However, services were even less understood than the modular story and thus ignored. The fact that services were dynamic was unfortunately not recognized as an asset but a seen as a potential risk.

Working with many of the major players we wrote specifications that mapped popular Java EE specifications to OSGi. This made us run into the problem that these specifications were a bit awkward. Things that would translate to a very simple Service API had to be done in a way that just looked not completely right in OSGi. There is clearly an impedance mismatch.

So we had a very good model with modules and services but in practice the step was too big for most enterprise developers to make. The result was that Java EE developed many APIs that were just not very cohesive and did not properly support modularity.

Here is an example. OSGi specifications strictly separate configuration and API. It turns out that the API needed to collaborate is actually quite small in most cases. However, the configuration needed to set each side up can be quite extensive. Let’s say you need to talk to a queue. In a classic app the producer and consumer need to agree on a name and somehow tell the queue manager what name to use. In OSGi you can use a service that is anonymously used by both parties; all the details are in the configuration. Since configuration management is standardized, this tends to work very well in OSGi. It still frequently amazes me how this can simplify modules and services while at the same time making each of them much more reusable.

In 2012 I decided to take a sabbatical. After editing the specifications for so many years while so many things were changing in the field I felt the need to go back to the trenches. I missed the deep knowledge that you get when you actually use a technology instead of just reading. What I also missed was a bit of playing during the weekend.

So during my sabbatical I decided to build a website: jpm4j.org. I took the OSGi Extreme approach. Everything had to be properly service-based and leverage OSGi to the hilt. To be honest, I wanted to prove to myself that I had not been talking nonsense during all those conferences where I said that OSGi is an awesome development environment.

The message was mixed. I definitely proved to myself that OSGi is awesome, especially the service model! (Which I guess the web-based microservices are helping to prove now as well since that model is based on the same concept; OSGi services are just magnitudes lighter weight).

However, I found that a number of specifications were missing which I had to develop from scratch. There were also many other bundles that did not seem to be available anywhere

I also found that building up a tool chain and runtime environment was not trivial. It definitely made me understand the challenges people faced when they wanted to get started with OSGi.

The OSGi Alliance had been discussing ways in which they could improve the developer experience for a long time. So when I shared my experiences with them it was agreed to put together OSGi enRoute, with the goal of making it easier to get started with OSGi.

OSGi enRoute

Today there is an incredibly elegant programming model in OSGi and amazing tooling available but unfortunately when you search the web you are confronted with a graveyard of dead tutorials and stale blogs. And there is a scary amount of bad advice out there. The intent of OSGi enRoute is to make it really easy to get started with OSGi in the right way.

In the following sections we describe OSGi enRoute from the different aspects that it encompasses.

Components

The OSGi enRoute programming model is based on components. Ok, that is a very generic word. A component is in this context a Declarative Services component.

In practice this means that a component is a plain old Java object with some annotations on it. At first sight these annotations resemble CDI and they do have a similar purpose. However, OSGi services embrace dynamic dependencies and have built-in configuration support. It turns out that these two dimensions make a lot of patterns in software easier to implement than working in a statically wired world.

Here is an example. A simple component that provides a Speaker service, gets configured through Configuration Admin, and (dynamically) depends on the Event Admin service, could look like this:

@Component
	public class MyComponent implements Speaker {

		@Reference
		EventAdmin	eventAdmin;

		interface @Config {
			int port();
			String host();
		}

		@Activate
		void start(Config config) {
		}
	}

Components are quite liberating because they do not require central registration. You want to start a listener on a socket? Just create an immediate component and you can start your server. Need to wait until some important services are ready? Just add a reference to these services.

Components are cheap, almost as cheap as objects. There are systems that have hundreds of thousands of components. If you’ve never used services then these new dimensions that are provided by OSGi components are hard to explain.You’ll have to trust me that they are really worth many times their small cost.

Components are the principal part of a design in OSGi enRoute. They abstract some part of the problem domain and communicate with other components through services. Components often have a set of associated objects that act as helpers which are developed with standard Java mechanisms. In practice, a significant number of these classes can stay package private.

Services

Components, in general, register a service. A service is named by its interface and has a number of properties that describe it. However, don’t confuse a service with only an object with an interface. A service has a contract. In OSGi, that contract is specified in a package. We need a package because in almost all cases there is a need for helper objects and additional interfaces although good contracts try to limit the number by keeping the packages highly cohesive.

Services have been part of OSGi since 1998. A service is beneficial because it concentrates the coupling between different parties on a single point with a set of well defined operations. Both sides of the fence can evolve independently as long as their contract is obeyed. It is hard to overestimate the benefit of this indirection for larger systems since complexity tends to be exponential to the number of connections a module has. Service contracts do for modules what interfaces did for classes: Solve the transitive coupling problem.

Service Oriented Architecture’s were a big trend some years ago; today microservices are the new fashion. These services are web services; they require a communication protocol and are mute about a lot of important things like for example the lifecycle of the service. In contrast OSGi µservices are extremely lightweight, fully dynamic, and can be fully lifecycle aware. Though web services, microservices, and µservices share the same benefits of the service model, it is only the OSGi model that can be transparently mapped to web or microservices.

Services are the architectural primitive of OSGi designs. The service diagram provides a very high level overview of an architecture, it is almost an architecture reified. To document designs, we developed over time the following diagramming technique:

Distributed OSGi takes the service model and makes it possible to call services in other frameworks. A topology manager discovers services in other frameworks and then based on its policy registers a proxy to the remote service in the local registry. Since services are dynamic, the topology manager can also unregister these services when it loses the connection to the remote framework. This is almost completely transparent. However, it does require that services are designed with distribution in mind. Before distributed OSGi was available, all OSGi service designs used properly encapsulated objects. However, those very object oriented objects are quite difficult to deserialize/unmarshal since the implementation classes are private. (Rule 1 of modularity, hide implementations.) Since distributed OSGi became a major force services have been designed with serialization in mind. The most popular pattern today is to use DTOs in the API. DTOs are objects with public fields that use a small set of Java types, including other DTOs. DTOs are a corner stone of OSGi enRoute.

1

Services are reified in runtime. As shown with the Declarative Services example it is trivial to use services to implement software patterns. One of the most popular patterns is the white board pattern, which is a corner stone of all modern OSGi service APIs. In classic Java it is common to get some server object and then register a listener to that object. For example in Android you must first get the Location Manager and then register a Location Listener to get the GPS positions. And if you are a clean programmer, you should of course also cleanup when your code is deactivated.

We initially copied this listener pattern in the OSGi Http Service and Log Service but soon realized that just registering the Location Listener in the service registry would suffice. An anonymous Location Manager could then easily discover those Location Listener services. Using this pattern often halves the number of types in a service contract.

Reifying the services also gives another huge advantage: visualization. Since the services are the joints of an application, a visualization of the services in a framework provides a very high level overview of the application’s architecture. OSGi enRoute contains the XRay Apache Felix Web Console plugin to show the services in the framework.

2

Dynamism

The earlier example component was fully dynamic. It gets activated when the Event Admin service is registered and is deactivated when it goes away. As you can see, the dynamics do not cost anything since they are not visible in most cases. That said, because the component is dynamic we can update the configuration anytime and do not have to worry about the initialization sequence since this is handled with the same dynamic mechanism.

Dynamism is crucial in an IoT world where the software interacts with the real world. Dynamism seems not that important in the enterprise world since in a single computer process everything lives and dies together. (Then again, in OSGi there is no extra cost for the dynamics.) However, dynamics are crucial in a distributed world. Most enterprise software goes out of its way to ignore these dynamics generally resulting in brittle systems. OSGi embraces failure because it embraced the dynamics from the beginning. For example, imagine you have a service that is implemented on a number of other systems but you want to return the result of the fastest invocation.

@Component(property=“service.ranking=1000”)
  public class MyComponent implements Foo {

    @Reference
    volatile List<Foo>	foos=
      new CopyOnWriteArrayList<>();

    @Override
    Promise<String> foo(int n) {
      Deferred<String> deferred = new Deferred();
      AtomicBoolean		first = new AtomicBoolean(false);

      for ( Foo foo : foos ) {
        if ( foo == this )
          continue;

        foo.foo(n).then( 
          (p)-> {
		  if (first.getAndSet(true)==false)
              deferred.resolve( p.getValue() );
            return null;
		});
       }
       return deferred.getPromise();
	}

This surprisingly small piece of code is actually complete. The foos list is automatically provisioned with the matching services from the OSGi service registry by the Declarative Services runtime. So whenever the payload method is called, we try the latest set of services that are visible.

Packaging

Components cannot be directly deployed in OSGi, they need to be packaged in bundles. Bundles are the modules of OSGi. Bundles contain Java packages (basically folders in a JAR/ZIP file). The manifest in this JAR file describes what packages from that bundle are exported and what packages are imported. Packages not listed are private; they are not directly accessible by other bundles.

There is only one mandatory header in OSGi but over time we’ve added additional manifest headers to provide more metadata. These are useful in some cases but they are all optional. We’ve tried to make the bundles as self describing as possible in a single ZIP file.

Bundles are reified in runtime. Over time this gave rise to the extender pattern. Since bundles can be discovered it is possible to look into a bundle to see if you recognize something. If so, you can take data from the bundle and do your thing. The intent of this pattern is to allow bundles to only provide the minimum and leave the boilerplate to another bundle. Since the observer can observe the complete lifecycle, whatever functionality it creates on behalf of that bundle, it can properly tear it down or uninstall it.

The archetypical example of the extender board pattern is Declarative Services. The runtime of Declarative Services listens to bundles that become active. It then checks if they have any Declarative Service components by looking at the manifest. If so, they read the corresponding XML and orchestrate the component accordingly. If the bundle is stopped, all components are deactivated.

Another example is the Configurator provided by OSGi enRoute. This is an extender that reads configuration data from bundles. Just activating a bundle will make the Configurator read the configuration data from the /configuration folder in the bundle’s JAR. This configuration is then installed via the Configuration Admin service.

There is even a special Bundle Tracker utility to simplify using this pattern.

Capabilities & Requirements

After we started with an initial dependency model based on packages only, some parties (that shall not be named here) more or less forced us to add bundles and fragments as additional dependency types. Let’s call these package, bundle, fragments semantic dependencies. Around 2005 we started to develop a repository model where these semantic dependencies had to be well understood to provide the proper artifacts on a request. Using these semantic dependencies felt very awkward because we also needed to model additional dependencies like the version of the VM, the operating system, and the hardware. Thinking further, it became quite clear that the things we wanted to depend on were basically open ended.

We therefore developed a language to represent capabilities and requirements. The language is powerful and surprisingly simple. A capability consists of a namespace that defines the type of the capability and a set of properties (key value pairs). For example:

	namespace  = ‘osgi.wiring.package’  
		{ osgi.wiring.package=com.example.foo,version=1.2.3 }

A requirement consists of a namespace and an OSGi filter expression. For example:

	namespace  = ‘osgi.wiring.package’  
		(osgi.wiring.package=com.example.foo)

Though the language to express capabilities and requirements looks very simple, it is surprisingly powerful. Mostly because OSGi filters are quite complete; they can have any depth of subexpressions and support wildcards and magnitude comparisons.

This work on repositories was fed back into the core specifications and today all the OSGi framework implementations are based on this Capability model. Using the capabilities and requirements the frameworks wire the bundles together; the wires are reified in the API so they can be used in runtime to find out what a bundle a is wired to.

For example, web resources are bundles that contain resources like javascript, css, html, images, etc. OSGi enRoute provides a web resource namespace. This allows a web resource bundle to provide a capability that can then be required by the application bundle. The web resources are then included in the runtime so that we always have the proper version installed.

In OSGi enRoute the Capability model is one of the architectural corner stones. An OSGi enRoute bundle should fully describe the capabilities it provides and requires.

To make this as easy and natural for the Java developer as possible we’ve developed a lot of support in bnd. This tool analyzes the byte codes to automatically create the requirements on the JVM and the imported packages. We’ve also added support in bnd for creating requirements with annotations.

For example, a specific web resource can design an annotation that requires a version or version range of that web resource. This allows the Java developer that relies on that web resource to annotate a class with a simple annotation and be assured that its bundle has the proper requirement. An annotation to require the Bootstrap CSS web resource will automatically add the necessary Require-Capability header in the bundle’s manifest.

	@RequireBootstrapWebResource(resource = “css/bootstrap.css")

Deployment

There two types of deployments. The most common type, which is kind of exclusive to the Java world, is the classic WAR model. The application is intended to be deployed in a Java EE Application server that will cherish and nurture the application. The application WAR can rely on the environment to provide it with a very rich and extensive API but must bring any additional dependencies.

The other deploy type is the executable JAR where the only dependency is a proper JVM. In that case the JAR must contain all the dependencies, including an OSGi Framework. Since the trend is towards more self contained units (see for example Docker), OSGi enRoute primarily focuses on the executable JAR. However, we will soon provide exports to most popular application servers that support OSGi.

In a component system the focus is to make the components as reusable, and thus uncoupled, as possible. Java developers intuitively feel this in their guts whenever they use interfaces instead of classes; life is so much easier when you can easily mock classes and use your code in different applications. Decoupling is liberating.

Couplings are, however, like balloons. You can press the air out of one place only for it to go somewhere else. Very uncoupled components shift part of the problem to the assembly phase where a set of components must be selected to form the application. This is a really hard problem since it must ensure that each component is satisfied in the runtime and will not have its assumptions violated.

For example, maven uses transitive dependencies to calculate a class path. However, transitive dependencies can (and usually do) have multiple versions. Ordering in such class paths can make a difference in runtime behavior. Worst of all, the only dependency type maven uses is the module name. This mostly puts the onus on the developers to understand what they actually get on their class path. With todays large reuse of software this can mean understanding the implications of hundreds to thousands of components.

Since we have a fine grained capability model in OSGi, we can actually largely automate this process. Given a set of initial requirements, a resolver can calculate the minimal set of bundles needed to satisfy the aggregated requirements of that set.

The second part of the article will be published on our portal soon. Stay tuned!

Author
Peter Kriens
Peter Kriens is an independent consultant since 1990.He currently works for the OSGi Allianceand jpm4j. During the eighties he developed advanced distributed systems for newspapers based on microcomputers based on, at the time very novel, object oriented technologies. For this experience in Objects he was hired by a number of international companies, including Adobe, Intel, Ericsson, IBM, and many others. During his work at Ericsson Research in 1998 he got involved with the OSGi specification; Later he became the primary editor for these specifications. In 2005 he was awarded the OSGi Fellows title. After taking a sabbatical in 2012 to develop jpm4j he returned to the OSGi Alliance to help increasing adoption. He is Dutch but decided to live in France.

Comments
comments powered by Disqus