Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
MortenWittrock
SAP Mentor
SAP Mentor
1,446


Updated March 31st, 2021: Because the SAP Cloud Platform brand has been retired, SAP Cloud Platform Integration is now known as SAP Cloud Integration or just Cloud Integration. I've updated the name in the blog post, but I'm keeping the CPITracker name. The Script API version is no longer being tracked (for this reason), so I removed the reference to that. Also, please note that the described technique for determining the CPI build number only works in the Neo environment.

Back in March, I launched HCITracker, which is a Twitter feed that tracks updates to the core components of SAP Cloud 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 🙂

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 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.profile 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.
3 Comments
Labels in this area