Add-Robot

    A robot which can stores user favorite robots using the JDO data store and add them automatically to the wavelet when it was added as participant.

The basic features of this are to add, delete and view. When the user enter the command “#add.robots.save”
it’s save all the current robots in the wavelet and automatically list the current favorite robots. And when users enter “#add.robots.delete<space><robotname>” it will delete that specific robot in the favorite.

Add Robots Samples

 

This Is How We Do It

    First Create an app engine project name AddRobot and setup the environment for JDO and Robots

    It is easier if you’re going to use eclipse with Google plug-in cause some configuration there is auto generated.

To set up Wave Robots refer to:

http://code.google.com/apis/wave/extensions/robots/java-tutorial.html

To set up JDO refer to:

http://code.google.com/appengine/docs/java/gettingstarted/usingdatastore.html

    Now we are ready, after setting up the environment. We have to create a datastore where we can save our favorite robots. Be sure that you already have PersistenceManagerFactory (Refer to JDO site above). Now let’s Create a new class named Robots with properties id, username and robotname with a private identifier and should be annotated with @Persistent to tell DataNucleus to store them as properties of objects in the App Engine datastore. And id should be annotated with @PrimaryKey to set id as primary key. It should be look like this:

package com.google.addrobots.jdo;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Robots{
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;
    @Persistent
    private String userName;
    @Persistent
    private String robotsName;
    public Robots(String userName, String robotsName) {
        this.userName = userName;
        this.robotsName = robotsName;    
    }
    public Long getId() {
        return id;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getRobotsname() {
        return robotsName;
    }
    public void setRobotsname(String robotsName) {
        this.robotsName = robotsName;
    }
}

    Now let’s begin with the robot, create a servlet let say” AddRobbotServlet that extends the AbstractRobotServlet class (from the com.google.wave.api package) and implement the method processEvents() which accepts a RobotMessageBundle object. As the first functionality of add-robot is to add automatically all the favorite robot to the wavelet, so we need to implement the condition if bundle.wasSelfAdded(). If that returns true it will automatically add all favorite robots as participants of the wavelet, it should be like this:

package com.google.addrobots;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import com.google.addrobots.jdo.PMF;
import com.google.addrobots.jdo.Robots;
import com.google.wave.api.*;

@SuppressWarnings("serial")
public void processEvents(RobotMessageBundle bundle) {
    Wavelet wavelet = bundle.getWavelet();
    String creator = wavelet.getCreator();
 if(bundle.wasSelfAdded()) {           
    //Search if theres existing favorites robots and add it to participants
    PersistenceManager pm = PMF.get().getPersistenceManager();
    Query query = pm.newQuery(Robots.class,"userName == userNameParam");
     query.declareParameters("String userNameParam");
     List<Robots> bots=(List<Robots>) query.execute(creator);
     if(!bots.isEmpty()){
             for (Robots r : bots) {
                wavelet.addParticipant(r.getRobotsname());
        }
}
    pm.close();
Blip blip = wavelet.appendBlip();
                TextView textView = blip.getDocument();        
                //append new string to blip for confirmation
                textView.append("Favorite Robots has been added as participant");                  
             }
    }

    Then for the other adding and deleting command, we need to get what the users input in the blip so we need to use the event type blip submitted. Now we need to compare that words that the user enters on the blip. So were going to create again a condition filtering if the user input if #add-robots.save or #add-robots.delete in the blip. If the user enter #add-robots.save all the robots in the wavelet will be save as favorite that would be look like this:

for (Event e : bundle.getEvents()) {
    if (e.getType() == EventType.BLIP_SUBMITTED) {
    //get event blip
    Blip blip = e.getBlip();           
    TextView textView = blip.getDocument();
    //get blip text
    String strBlip = e.getBlip().getDocument().getText();
    String delimiter = " ";
    String[] temp = strBlip.split(delimiter);  
//#add-robots.save ivoked
    if(temp[0].trim().equals("#add-robots.save")|| temp[0].trim()=="#add-robots.save"){
    //delete existing robot first before adding new list
    //query to search robots
PersistenceManager pm = PMF.get().getPersistenceManager();
    Query query = pm.newQuery(Robots.class,"userName == userNameParam");
    query.declareParameters("String userNameParam");
    List<Robots> bots=(List<Robots>) query.execute(creator);
    if(!bots.isEmpty()){
        for (Robots g : bots) {
        //delete robots
        pm.deletePersistent(g);
        }
        }
//get all participant
List participant = wavelet.getParticipants();
    //get robot from participant check if with a host of appspot.com
    List<String> appspot = getAllAppspot(participant);
    textView.delete();
//save List to JDO
textView.append("All Robots here has been Saved To Favorites \n");
        for(String bot : appspot)
            {
Robots rob = new Robots(creator,bot);
        pm.makePersistent(rob);
            textView.append(bot+"\n");
            }
        pm.close();
        }

    Notice that we use Java String method split to get what the input we need from the blip. And notice also that we delete first all existing favorite robot before we add a new favorite, you can create other logic for that but for me that is much easier. Now for deleting our unwanted robot, we will again get the user input and compare if the input #add.robots.delete<space><robotname> then that specific robot with that name will be deleted. Now let’s continue with the code:

else if(temp[0].trim().equals("#add-robots.delete")|| temp[0].trim()=="#add-robots.delete"){
PersistenceManager pm = PMF.get().getPersistenceManager();
    Query query = pm.newQuery(Robots.class,"userName=='"+creator+"' && robotsName=='"+temp[1]+"'");
    List<Robots> bots=(List<Robots>) query.execute(wavelet.getCreator());
        if(!bots.isEmpty()){
            for (Robots g : bots) {
 //delete robots
pm.deletePersistent(g);
            wavelet.removeParticipant(temp[1]);
            }
            }
    textView.delete();
    textView.append(temp[1]+" Has Been Deleted");
    }else{   //some additional code here for other function
}  
    }
    }  
}

Now that’s it, we are finish with the robot servlet. Just read the code comments for its purpose. So it’s time to create a Servlet Mapping, you have to map this servlet to the URL path /_wave/robot/jsonrpc, edit the file war/WEB-INF/web.xml so that it looks like this:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
  <!-- Servlets -->
      <servlet>
        <servlet-name>AddRobots</servlet-name>
        <servlet-class>com.google.addrobots.AddRobbotServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>AddRobots</servlet-name>
        <url-pattern>/_wave/robot/jsonrpc</url-pattern>
    </servlet-mapping>
</web-app>

    And finally This robot needs a configuration file where we can configure the robot capability . From the Package Explorer, create a new folder named war/_wave/, then inside this folder create a file named capabilities.xml with the following contents:

<?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"/>
  </w:capabilities>
  <w:version>2</w:version>
</w:robot>

    As you can see, we use special event BLIP_SUBMITTED. This work whenever a blip is submitted so our robot can respond to this wave events. Now were done we can test now our robot, you can test your new robot by deploying it to App Engine, then adding it to a wave. Please refer again to the site above.

Bookmark and Share

At-Buddy

When I was just starting to learn Google Wave Robots, I decided to create a robot that replaces the “at” phrase to “@” since Google does not allow Danish users to create an “@” in the current wave implementation. I’ll give you a walk-through on how I managed to create the “at-buddy.”

I started this robot using the wave robot tutorial which can be found here.

What we need to do is to replace the blip’s text while the user is typing so we need to register the event “DOCUMENT_CHANGED” in the capabilities.xml. For more information regarding capabilities.xml, you can refer to this post.

Once the capabilities are updated, we can now catch the event in our servlet. Let’s insert this in our “Process Events” method.

for (Event e : bundle.getEvents()) {
if (e.getType() == EventType.DOCUMENT_CHANGED) {

if(!e.getModifiedBy().equalsIgnoreCase("at-buddy@appspot.com"))
{

// get event blip
Blip blip = e.getBlip();
TextView textView = blip.getDocument();
// get blip text and replace (at) to @
String strBlip = e.getBlip().getDocument().getText().toLowerCase();

//check if text has an "(at)"
if(match(strBlip,".*\\([aA][tT]\\).*").length()&gt;0)
//if(strBlip.indexOf("(at)")&gt;0)
{

//get the index of the first "(at)"
int index = strBlip.indexOf("(at)");

//replace "(at)" to @
textView.replace(new Range(index, index+4), "@");

}
}
}
}

That’s it! now you can deploy your robot and try it out.

Bookmark and Share

Capabilities.xml

As I was creating my first robot, I wasn’t surprised on how little the number of resources available in the internet were, specially about capabilities.xml. I didn’t find any documentation on its schema yet. So, I decided to write about what I’ve learned.

My robot only needs the “document_changed” and “wavelet_self_added” events but I wanted to know what possible tags  I can add up to the file for my robot settings. As of now I only know two properties that you can set in the file, the capabilities/events and the version of the file.

Here’s my capabilities.xml
Continue reading Capabilities.xml

Bookmark and Share

Debugging Wave Robots

Debugging robots made in Google Wave can be a little difficult because it can only be tested on the server and it can be difficult to see how it is sent.

A way I have found is quite useful is to use the app engine log. On the log tab select “Requests only” and then see that data being sent to the robot. It is this data the robot can see, if it is not here then the robot API is not receiving the data.

I hope this help you debug and get at better understanding of your robot.

Update 19 aug 2009: If you want to format the JSON try the JSON formatter.

Bookmark and Share