Posts Tagged ‘activity streams’

Gluing Confluence Activity to Socialcast with Google App Engine / Gaelyk

August 2nd, 2010

I’ve been experimenting with ways to tie the activity of some of the systems we use together. In this case I would like activity information from Confluence to be streamed into Socialcast. There are several ways to go about this but none are perfect or “out of the box”.

Before I go on, here’s what this looks like in Socialcast with the verb and actor mentioned and object name and link:

A Little Background

You could configure a stream import in Socialcast with an RSS feed from Confluence though there are two problems with this. First, Confluence requires authentication and doesn’t support any token type convention so you have to hardcode the username and password of an account that has access to the activity you’re interested in in Confluence. Second, Socialcast appears to truncate long URLs for RSS feeds (consider this a reported bug ;) ). So the only way I got this to work was to use a URL shortener like bit.ly but a Confluence username and password was hardcoded at bit.ly and the bit.ly URL was used in the RSS feed import. And finally what was imported wasn’t linkable back to Confluence. So there were many stumbling blocks in this, though the simplest approach.

Second I created a confluence-bot user account in both Confluence and Socialcast (with the account in Socialcast having the email address that Confluence would send from) and set this account as a watcher to all of the spaces I wanted activity information to be published. Then I created a group in Socialcast where activity would be published. The email address of the confluence-bot account was set to the email address of the Socialcast group. So when Confluence sent the watch emails out for confluence-bot, they would be sent to the Socialcast group. For some reason this just didn’t work; the emails were never arriving. I suspect Socialcast was filtering the emails as spam, as they contained a ‘Precedence: bulk’ header. It was worth a try but much too kludgy without any control so not worth pursuing in my opinion.

Ideally I wish Confluence had more dynamic notification options. More specifically I wish I could register a webhook in confluence that would let me process watch events or notifications. Even cooler would be to let each user configure a webhook URL. This would decouple the notification from the event and eliminate the need to write a Confluence plugin to handle something like this. For example, if I wanted to get Growl notifications on my desktop for Confluence activity, I could configure a webhook to post to Jeff Lindsay’s Noftify.io service (awesome by the way!).

Anyway, in lieu of that, I decided to hack together watcher emails from Confluence to Socialcast using Gaelyk on Google App Engine and the Socialcast API.

Confluence -> GAE -> Socialcast

Using Gaelyk and Google App Engine as the Glue

Confluence can send out email notifications based on watched content, and Google App Engine applications can act as an SMTP endpoint so since I’m a Groovy fan and been looking for an excuse to kick the tires on Gaelyk, I created a very simple Gaelyk application with an email handler that parsed incoming messages from Confluence and called the RESTful Socialcast API to create new messages. Unfortunately the HTML email Confluence produces isn’t valid xhtml, with unclosed tags and such, so I can’t just parse the XML and pull out the bits I need. I used some quick and dirty regular expressions to grab what I needed, making assumptions that the format of the emails wouldn’t change. I suppose I could fix the Confluence email templates, but I’m trying not to touch Confluence programmatically in this case. He’s a stripped down version of the email handler (<my_project>/war/WEB-INF/groovy/email.groovy):

// have access to message object of javax.mail.internet.MimeMessage
import groovyx.gaelyk.logging.GroovyLogger
import java.io.BufferedReader;
import java.io.OutputStreamWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.net.HttpURLConnection;

def log = new GroovyLogger("emailLogger")

// the email notification message from confluence is a bit messy, try to pull out the important bits from it
def theLink = ""
def m1 = message.content =~ /<\/?(?i:a)(.|\n)*?>/
if(m1?.size() > 0) {
    def firstAnchor = m1[0][0]
    def m2 = firstAnchor =~  /href="(.)*"/
    if(m2?.size() > 0) {
        theLink = m2[0][0] - "href=\"" - "\""
    }
}

String content = message.content;
int start = content.indexOf("<h4>") + 4;
int end = content.indexOf("</h4>");
def details = content.substring(start, end).replaceAll(/<\/?(?i:a|b)(.|\n)*?>/, '')

def user = "confluence-bot@mydomain.com";
def pass = "mypassword";
def addr = "https://mydomain.socialcast.com/api/messages.xml"

def authString = "${user}:${pass}".getBytes().encodeBase64().toString()
def conn = addr.toURL().openConnection()
conn.setRequestProperty("Authorization", "Basic ${authString}")
conn.setRequestMethod("POST")
conn.doOutput = true

def queryString = "message[title]=${URLEncoder.encode(message.subject)}&" + "message[body]=${URLEncoder.encode(details)}" + "&message[url]=${URLEncoder.encode(theLink)}" + "&message[group_id]=6845";

def writer = new OutputStreamWriter(conn.outputStream)
writer.write(queryString)
writer.flush()
writer.close()
conn.connect()

def res = conn.content.text
log.info(res)

There’s some configuration required to turn on the inbound email service and map the incoming email to the proper Groovlet.

Add this to your appengine-web.xml:

<inbound-services>
    <service>mail</service>
</inbound-services>

In your web.xml file, add the new servlet mapping and security constraint:

...
<servlet>
    <servlet-name>EmailServlet</servlet-name>
    <servlet-class>groovyx.gaelyk.GaelykIncomingEmailServlet</servlet-class>
</servlet>
...
<servlet-mapping>
    <servlet-name>EmailServlet</servlet-name>
    <url-pattern>/_ah/mail/*</url-pattern>
</servlet-mapping>
...
<!-- Only allow the SDK and administrators to have access to the incoming email endpoint -->
<security-constraint>
    <web-resource-collection>
        <url-pattern>/_ah/mail/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>admin</role-name>
    </auth-constraint>
</security-constraint>
...

There are obvious improvements that can be made to this approach and much better ways to go about this, along with a lot of considerations that I ignored (e.g. security/permissions) but for a few hours of hacking this is hard to beat!

Actionable Activity Streams

April 21st, 2010

Since writing my last article for TheNextWeb on Socialcast and my conversation with Tim Young, I’ve been thinking about the bidirectional, actionable activity stream concept. It’s less relevant to human constructed messages and more related to machine generated activity messages, specifically exceptions raised from the system. When a source system generates an exception based message that’s put into an activity stream, then generally they require some action or acknowledgement . This is where things can get interesting.

For example, let’s say there is an expense management system for managing employee expense reports and payments. Employee Joe submits an expense report with $750 in miscellaneous “entertainment” expenses which raises an exception. The expense management system is integrated with an activity stream engine; it posts a new message into a stream alerting Jim of the exception (this is where permissions, users, groups, identity, etc get tricky – ignoring that here).

So that’s well and good, Jim is alerted to the exception but now what? Wouldn’t it be great if Jim could act on the message in context?

For example, he could discuss the exception with Joe in context of message in the activity steam. “@joe I need more details on the $750 entertainment charge, and it better be good!” Joe replies, “whoops! sorry that should have been $75, please reject and I will fix.” Jim then rejects the expense report in context of the stream and inherently passes along the stream conversation back to the expense management system as notes to the expense report.

Traditionally, Jim may have received the exception notification via email. He then would have logged into the expense management system, looked at the expense report and either rejected it, notating his question about the entertainment expense or would have emailed Joe asking for details on the expense. Joe then might have replied to Jim or logged into the expense system to add a note and fix the error. This doesn’t sound that bad in this simple scenario, but Jim is very busy and receives hundreds of emails a day and the notification from the system might likely be passed over or Jim just didn’t have time to interrupt what he was doing to go log into the expense management system.

The exception based activity stream use case is more powerful if it allows action to be taken in context. So how might you handle this? Now I’m no activity stream expert, and I’ve had no involvement with any of the standards like activitystrea.ms; I’m just a guy thinking aloud….

What really got me thinking about this use case was a presentation given by Neal Ford at the last social on Implementing Evolutionary Enterprise Architecture. It was primarily based on the Richardson Maturity Model, a model of breaking down REST into three steps that introduces resources, http verbs and hypermedia controls. A hypermedia service enforces a protocol by advertising legitimate interactions with relevant resources at runtime.

In the context of the machine generated activity stream message, the payload of the message would contain a representation of state with respect to the source system activity and links that define possible interactions.

The point of the hypermedia controls is to tell us what we can do next, and the URI of the resource we need to do it. For example, in the example above. The entry might look something like this, with two links – one for approve and the other for reject.

This is a loosely coupled way to provide actions for a given activity. The activity stream system can present the activity message in the stream with the options made available in the payload of the message and selectable actions. This makes no assumption of what type of interface you are viewing the activity stream message through, be it browser, mobile, API rendered, etc. The activity stream system would likely need to abstract the actions in it’s API allowing calls to be made back through the APIs in proxy to the source system.

You could obviously take this concept much further, adding a sequence of actions based on the previous action taken on an activity. For example, if you approve the expense report, the REST call response might return a new list of actions like “view’ the updated expense report.

Like I alluded to above, there are many complexities to this including authentication/identity, authorization, the capabilities of the source system, the capabilities of the activity stream system, etc.

I really have no bandwidth to pursue this idea, but it’s been on my mind for that last few weeks so I thought I’d write it up and see what people think.

Powered by Web Design Company Plugins

Switch to our mobile site