Blogs
Jason Tesser
Director of Engineering

OSGI Explained: Extending Your Software to Embed an OSGi Framework

Dec 12, 2017

By: Jason Tesser

You have your software in place, but you need a better way of allowing your technical users to extend the platform. Most Java developers are aware of OSGi, but embedding an OSGi framework into your product seems like a huge undertaking. Further clouding the path is the uncertainty of exactly how OSGi works. For many Java developers OSGi feels more like trying to navigate the open sea rather than something they would want to play with.

What is OSGi?

OSGi (Open Service Gateway Initiative) is a Java framework used for developing and deploying modules and components. It focuses on encapsulation and loose coupling of functions which gives many benefits to the developers, such as modular functionalities that are easily movable between source codes and testing which doesn’t involve endless dependencies.

How does OSGi work?

OSGi is a set of specifications that define a dynamic component system for Java. These specifications allow for a development model in which an application is composed of several components, and then packed into bundles. These components communicate locally and through the network via services.

The goal is to make the applications core code as small as possible. This code itself may be highly coupled to a number of components.

The components are the reusable building blocks for your application. For example shopping cart functionality for your e-commerce website, or a payroll application within your employee management software.

OSGi enables components to hide their implementations from other components whilst exposing only relevant data to other components via services. This stops components from accidentally modifying data they shouldn’t have access to, and also keeps dependencies under control.

In an ideal OSGi application, all links between components are done through services. Services have an API that is defined within a java package. The API consists of classes and/or interfaces that are needed for collaborations between the service providers and consumers.

A bundle is a package for all of the OSGi components, containing them and whichever resources they need to work. Bundles are explicit in their requirements on the environment and the capabilities that they can provide.

OSGi has a layered model as illustrated in the image below:

OSGI_graphic

OSGI Terminology Explained

  • OSGI Bundles – OSGI Bundles are the OSGi components made by the developers.
  • Services – The services layer connects bundles in a dynamic way by offering a publish-find-bind model for plain old Java objects.
  • Life-Cycle – The API to install, start, stop, update, and uninstall bundles.
  • Modules – The layer that defines how a bundle can import and export code.
  • Security – The layer that handles the security aspects.
  • Execution Environment – Defines what methods and classes are available in a specific platform.

Why Use OSGi within my Existing Software?

Some of the primary reasons to embed OSGi within your application is so that developers can:

  • Deploy functionality at runtime without having to stop the primary application
  • Run multiple versions of the same library, at the same time, within the same JVM
  • Separate and protect plugins from the core functionality and classpaths

Which Library to Use?

We, at dotcms, decided to use Apache Felix. The Equinox Eclipse project is also a solid option depending on what you are looking to do. Felix stays pretty close to the latest spec but both libraries have similar feature sets. In addition, Felix is better built to be embedded into an existing WAR. Both Equinox and Felix can do this but Equinox recommends, by default, embedding your server into Equinox, not the other way around.

eclipse screen grab

This is how Eclipse runs, which is what Equinox is primarily built for, so this makes sense.

If you use Jboss or Weblogic you can use the OSGi implementations they ship with, but we prefer to remain more agnostic and not rely on the underneath app server for something like OSGi.  As of the writing of this article, none of the other major implementations implement OSGi framework spec 6.

For more info see:

https://en.wikipedia.org/wiki/OSGi_Specification_Implementations

How to Embed OSGi

At dotCMS we were faced with the issue of needing to provide our users with the ability to deploy what we call Dynamic Plugins. A Dynamic Plugin is a plugin that can be deployed and undeployed at runtime, meaning without a server restart. The plugins needed to be able to override current functionality as well as add new functionality to our existing WAR. This involves adding Servlets, Spring Containers, Filters, Java Classes, REST Endpoints, etc.

In our case, we weren’t starting a new project where the entire app would be centered around OSGi (i.e. Eclipse), we already had an existing WAR on which we needed to add OSGi.

The great news is that OSGi is not difficult to embed. Essentially it boils down to the config and code example shown below.

First you need to add the library dependency to your project. This would be adding the JARs for either Apache Felix or Eclipse Equinox. Next you need something to start OSGi. Being a web application, we used a listener:

<listener>
  <listener-class>my.OSGiListener</listener-class>
</listener>

As an example consider the following:

import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.felix.framework.FrameworkFactory;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.launch.Framework;
public class OSGiListener implements ServletContextListener {

    private final String OSGi_LOAD_DIRECTORY = "/path/to/OSGi/bundles";

    @Override
    public void contextInitialized(ServletContextEvent servletContext) {

        Map<String, String> osgiConfig = new HashMap<>();
        osgiConfig.put(Constants.FRAMEWORK_STORAGE, "/path/to/OSGi/cache");
        osgiConfig.put(Constants.FRAMEWORK_STORAGE_CLEAN, "true");
        try {
            FrameworkFactory frameworkFactory = ServiceLoader.load(FrameworkFactory.class)
                    .iterator().next();
            Framework framework = frameworkFactory.newFramework(osgiConfig);
            framework.start();

            BundleContext bundleContext = framework.getBundleContext();
            BundleManager bundleManager = new BundleManager(bundleContext);
            bundleManager.load();
        } catch (Exception ex) {
            System.out.println("OSGi Failed to Start");
        }
    }
    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
        // stop framework
    }
    public class BundleManager {
        private BundleContext bundleContext = null;
        public BundleManager(BundleContext bundleContext) {
            this.bundleContext = bundleContext;
        }
        public void load() throws Exception {

            ArrayList<Bundle> availableBundles = new ArrayList<Bundle>();
            //get and open available bundles
            for (URL url : getBundles()) {
                Bundle bundle = bundleContext.installBundle(url.getFile(), url.openStream());
                availableBundles.add(bundle);
            }

            //start the bundles
            for (Bundle bundle : availableBundles) {
                try {
                    bundle.start();
                } catch (Exception ex) {
                    System.out.println("Failed to start bundle " + bundle.getSymbolicName());
                }
            }
        }

        private List<URL> getBundles() {
            List<URL> bundleURLs = new ArrayList<>();
            //load into bundleURLs the URLS for OSGi_LOAD_DIRECTORY
            return bundleURLs;
        }
    }
}

For more information see the following links:

http://felix.apache.org/documentation/subprojects/apache-felix-framework/apache-felix-framework-launching-and-embedding.html

http://www.eclipse.org/equinox/server/http_in_container.php

What We Learned

  • BND is your friend. BND http://bnd.bndtools.org is a library to help you build OSGi plugins.
  • The default Gradle plugin for BND stinks. I would recommend looking at this library https://plugins.gradle.org/plugin/biz.aQute.bnd for building BND plugins. I find Gradle good for OSGi plugins, just not its default plugin.
  • Keep OSGi libraries separate from your Web App underneath. When we first got into OSGi, we provided all the libraries we had on the classpath to OSGi. Felix has a mechanism for exporting the libraries to the OSGi bundles. In hindsight, we learned that it is best for bundles to provide their own libraries or to use services from other bundles. This keeps things clean and easier for development.

OSGi is a great way to allow developers to deploy new functionality in an existing web application while keeping it protected and separate from other things within the main application. Following the instructions above, and heeding our lessons learned, it is not difficult to embed OSGi within your own application.

Filed Under:

webinar

​On-Demand Demo

Get a complete demo of the dotCMS Content Management System. This webinar covers all the major features of the platform, including editing and managing content, creating pages, creating content types, building custom workflows and more.

Get On-Demand Demo

Recommended Reading

Exploring TimeMachine: How dotCMS Handles Content Versioning

With dotCMS, tools like TimeMachine and Workflow Management help enterprises control content versioning, content edits, user permissions, workflows and more.

Digital Ambition Requires Ambitious Technology

When it comes to the content foundation of a digital experience platform, critical capabilities are reasonably straightforward. There are only four, really - since security, scalability and containeri...

Optimizing On-page SEO in dotCMS

In this blog, I will show you how to use dotCMS to make your SEO life easier. We'll take a look at code examples of how to programmatically ensure best practices for some of your SEO tasks.

Scripting as a Service: A Look at dotCMS’s Scriptable API Builder

For years, developers have had to build custom REST endpoints to enable brands to deliver experiences to mobile devices, social channels and IoT devices. While this practice is perfectly valid, we hav...