Appirio's Tech Blog

Monday, September 14, 2009

Creating a Salesforce.com Robot for Google Wave

Kyle Roche

In this example, I'm going to walk you through creating a mashup between Google Wave and Salesforce.com using Google App Engine as a platform. If you haven't heard of Google Wave yet, I encourage you to watch the introduction video from Google I/O. Google Wave is still in developer preview but Google said they'd be releasing another 100K developer / sandbox logins this month and access has been granted to a ton of users through the Google Technology Users Groups and other community events.

There's a few ways to develop around Google Wave. They are the following:
Robots: an automated participant of a wave. (this example). A robot can read the contents of a Wave and modify, add, or remove items in the conversation. In short, an automated participant.

Gadgets: a way to embed non-trusted code in Google Web applications. Basically, 3rd party tools to change the way Waves look / feel

Embed: a way to simply embed a real time conversation into another web page

So, here's the use case I was thinking of when I put this mashup together. A salesrep is on the road... they are chatting in real time via Google Wave about their account. You can add the Salesforce.com Wave Robot to your conversation and have it contribute useful information to the thread. A possibly flow might look like this:
Rep: I'm at Acme, Corp today. Do we have open cases?
SFDC Robot: You have 14 open cases with Acme, Corp (4 SEV1, 10 SEV2)
Home Office Staff: Did they mention the new SOW?
Rep: No. What's the status of the Opportunity?
SFDC Robot: Acme has 3 open Opportunities. They are: blah blah blah

Wouldn't that be great? The Robot could contribute in real time to authorized parties. This is a very simple example of how to get the technology up and running. I'll extend this library and post it to Google Code / Code Share in the coming weeks.

Getting everything in order:
You'll need the following to complete this example.

Create a new Web Application Project Make sure you DO NOT check Google Web Toolkit. GWT is currently only supported on Java 1.5. You need to use Java 1.6 for Google Wave.

Next let's make sure we have the right JRE enabled for our project. If you've been using GWT already you probably have 1.5 as the default JRE for your Eclipse projects. Right click the JRE directory under the new project and select Properties. Change the JRE to a supported 1.5 JRE. (I'm on Snow Leopard, so my version may appear slightly different).

If you've already downloaded the Force.com toolkit for Google App Engine and the Wave Robot Java Client Toolkit you will need to gather the .jar files from both distributions and put them in the /war/WEB-INF/lib directory of your new Eclipse project. Refresh the project (right click / refresh) and you will see the new .jar files listed. Select each of the .jar files (as shown in the image to the right) and right click and select Build Path -> Add to Build Path. The .jar files selected will move up to the newly created References Libraries directory. You can now start coding for Google Wave and Force.com in your App Engine application.

Next we have to setup the configuration files that Wave expects to find when it first talks to your application. Under the war directory create a sub-directory called _wave. Create two files called capabilities.xml and profile.xml. Copy the source code for each below:

capabilities.xml






0.6



profile.xml


Salesforce_Wave

We're going to need two Servlets to add the functionality for the Robot. The first one, SalesforceWaveProfile.java, just describes the Profile for the Robot so Wave knows what to call it and how to present it in conversations. The second, SalesforceWaveServlet.java, adds the functionality for the call out to SFDC and the listeners for the Wave conversation. Copy the source code for each. Then we'll walk through the SalesforceWaveServlet.java class in a bit more detail.

SalesforceWaveProfile.java

package com.appirio.wave;

import com.google.wave.api.ProfileServlet;

public class SalesforceWaveProfile extends ProfileServlet{

/**
*
*/
private static final long serialVersionUID = -9118330158854113534L;

@Override
public String getRobotName() {
return "Salesforce Wave";
}

@Override
public String getRobotAvatarUrl() {
return "http://gaesandbox.appspot.com/assets/logo.png";
}

}}

SalesforceWaveServlet.java

package com.appirio.wave;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.wave.api.*;
import com.sforce.ws.*;
import com.sforce.soap.partner.*;
import com.sforce.soap.partner.sobject.SObject;

public class SalesforceWaveServlet extends AbstractRobotServlet {

/**
*
*/
private static final long serialVersionUID = -6772624822771544542L;

@Override
public void processEvents(RobotMessageBundle bundle) {
Wavelet wavelet = bundle.getWavelet();

if (bundle.wasSelfAdded()){
Blip b = wavelet.appendBlip();
TextView t = b.getDocument();
t.append("Hi, the SFDC Robot is connected. Type 'ping' to see if I'm still online. Type 'sfdc get accounts' to list your accounts.");
}

for (Event e : bundle.getEvents()) {
if (e.getType() == EventType.BLIP_SUBMITTED) {
submit(wavelet, e.getBlip());
}
}
}

private void submit(Wavelet wavelet, Blip blip)
{
TextView t = blip.getDocument();
String str = t.getText();

if (str.indexOf("ping") != -1){
t.append("
Yes, I'm here
");
} else if (str.indexOf("sfdc get accounts") != -1) {
t.append("
Getting Last 10 modified Accounts
");
getAccounts(t);
}
}

void getAccounts(TextView t) {
String username = "usernamehere";
String password = "passwordhere";
PartnerConnection connection = null;

try {
if ( connection == null ) {
ConnectorConfig config = new ConnectorConfig();
config.setUsername(username);
config.setPassword(password);
connection = Connector.newConnection(config);
}
QueryResult result = null;

result = connection.query( "select id, name from Account order by LastModifiedDate desc limit 10 ");

for (SObject account : result.getRecords()) {
t.append((String)account.getField("Name") + "
");
}
} catch (ConnectionException ce) {
t.append(ce.getMessage() + ":" + ce.getClass());
}
}
}

There's a few key methods to the SalesforceWaveServlet.java class. The entire getAccounts() method was taken and altered from the Force.com toolkit for Google App Engine example. The override on the processEvents() methods interacts with Google Wave. Each time something changes in the wave, your application is alerted. You can see that in our example we're listening first for the .wasSelfAdded() call from Wave. This tells us when our Robot was added to a conversation. When that fires the users will see a greeting message like the following.

From the welcome message you are given some brief instructions on the options available in the demo application. They are ping and sfdc get accounts. When any message is added to the Wave the BLIP_SUBMITTED event is fired. You can trigger off this event and analyze the text in the conversation. In our case we are responding to two events. The interaction in the conversation is shown below.

It's important to note. If you're using Google Wave and decide to write a Robot there's a few other steps I didn't go over in detail in this post. First, the Java Robot SDK is only supported on Google App Engine at this time. Second, to interact with a Robot in a wave you need to add them to your conversation. You do that by adding the application-identifier@appspot.com address to your wave (where application-id is your App Engine application ID). There's a Google Wave Hackathon in Denver this week. Hopefully, we'll extend this example application a bit more. We'll keep you posted.

 
2006-2010 Appirio Inc. All rights reserved.
Appirio.com | Support | Resource Center | Contact | Careers | Privacy Policy