Skip to Content

Updated December 9th, 2017: HCITracker has been renamed CPITracker, since the HANA Cloud Integration product name no longer exists.

CPITracker is a Twitter feed that informs you of updates to some of the infrastructure components of SAP Cloud Platform Integration, such as the Java VM and the Apache Camel framework. It is implemented as an integration flow, that posts to Twitter whenever it detects a change in one of the properties it tracks.

In this blog I’d like to share a couple of updates to CPITracker, and talk about two cool Groovy features, I used to implement them: Closures and dynamic programming.

Two new tracked properties

First off, CPITracker now also tracks the Cloud Connector version and the Adapter Development Kit API version; two related products that I think quite a few CPI developers are interested in.

Unlike most of the other tracked properties, these two new ones are not extracted from the tenant, in which CPITracker runs. Rather, they are pulled from the SAP Development Tools page, specifically by parsing the page’s HTML source with the jsoup Java library.

Language variety via Groovy closures

Second, I wanted to introduce a bit of variety in how the tweets are phrased, instead of using the same pattern for each one. This is a good fit for one of the coolest features of the Groovy language: Closures.

A closure is basically a chunk of code, that is packaged up as an object. Closures can take parameters and return values, like a method. In the case of CPITracker, I use closures as sentence templates:

def template = { property, fromValue, toValue -> "$property has been updated from $fromValue to $toValue" }

Once you have a reference to a closure, you can execute it in one of the two following ways:

println template('The CPITracker version', '1.0', '1.1')
println template.call('The CPITracker version', '1.0', '1.1')

In my particular case, I want to choose a random closure to generate each tweet:

def messageClosures = [
   { property, fromValue, toValue -> "$property has been updated from $fromValue to $toValue" },
   { property, fromValue, toValue -> "$property has changed from $fromValue to $toValue" },
   { property, fromValue, toValue -> "$property, which was previously $fromValue, is now $toValue" }
]

5.times {
  // Choose a random closure...
  def template = messageClosures.get(new Random().nextInt(messageClosures.size()))
  // ...and call it
  println template.call('The CPITracker version', '1.0', '1.1')
}

Running it just now, I got the following output:

The CPITracker version, which was previously 1.0, is now 1.1
The CPITracker version has been updated from 1.0 to 1.1
The CPITracker version has changed from 1.0 to 1.1
The CPITracker version has changed from 1.0 to 1.1
The CPITracker version, which was previously 1.0, is now 1.1

Granted, a Pulitzer Price is not in the cards, but at least the tweets are a little more varied now 🙂

Adding methods with dynamic programming

In the above code, the part that chooses a random element from a list irks me a little bit. Shouldn’t the list itself be able to return a random element? The Array class in the Ruby programming language has a method that does exactly that: sample. Groovy, sadly, doesn’t. We can fix that, though, with Groovy’s dynamic programming model.

In a nutshell, dynamic programming lets us add new methods to existing classes on the fly. This is all there is to it:

ArrayList.metaClass.sample = { delegate.get(new Random().nextInt(delegate.size())) }

The delegate reference refers to the specific ArrayList instance that receives the call to the new sample method.

Having added the new method, the finished code looks like this:

// Add the sample method to the ArrayList class dynamically
ArrayList.metaClass.sample = { delegate.get(new Random().nextInt(delegate.size())) }

def messageClosures = [
   { property, fromValue, toValue -> "$property has been updated from $fromValue to $toValue" },
   { property, fromValue, toValue -> "$property has changed from $fromValue to $toValue" },
   { property, fromValue, toValue -> "$property, which was previously $fromValue, is now $toValue" }
]

5.times { println messageClosures.sample().call('The CPITracker version', '1.0', '1.1') }
To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply