Episode 12 : Writing an Advanced Google Wave Robot using WadRobotFramework

Welcome to Episode 12 of the series. This episode is an extension of the earlier episode where we saw how to write Simple Google Wave Robots using the WadRobotFramework. I strongly recommend that  you have completed the earlier episode and have got comfortable with the WadRobotFramework since this episode builds on the earlier one.

To recap, the WadRobotFramework distinguishes between 2 kinds of Robots and I summarize it again over here.

Simple Robots : These robots we covered in the earlier episode and saw how you can write a simple robot to react to a blip by appending a new blip (BlipAppenderRobot) or even modify the Blip Text (BlipModifierRobot).

Advanced Robots : These are of main focus in this article and I reproduce from the earlier episode the text so that you understand what Advanced Robots are first. The definition of Advanced Robots is per the WadRobotFramework and it is not meant to indicate this is the final definition of it.

Advanced Robots are those that can react to instructions ( or commands) in the Blips. Here are some samples of Advanced Robots and how they would react to commands from Blips:

1. A character Counting Advanced Robot:

Your submitted Blip Text contains : “Here is some text in the blip. Please count the length of this message for me.” {mycountingrobot:count}.

You can write an advanced Robot (mycountingrobot) that knows how to count the length of the message. So it gets notified when it there is a instruction (count) in the Blip. Your Advanced Robot can then count out the characters and then either append or modify a blip as needed.

2. A Tweeting Robot:

Your submitted Blip Text contains the following text : “{mytweetingrobot:tweet} Some text to tweet”

You can write an advanced Robot (mytweetingrobot) that knows how to tweet the message to Twitter. So it gets notified when it there is a instruction (tweet) in the Blip. Your Advanced Robot can then append a blip or modify a blip saying that the message has been tweeted.

The best part of it all is that you could combine all of this into a single Robot that can respond to one or more commands. For example, take a look at Today’s Special Robot (see http://ppandhi.wordpress.com/2009/11/08/todays-special-robot/) that can respond to more than one command. It can give you the quotes, day in history, word of the day, cricket score, your daily horoscope by simply responding to the command that you type in.

So for example, you could write a robot and give it commands like this:

1. {myrobot:doCommand1}

2. {myrobot:doCommand2}

3. {myrobot:doCommandN} and so on.

In this article, we are going to see exactly how to achieve the above command Robot that will delegate its work to different workers who are responsible for executing the command i.e. doing the work.

Let us get a few definitions in place first:

1. The Command Robot: This is the main class of your Robot and you need to extend the org.wadael.waverobotfrmwrk.advanced.WithWorkersRobot. You need to have an identifier for your robot, which is a unique ID for your Robot. Let us call it GAEJRobot.

2. Each Command Robot is capable of following instructions or commands. These instructions are executed by the Workers.

3. A Worker is a Command implementation that performs a certain logic. For e.g. fetching a stock quote, getting a word of a day, sending a Tweet, sending an email, etc. Each worker will indicate what instruction or command it obeys.

As an example, say you want to write an Advanced Robot class (WithWorkersRobot) whose identifier is named GAEJRobot that can responds to the following two commands:

a. SendTweet
b. GiveWordOfTheDay

So, you will implement two Workers and register (add) them to the GAEJRobot class. The two worker classes will be :

  • SendTweetWorker which says that listens to an instruction named tweet and it will implement its core logic in the doWork() method.
  • GiveWordOfTheDayWorker which says that it listens to an instruction named wotd and it will implement its core logic in the doWork() method.

Now, in your Wave Conversation, you can give the following text in the Blip (of course after adding the Robot as a participant).

1. {GAEJRobot:tweet}

2. {GAEJRobot:wotd}

Voila! The WadRobotFramework will then do the heavy lifting for you. It roughly works as follows:

  • When you submit a Blip, it will scan the Blip Text for the identifier and see if it matches itself.
  • If yes, it will scan out the instructions and invoke the doWork() method of the Worker Robot that implements the instruction.

This is not all. The WadRobotFramework has thought about parameters that you may need to pass to your Robot. For e.g. consider the following fictitious instruction that you need to give to a Stock Quote Robot.

{StockQuoteRobot:getquote GOOG} or {StockQuoteRobot:getquote GOOG,MSFT,ADBE,ORCL,IBM}

In short the format is as follows:

{RobotIdentifier:Instruction<space>[parameters]}

So in the above two examples the parameter GOOG and the parameter string “GOOG,MSFT,ADBE,ORCL,IBM” will be passed to the doWork() method of your RobotWorker that has registered with the Advanced Robot and who implements the getquote instruction. Please read this last statement clearly with the highlighted words as the key pieces required to build out an Advanced Robot.

Simple yet powerful and it opens up a wide range of Robots that you can start writing today. So let me get started and demonstrate the basic flow to get an Advanced Robot up and running which can accept 1 or more instructions. The Robot does not do anything specific except for simply demonstrating the flow. Readers are expected to extend it with their ideas brimming inside their minds.

To understand what we will build, it helps to take a look at the running robot as shown below:

You will notice that I have added my robot called MyAdvancedRobot. The identifier for the Robot is GAEJRobot and the Robot has two workers (Worker1 and Worker2)  registered with it, which implement the instructions  command1 and command2 respectively.

Now when I submit the text {GAEJRobot:command1} , the doWork() method of the Worker1 is invoked. It simply accepts the command and prints out that it received the command with no parameters passed to it.

Similarly, look at the wave conversation below:

Here I give the command2 to the GAEJRobot and I am also passing a string of parameters. When I click the Done button, the doWork() method of the Worker2 is invoked. It simply accepts the command and prints out that it received the command with the parameter string. Now, it could have processed the parameters and performed its logic accordingly. This demonstrates how the wiring is done by WadRobotFramework to make your life easier in writing Advanced Google Wave Robots.

Let us start writing code now. I will assume that you are fairly conversant now with the typical project structure and requirements of writing a Google Wave Robot. If not, I suggest that you first go through these tutorials in the following order:

  • Episode 7 : Writing your First Google Wave Robot
  • Episode 11: Develop Simple Google Wave Robots using the WadRobotFramework

Project Setup

Create a New Project or use the same project MyGoogleWaveRobot that we used in the earlier Episode 11.

If you are creating a New project, then please make sure that you download all the JARs from the following two locations:

http://code.google.com/p/wave-robot-java-client/downloads/list

The web page when you navigate to the above URL is shown below:

ep7-6

Download all the above files to your machine.

The WadRobotFramework JAR file is available at the following location :

http://code.google.com/p/wadrobotframework/downloads/list

The web page when you navigate to the above URL is shown below:

Download the JAR file to your machine. Once you have downloaded the 5 JAR files, make sure that they are copied to the WEB-INFlib folder of your project and that the Project Build Path is also setup with the JAR files as shown below:

Writing the Advanced Robot : MyAdvancedRobot.java

The first step is to create a Java class. Call it MyAdvancedRobot.java. The source code is listed below:

 

package com.gaejexperiments.waverobot;

import org.wadael.waverobotfrmwrk.advanced.WithWorkersRobot;

public class MyAdvancedRobot extends WithWorkersRobot {

public MyAdvancedRobot() {
super();
//This will process 'command1'
addRobotWorker(new Worker1());

//This will process 'command2'
addRobotWorker(new Worker2());
}

@Override
public String getRobotIdentifier() {
return "GAEJRobot";
}

@Override
protected String getUsage() {
return "Advanced Robot commands : command1 and command2";
}

@Override
protected String getRobotSelfIntroduction() {
return "I am an Advanced Robot";
}

}

 

Let us dissect the code now:

1. Our Advanced Robot class MyAdvancedRobot extends the WithWorkersRobot class, which is required for creating the Advanced Robots with the WadRobotFramework.

2. The WithWorkersRobot class constructor uses a method called addRobotWorker(…) to add one or more workers to it. Remember each worker will execute or do its job as per the instruction that it obeys. So we are going to have two workers : Worker1 and Worker2 which we are adding to our AdvancedRobot. We will get to the listing of the Worker1 and Worker2 later but it is sufficient to keep in mind, that Worker1 class will perform the work required with command1 instruction is given in the Blip and Worker2 class will perform the work required when command2 instruction is given in the Blip. To recap, as you add more workers, you will need to add them here in the constructor using the addRobotWorker method.

3. The getRobotIdentifier() method is used to return the string that is the unique identifier of your Robot. This is used to distinguish Robots in the same Wave. The identifier if you recollect is going to be used by the other participants in the wave to give instructions to your Robot. As mentioned, the format in the Blip to give an instruction to your robot will be like this:

{RobotIdentifier:Instruction<space>[parameters]}

Each Instruction is implemented by a Worker. For e.g. command1 will be implemented by Worker1 and command2 will be implemented by Worker2.

So to invoke Worker1, we have to say {GAEJRobot:command1} in the Blip and submit it. Hence we return GAEJRobot in the getRobotIdentifier() method and this will be the unique way in which we identify this Robot

4. The getUsage() method is used to return a string that will be displayed to the participant when they type /help in the start of the Blip. This is useful to give a list of possible instructions that your Advanced Robot implements. In our case here, we are implementing two instructions and hence we have returned some string. But you can give an elaborate help string stating detailed command text, sample parameters, etc.

5. Finally, we have the getRobotSelfIntroduction() method. This is not mandatory but it is nice to announce to the particpants when you (Robot) gets added to the wave as a participant. Simply return a String that you would like to announce to the other (existing) participants in the Wave.

Implementing the Workers

We are now going to implement the Workers i.e. Worker1 and Worker2. The code is identical for both of them and it is listed below:

Worker1.java

 

package com.gaejexperiments.waverobot;

import org.wadael.waverobotfrmwrk.advanced.RobotWorker;

import com.google.wave.api.Blip;
import com.google.wave.api.Event;
import com.google.wave.api.RobotMessageBundle;

public class Worker1 implements RobotWorker {

public String getInstruction() {

return "command1";
}

public boolean doWork(RobotMessageBundle bundle, Blip blip, Event evt, String params) {
blip.getDocument().append("Robot Worker 1 got the command with parameter string : " + params);
return true;
}

public String getDescription() {
return "Robot Worker 1";
}
}

 

Worker2.java

 

package com.gaejexperiments.waverobot;

import org.wadael.waverobotfrmwrk.advanced.RobotWorker;

import com.google.wave.api.Blip;
import com.google.wave.api.Event;
import com.google.wave.api.RobotMessageBundle;

public class Worker2 implements RobotWorker {

public boolean doWork(RobotMessageBundle bundle, Blip blip, Event evt, String params) {
blip.getDocument().append("Robot Worker 2 got the command with parameter string : " + params);
return true;
}

public String getDescription() {
return "Robot Worker 2";
}

public String getInstruction() {

return "command2";
}

}

 

Let us go through the code of one of them and you will be able to understand it:

1. To recap, a Worker implements an instruction or a single command. Each Worker class needs to implement the RobotWorker interface in the WadRobotFramework.

2. It needs to implement the getInstruction() method which returns a String. This is the instruction that the Worker will obey or perform. In our case, the command1 is being done by Worker1 class and the command2 is being done by Worker2 class respectively.  So when someone submits {GAEJRobot:command1} in the Blip, the doWork() implementation of the Worker1 class will be invoked and if they submit {GAEJRobot:command2} in the Blip, the doWork() implementation of the Worker2 class will be invoked.

3. It needs to implement the doWork() method. This method is the heart or the main implementation of the Worker. Here you will place all your processing logic. Notice that since this is an Advanced Robot, it is assumed that you would even like to make use of the Google Wave API classes directly. So you are passed in instances of RobotMessageBundle, Blip and Event classes. The last parameter passed is params and it represents any parameters passed to the robot.

You will notice in the implementation that we have done for the Worker, that we simply Append to the Blip Text saying that the Worker got its command and notice that we also print out the Parameter String. So if you logic depends on the values of the parameters passed, you can parse out the parameters here itself and perform your logic.

That is all we need to do as far as writing Java code is concerned. Of course we have the other mandatory files that we need to create, which we will cover quickly now:

Configuring the MyAppenderRobot in web.xml

We need to add the MyAdvancedRobot in the <servlet/> and <servlet-mapping/> entry to the web.xml file. This file is present in the WEB-INF folder of the project. The necessary fragment to be added to your web.xml file are shown below.

 

<servlet>
<servlet-name>MyAdvancedRobot</servlet-name>
<servlet-class>com.gaejexperiments.waverobot.MyAdvancedRobot</servlet-class>
</servlet>
<servlet>
<servlet-name>MyRobotProfileServlet</servlet-name>
<servlet-class>com.gaejexperiments.waverobot.MyRobotProfileServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyAdvancedRobot</servlet-name>
<url-pattern>/_wave/robot/jsonrpc</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>MyRobotProfileServlet</servlet-name>
<url-pattern>/_wave/robot/profile</url-pattern>
</servlet-mapping>

 

Notice that we also have the ProfileServlet configured here, which is a good and recommended thing to have for your Robot. The ProfileServlet class is covered in the next section.

ProfileServlet.java

The implementation is straightforward and contains the ApplicationId that I have used for my AdvancedRobot. You can replace it with your id.

 

package com.gaejexperiments.waverobot;

import com.google.wave.api.ProfileServlet;

public class MyRobotProfileServlet extends ProfileServlet {

@Override
public String getRobotAvatarUrl() {
return "http://myinfoagent.appspot.com/_wave/myimage.jpg";
}

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

@Override
public String getRobotProfilePageUrl() {
return "http://myinfoagent.appspot.com";
}

}

 

Creating the MyAdvancedRobot capabilities.xml files

We need an additional file to describe the capabilities of the Robot that we have written. This file is called the capabilities.xml and it needs to reside in a certain location. You need to create a _wave directory inside of the war directory of your project. The location of this file is  war/_wave directory.

You will need to create the _wave directory and create the capabilities.xml file over there. The capabilities file shown below is pretty straightforward and is shown below:

 

<?xml version="1.0" encoding="utf-8"?>
<w:robot xmlns:w="http://wave.google.com/extensions/robots/1.0">
<w:capabilities>
<w:capability name="BLIP_SUBMITTED" content="true" />
</w:capabilities>
<w:version>1</w:version>
</w:robot>

 

Deploying the Application

To deploy the application, you will need to first create your Application ID. The Application Identifier can be created by logging in at http://appengine.google.com with your Google Account. You will see a list of application identifiers already registered under your account (or none if you are just getting started). To create a new Application, click on the Create Application button and provide the Application Identifier as requested. Please note this name down since you will be using it for deployment.

For e.g. I have registered an application identifier named myinfoagent.

To deploy the application, follow these steps (they should be familiar to you now):

  1. Click on the Deploy Icon in the Toolbar.
  2. In the Deploy dialog, provide your Email and Password. Do not click on Deploy button yet.
  3. Click on the App Engine Project settings link. This will lead you to a dialog, where you need to enter your Application ID [For e.g. my Application Identifier myinfoagent]
  4. Click on OK. You will be lead back to the previous screen, where you can click on the Deploy button. This will start deploying your application to the GAEJ cloud. You should see several messages in the Console window as the application is being deployed.
  5. Finally, you should see the message “Deployment completed successfully”.

MyAdvancedRobot in Action

Your application is going to be available at the http://yourapplicationid.appspot.com. In my case, the application is available at http://myinfoagent.appspot.com.

You can test for the presence of your robot capabilities file by simply typing in the following:

http://yourapplicationid.appspot.com/_wave/capabilities.xml [Replace yourapplicationid with the Application ID that you have] and you should see the capabilities.xml file being served up.

To test out the Robot, you need to launch the Google Wave client and login in with your account by going to http://wave.google.com. On successful login, you will be inside the Wave client from where you can create a new wave by clicking on the New Wave link. I then add the myinfoagent@appspot.com, which is our AdvancedRobot to the Wave as a participant as shown below:

 

On addition, the AdvancedRobot will announce itself to the Participants. This is the method getRobotSelfIntroduction() that we implemented in the MyAdvancedRobot.java class. The output is shown below:

Now, we type in the message /help in the beginning of the Blip and submit it as shown below:

When submitted, the method getUsage() of the MyAdvancedRobot class is invoked and it displays the commands and any help instruction that you provided in there.

The next thing we do is give a command to the first Worker as shown below and click on the Done button.

This will invoke the doWork() method on the Worker1 class since this class has indicated that it implements command1 as mentioned in the getInstruction() method of the class. The response of the Worker is shown below:

As you can see we did not pass any parameters to it, so it printed out null.

Now, let us invoke the second Worker as shown below and notice that we are passing parameters to it now:

When we click on Done, the doWork() method on the Worker2 class since this class has indicated that it implements command2 as mentioned in the getInstruction() method of the class. The response of the Worker is shown below:

Notice that the parameter string was now passed and successfully displayed. You can easily parse out the String and perform your logic.

Conclusion

This concludes the 2-part tutorial on creating Simple and Advanced Robots using the WadRobotFramework. The framework takes away much of the API that you need to know to get going with the Wave API and instead lets you focus on the core of our Robot logic. The framework is still is its early stages and would do with some feedback from you.

Till the next episode, just Smile and Wave!

 

About these ads

5 thoughts on “Episode 12 : Writing an Advanced Google Wave Robot using WadRobotFramework

  1. Romin,

    thank you for this article.

    “Simple yet powerful” sums up really well how I wanted it.

    My next work item is to make generate a project skeleton to win time over the configuration steps.

    I’ll create a wave with a poll to know which solution do users prefer between ANT, external app (swing), eclipse plugin.
    Even if I don’t know how to code an eclipse plugin.
    So, like any other os projects, volunteers are welcome.

    Search public waves for wadrobotframework

    Cheers,

    Jérôme
    @wadael http://twitter.com/wadael

  2. Romin,

    Let me add two informations

    1/
    In fact, I do NOT recommend overriding getUsage() for there is already a behaviour implemented that will fit most needs as for each worker, we get a line of form

    getInstruction() : getDescription()

    It lists the usages of all (non stealth) workers and gives a sample usage, that uses the delimiters you may have chosen.
    Default delimiters are { and }

    2/ About /help
    If there is more than one robot based on WithWorkersRobot in a conversation and someone types /help (at the beginning of the blip) then all the WWR-based robots will show their usage.

    I’ll keep this behaviour and add “help” as a default instruction for each robot
    so that {myrobot:help} displays the usage of the robot identified with myrobot.

    Happy waving
    Have fun
    Stay tuned for the project generator

    Jérôme
    @wadael http://twitter.com/wadael

  3. As I commented in Episode 11, the new API is out for the public, and we are encouraging article authors to port existing articles like this one. If you do port it, please let us know, so that we keep it listed in the documentation. Thanks!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s