Book Image

Instant OSGi Starter

Book Image

Instant OSGi Starter

Overview of this book

OSGi is a tried and true modularity standard for Java. It has in recent years gained a lot of traction and tooling; becoming frequently used in Enterprise containers and distributed software systems. "Instant OSGi Starter" is where you should start before beginning your first OSGi based project. You'll be exposed to the core concepts, gain practical experience with the most important features, and learn about the basic tenets of modular code practices. This book begins with the fundamental tools needed for building modular applications, top features with basic tenets of modular core practices and provides useful insights into resources and the community.As this book progresses you will be able to get started programming in OSGi whilst looking at the default three layer design so the application will be architected towards modularity and simplicity.Through exploring several tools and technologies and browsing the communities you will be able to work towards modular programming in OSGi.
Table of Contents (7 chapters)

Quick start – your first OSGi bundles


OSGi bundles are the cornerstones of a modular OSGi application. In this section we'll guide you through building your first example application. This application consists of a set of Apache Maven modules that'll generate, compile, and test your application code. The whole set of modules is prefabricated for your convenience so that you can spend time on analyzing and modifying the project to suit your specific needs.

All of the source code for the examples is located at GitHub, a free git hosting provider. The following screenshot shows the OSGi Starter GitHub project page. To build the sources you'll need git to integrate and download the source (you can download the sources as a ZIP file as well from https://github.com/seijoed/osgi-starter/zipball/master), and Apache Maven to actually compile the projects.

To obtain a copy of the code you may perform a git clone of the project.

The projects utilize Apache Maven and the Apache Felix plugins; this will provide us with quick and easy tooling integration, a single build command, as well as integrated testing of all of the projects.

Start by navigating to the GitHub URL containing the demonstration code (https://github.com/seijoed/osgi-starter), and then clone the sources (https://github.com/seijoed/osgi-starter.git) to a local directory. Make sure that you have Apache Maven set up correctly and on the PATH of your shell or command-line window.

Step 1 – what do I need?

For the OSGi tutorial you'll need all of the components downloaded in the previous section as well as Apache Maven and a Java JDK. Once you have downloaded the project you'll have a structure consisting of a consumer, producer, itest, and parent project.

The parent project is used to allow for building properties as well as Apache Maven plugin inheritance; the itests contain the integration tests for the projects. This allows you to have a continuous integration cycle while writing your modules. It also prevents the necessity of needing to have a complete OSGi container running at all times during the development cycle.

To build the project invoke the following command:

%> mvn install

Each module will in turn be compiled with artifacts placed into your local Maven repository. Note that during the build you will observe unit tests being performed and reported upon; more on this will be discussed shortly. The following screenshot shows a successful build of the application:

Step 2 – analyzing the producer

The producer module contains a few key components, one of them being the Maven bundle plugin (illustrated in the following screenshot) that exports the packages containing the interfaces and hides the implementation of exported API components. This allows for a complete separation of concerns.

The plugin will build the META-INF/MANIFEST.MF file for us (thereby saving us from having to manually populate the bundle headers that make the produced JAR an OSGi bundle), exporting all classes in the package com.packt.osgi.starter.producer while hiding everything in the impl package. We do this in order to hide from the unnecessary framework packages, otherwise we'd have to import what we are exporting. The impl package also contains Bundle-Activator. This activator is going to take part in the OSGi life cycle, implementing org.osgi.framework.BundleActivator that allows us to mark a class as executable by the OSGi framework. When the bundle transitions from resolution and has satisfied imports and exports as per the MANIFEST.MF file this class will be called from the framework and activated.

This file tells the OSGi container all it needs to know about the JAR; it specifies starting parameters, imports, what we have asked to export, and symbolic information containing build tool, builder, and versioning information. The following screenshot shows the BundleActivator interface for the producer bundle:

The Service registration for this bundle is one single line. Utilizing org.osgi.framework.BundleContext (the bundle's execution context) we register a service for our interface. It allows the bundle to interact with the environment interface. The context gives you access to the OSGi service registry as well as resolution of other bundles if so desired. There are several other interfaces besides BundleActivator that you can implement to get event, logging, container, and bundle information.

Tip

Apache Karaf command

Look through some of the APIs for org.osgi.framework.BundleContext and you'll see what you can do with a running bundle!

To quickly install and start the producer bundle in Karaf, issue the osgi:install command (bundle:install –s on Karaf 3.x). The short hand install –s tells Karaf to resolve and start the bundle. Note that the same command will work on Linux, Unix, or Windows (no additional configuration required) as follows:

karaf@root> install –s mvn:com.packt.osgi.starter/1.0.0/producer

After the bundle has been deployed to Apache Karaf it will show up in the console as an Active and running bundle. We can see the bundle state, ID, name, and if it contains a Blueprint context.

Since we also had BundleActivator that registered a service, we can list the service registry to ensure that our service has been registered correctly. Our producer bundle is now activated; the service is registered and ready for subscription.

Congratulations, you have just deployed your first piece of modular software!

Step 3 – analyzing the consumer

The consumer is built from two Java classes and a deployment file for Aries Blueprint. Blueprint being an inversion of control and dependency injection framework for OSGi, Blueprint allows you to write simple Java beans, inject services and references. The following screenshot shows the relevant package structure for the Consumer in Maven format, showing classes and resources in the correct locations for a bundle deployment:

Once the blueprint file is deployed, a blueprint extender will parse it, and your bundle will be handed a recipe to deploy that contains the correct wiring. We utilize it here to consume the service from the producer; the blueprint container will handle the service subscription and related error handling for us. Aries Blueprint instantiates a blueprint container for us. According to the blueprint.xml configuration file, it also will take the reference, that is our service subscription, and inject it as a field in SimpleResponseConsumer.

The consumer class contains a simple java.util.Timer that will utilize the service on a schedule.

Tip

Apache Karaf command

To quickly install and start the consumer bundle in Karaf, issue the osgi:install command (bundle:install –s on Karaf 3.x) as follows:

karaf@root> install –s mvn:com.packt.osgi.starter/1.0.0/consumer

Once the consumer is deployed, it will start the bundle and then create a Blueprint container. Aries, being a set of bundles that provide OSGi development tools (we will discuss Blueprint more in depth in a later section), creates the Blueprint container. The container will activate our consumer class. The consumer then instantiates a timer and passes in a reference to the service into TimerTask (TimerTasks are used to schedule an action to occur once, or repeatedly).

Once this is activated, you'll see your console screen starting to fill up with service requests, as follows:

These requests are from a full life cycle project. We have our producer deployed, running, and providing a service. The consumer bundle is resolved, loaded, and instantiated by Apache Aries Blueprint; the proxy from the Blueprint framework consumes the service and our Java classes are now able to invoke methods until we stop execution.

Step 4 – testing

Since we are building this demo system OSGi, a technology built around runtime, we'll illustrate this in the testing phase with an integration test. Integration tests are natural extensions of regular unit tests where we also involve the necessary components to execute our code in the test bed. In the code examples, we use a framework called Pax Exam combined with Junit to execute, mark, and define our test suite. We also rely on a new plugin; this plugin is going to write out dependency information for us so that the Pax Exam execution environment can re-use this information and allow us to simplify our test configurations.

Note that you can configure the use of a specific version of the plugin using the tags <version></version.

We also add a set of dependencies in our Apache Maven pom file; these contain Pax Exam artifacts, an execution model as well as the projects we wish to test. Pax Exam allows us to annotate a configure model. In this model, we specify all of the various bundles that we need to load for a complete test cycle.

Loading these, we re-use the Apache Maven plugin; it enables us to let Pax Exam figure out the right dependency versions from the build environment. This will lead to less manual work when you are upgrading versions or modifying the tests.

We also configure a Junit runner and a Pax Exam reactor strategy for the execution of our test. Both of these are annotations that will provide test behavior; the first tells Junit how to run our tests and the second tells Pax Exam what execution strategy we want to utilize.

Once we run this test, either from the command line via an mvn test or from within an IDE, we will see Pax Exam starting an OSGi container for us, loading the necessary bundles, loading our bundles, and lastly our Junit test will be executed.

The test-code that does all of the heavy lifting is shown in the following screenshot:

This is a very simple little test suite that does quite a few things. We rely on a few niceties of Pax Exam, so we let Pax Exam inject our service for us. Then we make sure that the service is valid and not null, and lastly we actually exercise the service with a predictable request where we can anticipate the response. Having reached this far in the tests, we know that we have accomplished quite a few things; they are as follows:

  • Our bundles are correct with regards to imports and exports

  • We know that Aries Blueprint is loading our context correctly

  • We know that we are correctly registering a service

  • We know that we correctly and predictably can use the service

  • We know that our APIs are correctly implemented

Comparing the two tests in the test suite, the ProducerAndConsumerTest is vastly more complicated, primarily due to external dependencies since the consumer is using Apache Aries Blueprint. This helps to illustrate the point for modularity since we can clearly show that the more things we add to a bundle, the likelihood of us using more technologies will grow, sometimes exponentially.

Our test bundles, in all fairness, are not that complicated though, so it isn't much of an issue for this exercise, but the argument being made is that decoupled, simple, and predictable code is going to be far simpler to test. This will, in particular, hold true for systems that introduce concurrency and are needed to scale.

To illustrate what we really have deployed, we'll introduce the following UML diagram containing all of the existing deployments we have done against this source:

The preceding diagram is using OSGi UML symbols to describe the entire example project. Starting from the left, we have a Consumer bundle (represented using a component icon); it is utilizing Blueprint Container to import services from OSGi Service Registry (the stylistic details vary; however, imports are depicted as a receptacle, while exports are depicted as a matching shape to plug in to the import). Blueprint Container is an additional API component that has a slightly different life cycle from the native OSGi one, hence it is clearly broken out and illustrated as a separate container. The major life cycle difference is that since Blueprint Container is more or less custom code executing in a bundle, that bundle technically can be started with a failed Blueprint context.

At the other end, we have our OSGi native Producer bundle (also represented using a component icon). This bundle contains nothing but pure framework code, thus it exports to and communicates directly with OSGi Service Registry (shown in the following diagram). We have used both approaches in the examples. Blueprint is a dependency injection and inversion of control framework modeled closely to the Spring Framework. It is a development style that has become very popular and successful in the Java world as it allows you to quickly configure rather complex applications from very simple building blocks. If you are writing more container or framework-oriented code, it is likely that you'll look more at BundleActivators and pure OSGi code; the choice is left to the reader!

Note that the solid arrow represents invoking and the line arrow represents return in the preceding diagram.

Another common way of describing bundles and their tasks is to use sequence diagrams; these diagrams depict object interactions in a time sequence. Combining a sequence diagram with a high-level abstract UML diagram will provide you with ample documentation of what your bundles are actually doing. We are not intending to provide UML education, so see these diagrams as simplified documentation intended to be at a fairly high level, and describing exactly what we have in our bundles and deployable units.

We start from left to right; ProducerBundle is resolved, starts, and then contacts OSGi Service Registry to register a service. Once completed, it will wait until a service subscription request is initiated. The subscription request is performed by ConsumerBundle, this bundle will resolve dependencies, start, and the extender pattern (this pattern will be explained in a later section) from the blueprint provider will kick in and help us initiate the Blueprint context file. Once the Blueprint recipe is resolved and composed, it will instantiate our classes and communicate with the service registry to start its subscription. If the subscription is unavailable, by default, the Blueprint container will wait with a configurable timeout until a service is registered. This is one of the reasons that frameworks like Blueprint are popular; they allow you to focus on your business code instead of error handling and boilerplate code.