In this article Ivan and Alex show how declarative services can be used to update a Java OSGi component with zero downtime.

Invoke Ivan and Assign Alex enjoying a mocha

Summary

  • OSGi bundle implementation
  • Writing the CICS application
  • GitHub sample source and projects

Dynamically updating applications without outage using OSGi’s Declarative Services

In this post we’re going to look at how we can use OSGi’s declarative services to build applications that handle updates to services without outage. We’ll use the simple project that’s readily available on the CICSDev GitHub.

In this sample, we have a hypothetical requirement to create a local storage service. The storage service operates by providing some string data and getting an ID number in response. The ID number can be used to look up the data from the service at a later time. The sample is fairly simple and contrived, but serves our purpose to showcase declarative services.

To import the projects, simply clone or download the git repository onto your local filesystem. Then, in Eclipse, File → Import... → Existing Projects into Workspace. The root directory is the directory where the git repository has been downloaded to. Ensure all projects are selected then click Finish. You may also need to set your target platform to CICS TS 5.4 with Java EE and Liberty (replacing 5.4 with the relevant version of CICS) to compile the projects.

Interface Definition

As well behaved programmers we’re going to make use of interfaces, so our first task is to determine the OSGi interfaces needed to define our services. A well designed interface will need less change and be easily extensible. Typically, we consider it a minor change to add to an interface, and a major change to remove from or modify existing methods of an interface. The latter almost certainly requires dependent code to be modified and could result in more disruption.

Good practice dictates we should define the API and implementation in separate bundles. If we didn’t, and modified an implementation stored with an API, the API dependencies alone would likely cause a cascade of bundle refreshes. By separating API and implementation(s) we can add and remove implementations without affecting the API. We’ll also package each OSGi bundle in its own CICS bundle for similar reasons.

In the API bundle com.ibm.cicsdev.osgi.ds.storage, we define the StorageService interface and export it using the Export-Package header in the OSGi manifest. That gives us a contract to work to. At this point we’re free to write either the application that will use this code, or the implementation. Both could even be done at the same time by different teams – neither team needing to know what the other will do, as long as they keep to the interface.

The first implementation

We’ll write a very simple implementation first, something that can be used to verify the behaviour without doing anything complex. On a larger scale project, the team writing the application might even want to create a mock or stub service to verify the application without needing a real implementation.

In a new implementation bundle com.ibm.cicsdev.osgi.ds.storage.impl version 1.0.0, we create the Java class InMemoryStorage, implementing the StorageService interface. In this service, we just add and read a Java list. We then register our implementation using the declarative services component definition XML. In the component definition XML, we define that the InMemoryStorage class is an implementation of the StorageService. This allows the OSGi framework to handle the process of registering it as an OSGi service.

In Eclipse, these component definition XML files can be created and easily edited. Create a New → Other... → Component Definition. The parent folder, by convention, is the OSGI-INF directory under the root of the project. Choose a file name, a name for the service, and the class the service uses.

This will create the component XML file, and open a visual editor, which allows the above detail to be defined easily:


This OSGi bundle is also packaged into its own CICS bundle. For a lot of projects it makes sense to keep them individual allowing them to be swapped in and out easily.

Writing the CICS application

The next task is to write the CICS application that will use the service. In our system, the application is a CICS program that’s invoked via a CICS transaction. But it could just as easily be linked to from another CICS program or invoked through a web service of some form.

We create the OSGi bundle com.ibm.cicsdev.osgi.ds.cicsapp to package the application code, and the class CICSApp processes parameters from the terminal and invokes the service. In this class you’ll notice there’s methods for binding and unbinding the storage service too.

Using declarative services, we create a component XML descriptor, which specifies that the CICSApp class is an OSGi service which references the storage service, with a cardinality of 1..1 (so exactly one service must be bound at a time). As we haven’t specified otherwise, services will be bound reluctantly – so even if a service with a higher ranking is available the current service will remain bound. The resulting XML looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" 
               activate="activate"
               deactivate="deactivate"
               modified="modified"
               name="com.ibm.cicsdev.osgi.ds.app.CICSApp">
   <implementation class="com.ibm.cicsdev.osgi.ds.cicsapp.CICSApp"/>
   <reference name="StorageService"
              interface="com.ibm.cicsdev.osgi.ds.storage.StorageService" 
              bind="bindStorageService"
              unbind="unbindStorageService"
              cardinality="1..1"
              policy="dynamic" />
</scr:component>

We also provide a CICS-MainClass header in the OSGi manifest to ensure CICS can resolve this class for program link.

As well as packaging this application in its own CICS bundle, we provide the accompanying PROGRAM and TRANSACTION resource definitions in the CICS bundle. Although not strictly necessary, keeping the application’s dependencies together within the same package, is very convenient. Now we’re ready to deploy the first version of this application.

Deploy version 1.0.0 of the application

Having deployed the CICS bundles to USS and defined CICS resources, here’s an overview of the naming scheme we’ve employed:

CICS Bundle Name OSGi Bundle Version Description
DS com.ibm.cicsdev.osgi.ds.storage 1.0.0 The OSGi interface bundle
DS-APP com.ibm.cicsdev.osgi.ds.cicsapp 1.0.0 The CICS application OSGi bundle
DS-IMP10 com.ibm.cicsdev.osgi.ds.storage.impl 1.0.0 The in-memory implementation of the storage service

 

Install all three CICS bundles, then run the transaction DSTS. To put data into the storage service, use the syntax DSTS PUT data, this will return the string: “Created entry item“, where item is the item ID of the stored data.

To retrieve data, use the syntax DSTS GET item, where item is the integer item ID to retrieve. Note that for both of these, we start our indexes at 1, not 0.

This should verify that the code is all working with this first simple implementation of our storage service.

CICS TSQ storage service

We think we can do better and provide a more capable version of our storage service. So let’s look at using a CICS TSQ to store data instead. TSQs don’t need to be defined before use, which keeps the set-up minimal for our sample. However, in a real world example, we’d probably use something that’s more persistent like VSAM, Db2 or the coupling facility.

We’ll show later how we can lean on DS and the OSGi framework to upgrade the services with no downtime for the application, but first let’s create another OSGi bundle, com.ibm.cicsdev.osgi.ds.storage.impl with version 1.1.0. In this implementation the class TSQStorage contains the logic for reading and writing to a CICS TSQ. As with the in-memory servce, we define the DS component XML descriptor and mark this as an implementation of the storage service. Package up the new implementation in a CICS bundle and export it to zFS. To recap, here’s a list of the OSGi and CICS bundles you should have:

CICS Bundle Name OSGi Bundle Version Description
DS com.ibm.cicsdev.osgi.ds.storage 1.0.0 The OSGi interface bundle
DS-APP com.ibm.cicsdev.osgi.ds.cicsapp 1.0.0 The CICS application OSGi bundle
DS-IMP10 com.ibm.cicsdev.osgi.ds.storage.impl 1.0.0 The in-memory implementation of the storage service
DS-IMP11 com.ibm.cicsdev.osgi.ds.storage.impl 1.1.0 The TSQ implementation of the storage service

Upgrading with zero downtime

Although the TSQ implementation of our storage service is installed and available, the application will continue to use the in-memory service until that service is disabled. That’s because we bound the storage service reluctantly, the default.

Even though the TSQ service is not bound to our application, it may be bound to other services if they used a 1..n cardinality, or had the greedy policy option. In our service we could use this to migrate data from the old service to the new service to avoid data loss during the upgrade. For simplicity we won’t cover this in our sample.

If we disable the DS-IMP10 bundle, you should notice some interesting events. Before the in-memory service is disabled, all services which reference it must be processed. The CICSApp service references this service, so CICSApp must either bind to another storage service, or it will be disabled. Fortunately we have the TSQ service available, so CICSApp is rebound to that service. Once all the references to our in-memory service have been processed, the in-memory service itself can then be unbound and finally it can be disabled. A keen observer will note that there’s never a time at which the CICSApp is without a storage service – in fact there’s a moment where it has two. The net result is that during the swap over of service implementations, there is zero downtime. All by virtue of using OSGi declarative services.

It is easy to see the benefit of these types of seamless updates being made in a large running system with chains of referenced services. Being able to upgrade individual services without affecting the larger system is incredibly powerful.

Final Thoughts

In this article we have seen an example of how to build applications that use OSGi declarative services. We’ve seen how to design services so they can be upgraded with zero downtime, and we’ve touched on some important architectural designs like separating API from implementation and separating business logic from user-interface. We hope it has enthused you and opened up a world of possibilities for your applications and your business.

To see more Invoke Ivan blogs on the topic of OSGi, see the collection here.

Further Reading

Join The Discussion

Your email address will not be published. Required fields are marked *