Logo Computer scientist,
engineer, and educator
• Articles • Articles about computing • Articles about software development • Apache integration software

Deploying an OSGi application on Apache Fabric8, from the ground up

Apache Fabric8 is an OSGi-based platform designed to run Apache integration products (ActiveMQ, Camel, CXF) in a distributed environment. Fabric8 is broadly aligned with RedHat JBoss Fuse 6.1, and the demonstrations in this article will run on Fuse 6.1, with the few small changes that are mentioned.

The core of Fabric8 is a repository of software components, and a distributed configuration management system. The configuration management system keeps track of which software should be running on which host (specifically, in which container — but more on that later).

This article briefly describes the Fabric8 architecture, and explains how to deploy a simple application on it. I'll be using the same "Tick/Tock" application as in the article Dynamic configuration of OSGi bundles running on Apache Karaf, from the ground up. This application consists of two OSGi bundles called Tick and Tock, that interact according to a service producer/consumer model. The application is very simple, and the only sign that it is running is to look for its messages in the log. I use this application because it has few dependencies on other components, and can easily be built from source using command-line tools. You won't need to build the code to follow this article — just download the application (see the Download link at the end of the article), and unpack it to extract the two bundle archives osgitest_tick2.jar and osgitest_tick1.jar. If you have no idea what an OSGi bundle is, I would suggest reading my article Creating OSGI bundles and services from the ground up using Apache Karaf. That article also describes how the Tick/Tock application works, which might be helpful if you haven't looked at earlier articles in this series.

The steps in this article were test using version 1.1.0.Beta4 of Fabric8 and version 6.1.0-379 of JBoss Fuse, on a Fedora Linux system. Windows users will, as always, have to fiddle about with some filenames.

About Fabric8

Fabric8 provides a code and configuration repository, and a way to manage containers that will host that code in accordance with the configuration. Each container is a JVM in its own right; containers can be distributed across physical hosts, and will synchronize to one another over the network. Each installation will have at least one root container, that provides an administration console and manages any number of child containers on which applications are deployed (actually, you can deploy code on the root container as well, but that doesn't really demonstrate anything interesting). The root container provides a web-based administration GUI which listens, by default, on port 8181. On the whole both the web GUI and the console provide the same features, but in this article I use the console, simply because it's easier to describe.

Child containers keep in constant communication with their root container, but will continue to run if the root is shut down or becomes unreachable (there will be many messages in the log if this happens, and containers will strive to reconnect as soon as possible).

The unit of application deployment in Fabric8 is the profile. A profile lists one or more features, each of which references one or more bundles. The bundles can be OSGi bundles, as in this example, or Fuse Application Bundles (FABs). The use FABs is sometimes preferable, as some of the dependency management issues can be taken care of at runtime, rather than being a chore for the developer. However, the use of FABs is outside the scope of this article.

The list of features in the profile is just that — a list. There is no actual code referenced in the profile, just the names of the features. Associated with each profile is one or more repositories. A repository is a URI that indicates a place where the software features can be obtained. In practice, this is usually a Maven repository, but in this example the repository will be a simple XML file on the local filesystem, which refers to OSGi bundles which are also on the local filesystem. This is very easy to set up and test but, obviously, it's limited to testing on a single host (unless you use some sort of shared filesystem).

Each container caches the bundles it retrieves from the repository so that although the repository itself might not be fault tolerant, that won't reduce the fault tolerance of the fabric itself, once code has been deployed.

Profiles can form a parent-child relationship and, in practice, all administrator-defined profiles will need to inherit from the default profile at least. The default profile specifies the components that are needed for inter-container communication (among other things), and the container won't work properly without these bits.

Profiles are also associated with a set of configuration properties, of which more later.

In summary, the structure of a profile is shown diagramatically below:

parent-profile
  profile
    feature 1
      bundle 1
      bundle 2 
    feature 2
      bundle 3 
      bundle 4 
    profile configuration properties
    profile repository list
In this example, we'll set up a very simple profile, which will have the following structure:
parent-profile ('default')
  profile ('test')
    feature ('ticktock') 
      bundle 1 ('osgitest_tick2.jar')
      bundle 2 ('osgitest_tock2.jar')
    profile configuration properties
      'delay' (delay between ticks in milliseconds)
    profile repository list (reference to a file 'features.xml')
The parent-child relationship between profiles is very important in practice, because it allows profiles to differ only in their configuration. For example, we could specify all the code needed to implement a web services application in the parent profile, and then in sub-profiles specify only configuration properties such as the HTTP port. Then the different sub-profiles can be assigned to different containers, so that each gets the same code but different configuration.

Install and initialize fabric8

Fabric8 is available from the Fabric8 web site. The RedHat product, JBoss Fuse 6.1, is available from here, but you'll need to sign up for the community program (free). In most respects, so far as this simple article is concerned, the two pieces of sofware behave identically. Where they don't, I'll point it out. Note that the Fabric8 binary bundle is available in .zip or .tar.gz format. The .zip format did not work for me — I suspect that Windows-friendly .zip files unpack with missing permissions on Linux.
$ unzip fabric8-karaf-1.1.0.Beta4.tar.gz
$ cd fabric8-karaf-1.1.0.Beta4
$ ./bin/fabric 
Fabric8:karaf@root> fabric:create
Note that with Fuse the startup command is bin/fuse. With Fabric8, the admin user credentials are already defined in the file etc/users.properties. With Fuse this user is commented out, so you'll need to restore it if you want to use the Web console.

The console command fabric:create creates a default configuration for the root container, for stand-alone use. You can also use fabric:join to make a Fabric8 installation participate in an existing cluster.

Create the application profile

The profile is the unit of application deployment. Running the command
Fabric8:karaf@root> profile-create --parents default test
creates a new profile called 'test' (sorry about the dull name), as a child of the default profile. If you want to create a more sophisticated application, it may be better to use one of the more feature-rich profiles provided with Fabric8, than to define all the necessary dependencies in your own profile; but 'default' is fine for this simple example.

Now we need to create the software repository, which will host the two OSGi bundles that compromise the Tick/Tock application. This repository will be on the local filesystem — not a great idea in a production setting, but fine for this test. The repository will consist of the two bundle JAR files (which can be anywhere on the filesystem), and an XML file, whose location we will supply to the profile. So the links go like this: the profile references the filesystem location of the XML file, and the XML file references the filesystem locations of the bundles. I have created the XML file as /home/kevin/features/features.xml — any location is fine for this simple test. features.xml looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<features name="my-features">
  <feature name="ticktock">
    <bundle>file:///home/kevin/shared_source/osgitest2/osgitest_tick2.jar</bundle>
    <bundle>file:///home/kevin/shared_source/osgitest2/osgitest_tock2.jar</bundle>
  </feature>
</features>
This XML file indicates that one feature is available, called 'ticktock', and that it consists of two OSGi bundles, whose full paths are specified.

To assign the 'ticktock' feature to a profile, we indicate the path the XML file, and the name of the feature, as follows:

Fabric8:karaf@root> profile-edit --repositories file:///home/kevin/features/features.xml
Fabric8:karaf@root> profile-edit --features ticktock test
For a quick check that the profile has been set up:
Fabric8:karaf@root> profile-display test 
Profile id: test
Version   : 1.0
Attributes: 
	parents: default

Container settings
----------------------------
Repositories : 
	file:///home/kevin/features/features.xml

Features : 
	ticktock
Note:
The values entered into the profile are not checked in any way at this stage, not even for basic sanity. It's easy to make mistakes with names and patchs, and generally these will not show up until you try to assign the profile to a container.
If it's more convenient, you can inspect and edit profiles and features using the Web GUI — the 'test' profile is shown in the following screenshot.

web console image

Manage the application on discrete containers

The application is now defined as a Fabric8 profile called 'test', but it is not deployed anywhere. The next step is to create a new container into which to deploy the code, and to manage it. You can create multiple containers, each with its own profile, or profiles, and in a production environment they can be on distributed hosts. For this example, we need only one container, to host the single profile we've defined.

Create a new container called test1

The following console command creates a new container called test1 (sorry about my lack of imagination, as always) as a child of the root container. There are variants of this command that will create multiple child containers, if necessary.
Fabric8:karaf@root> container-create-child root test1
By default, containers are started as soon as they are created. They will have a default profile, that is, enough code deployed on them to be able to take part in administration operations. Each container runs in its own JVM and, on Linux, you'll be able to see the new container using the ps command. The console command container:info will helpfully give the operating system process ID, which can be useful if you have large numbers of containers running, and can't tell them apart.

When the container is created, its data and configuration are stored in a subdirectory of the instances directory of the Fabric8 installation. Of particular importance are the instance specific logs, which in this example will be in instances/test1/data/log. At first, there will only be a single karaf.log, but these logs are rotated as they fill up. In the following text, all references to 'the log file' mean to instances/test1/data/log/karaf.log.

Install the feature on container test1

Use the container-change-profile command to assign the profile 'test' to the new container 'test1'.
Fabric8:karaf@root> container-change-profile test1 test
@notebox(If the container immediately loses contact with the console, so that you can't even tell if it's running or not, consider whether you correctly specified the parent of the new profile. 'default' is not, in this case, a default — it must explicitly be given as the parent of the profile.) In the log for the test1 container, you should see the application bundles starting up, and the Tick service starting to issue Tick! messages.
65 - io.fabric8.fabric-agent - 1.1.0.Beta4 | Configuration changed.  New bundles list:
   file:///home/kevin/shared_source/osgitest2/osgitest_tick2.jar
   file:///home/kevin/shared_source/osgitest2/osgitest_tock2.jar
...
65 - io.fabric8.fabric-agent - 1.1.0.Beta4 |     file:///home/kevin/shared_source/osgitest2/osgitest_tick2.jar
65 - io.fabric8.fabric-agent - 1.1.0.Beta4 |     file:///home/kevin/shared_source/osgitest2/osgitest_tock2.jar
...
111 - net.kevinboone.OSGiTest.tick - 1.0.2 | Tick bundle started
111 - net.kevinboone.OSGiTest.tick - 1.0.2 | updated() called
111 - net.kevinboone.OSGiTest.tick - 1.0.2 | Passed a null configuration
...
111 - net.kevinboone.OSGiTest.tick - 1.0.2 | Tick! (v2.05) after delay 5000 msec
112 - net.kevinboone.OSGiTest.tock - 1.0.2 | tock 
Note that I've removed the date/time information from the log information since this doesn't contribute much; so the first entry on each line is the OSGi bundle ID. In this case, 65 is a system bundle that is managing the code deployment, and 111 and 112 are the IDs of our application bundles.

The message "Passed a null configuration" is displayed by the Tick bundle because, at this point, no configuration has been defined in the profile for this bundle. It must therefore using its default time interval of five seconds. The next section explains how to configure the bundle.

Configure the Tick service at runtime

Fabric8 contains a distributed persistent configuration store; this means that any changes you make to a profile configuration, even at runtime, are stored, and need not be made again. In a clustered environment, the configuration is replicated such that the failure of one node of the cluster should not prevent other nodes getting access to their profiles' configuration. To set the value of the tick interval in the 'test' profile, use profile-edit as follows:
Fabric8:karaf@root>  profile-edit -p net.kevinboone.tick/delay=10000 test
The string net.kevinboone.tick is the 'Persistent ID' defined by the Java class the implements the Tick bundle. There is absolutely nothing — apart from good documentation — to stop you entering the wrong ID here, or the wrong property name, and this is not considered an error. In an environment that can be flexibly reconfigured at any time, it has to be possible to add values to the profile that are not yet registered with any bundle, or for the bundle to read values from the profile that have not yet been defined. Considerable care is needed in this area to ensure consistency.

When the profile is updated, you should see the following messages in the log, indicating that a new value for the tick delay has been accepted.

113 - net.kevinboone.OSGiTest.tick - 1.0.2 | Passed a non-null configuration
113 - net.kevinboone.OSGiTest.tick - 1.0.2 | Passed a new delay: 10000 msec
...
113 - net.kevinboone.OSGiTest.tick - 1.0.2 | Tick! (v2.05) after delay 10000 msec

Remove the feature from container test1

To remove the feature, which amounts to undeploying the application from the container, just use container-remove-profile to remove the profile from the container.
Fabric8:karaf@root> container-remove-profile test1 test
This change takes effect immediately on any container that uses the affected profile. In the log, you should see the bundles being stopped.
65 - io.fabric8.fabric-agent - 1.1.0.Beta4 | Stopping bundles:
...
112 - net.kevinboone.OSGiTest.tock - 1.0.2 | Tock bundle stopped
111 - net.kevinboone.OSGiTest.tick - 1.0.2 | Tick bundle stopped
Then the code will be dereferenced and eventually garbage-collected.

Summary

This has been a very brief overview of setting up a single Fabric8/Fuse6.1 installation, and deploying code on it. Although this example used only a single physical host, the same basic principles apply to managing the applications on containers in a distributed environment.

Downloads

Source code, including compiled OSGi bundles

Copyright © 1994-2013 Kevin Boone. Updated May 01 2014