Back in March, I launched HCITracker, which is a Twitter feed that tracks updates to the core components of SAP Cloud Platform Integration, such as the underlying Apache Camel framework, the Java runtime etc.

Officially, though, the HCI abbreviation has not existed since the HANA Cloud Integration name was dropped, so HCITracker needed a new name.

Better late than never, right? HCITracker is now CPITracker! If you were already following the account on Twitter, you don’t need to do anything; you are still following it under its new name. If you are not following it, well… you know what to do

In addition, I’ve also added tracking of the Script API version, so if you’re using the Script API JAR file in your integration development work, CPITracker will now let you know when a new version is out.

About the CPI build number

A couple of people have asked me how I determine the CPI build number in CPITracker, in order to track how it changes over time.

That’s a really good question, and the answer is all about OSGi bundles. In a nutshell, a bundle is a collection of Java classes and configuration files, that you can dynamically install and uninstall as a unit. The SAP Cloud Platform Integration service consists of hundreds of such bundles installed on your tenant.

CPITracker’s approach to finding the CPI build number was based on the idea, that at least one of these bundles would have a version number matching the build number. In the following, I will describe the steps I took to find such a bundle.

The first order of business is to get programmatic access to every installed bundle. How do we go about that? Take a look at the Bundle interface documentation. If we can get a Bundle object, we can call its getBundleContext() method to get a BundleContext object. The BundleContext interface has a getBundles() method, which returns all installed bundles.

So, the problem of accessing all installed bundles has been reduced to getting a single Bundle object. To that end, we can use the static method getBundle of the FrameworkUtil class, which takes a Class object, and returns a Bundle object representing the bundle containing that class (assuming it belongs to a bundle, of course).

Now the problem of accessing all installed bundles has been reduced to finding a Java class that belongs to a bundle. Here’s a likely candidate, that you interact with every time you add a script to an integration flow: com.sap.gateway.ip.core.customdev.util.Message.

Putting all this together, here’s a few lines of Groovy that iterates over each installed bundle:

def knownBundleClass = 'com.sap.gateway.ip.core.customdev.util.Message' def entryBundle = FrameworkUtil.getBundle(Class.forName(knownBundleClass)) def bundleContext = entryBundle.getBundleContext() bundleContext.getBundles().each { b -> // Process each Bundle object here }

Now that we know how to access all installed bundles, it’s straightforward to find every bundle, whose version number matches the current CPI build number (which is 2.35.7 at the time of writing). The following complete Groovy script does exactly that, and builds an XML message body containing the bundles.

import com.sap.gateway.ip.core.customdev.util.Message import groovy.xml.MarkupBuilder import org.osgi.framework.FrameworkUtil def Message matchingVersionBundles(Message message) { def sw = new StringWriter() def builder = new MarkupBuilder(sw) def matchingVersionBundles = getMatchingVersionBundles('2.35.7') builder.bundles { matchingVersionBundles.each { b -> bundle { id(b.getSymbolicName()) name(b.getHeaders().get('Bundle-Name')) version(b.getVersion().toString()) } } } message.setBody(sw.toString()) return message } def getBundleContext() { def knownBundleClass = 'com.sap.gateway.ip.core.customdev.util.Message' def entryBundle = FrameworkUtil.getBundle(Class.forName(knownBundleClass)) if (entryBundle == null) { throw new AssertionError("No OSGi bundle for class ${knownBundleClass}") } entryBundle.getBundleContext() } def getMatchingVersionBundles(String version) { getBundleContext().getBundles().findAll() { b -> b.getVersion().toString() == version } }

As I write this, the script generates the following output:

<bundles> <bundle> <id>com.sap.it.node.stack.profile</id> <name>Node Stack Profile Bundle for Stack iflmap</name> <version>2.35.7</version> </bundle> <bundle> <id>com.sap.it.node.stack.repository</id> <name>Node Stack Repsository Bundle for Stack iflmap</name> <version>2.35.7</version> </bundle> </bundles>

I chose the com.sap.it.node.stack.repository bundle, and watched it over a period of time, to confirm that its version number does in fact match the CPI build number. The CPITracker code uses this bundle, and the technique described above, to determine the CPI build number.