Posts tagged ‘Google Wave’

Developing for Google Wave

Hi! I don’t know how many of you had the chance to play with Google Wave at all, but I had the opportunity to fool around with it a bit. I’m quite amazed of how things turned out, even though their interface needs quite a bit of work. The overall performance of the system is a little disappointing, but I try to keep in mind that this is only a preview version. As soon as it hits the beta stage, I think we’ll see some major improvement across the board.

Even though the system is only an alpha version (or preview, call it what you will), Google still released some information on how to develop for it. I will go over two different aspects of Google Wave development, which are gadgets and robots.

Gadgets

Google Wave gives you the possibility to add gadgets to¬† your waves. They’re used to display some custom data, for example, a Google Map gadget. You can pass it parameters and Google Wave will add an interactive Google Map within your Wave posts. The beautiful part? They all use shared states. That means that, whoever is participating in your wave has access to control your gadget and interact with it. Even pass in new parameters. They opened the API to developers so you can develop your own gadgets. They chose to use XML to deliver the gadget content. The gadget itself is HTML and JavaScript. Here is an example of a shared state gadget that simply increments a counter:

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="State Example" height="120">
  <Require feature="wave" />
</ModulePrefs>
<Content type="html">
<![CDATA[
<div id="content_div" style="height: 50px;"></div>
    <script type="text/javascript">

    var div = document.getElementById('content_div');

    function buttonClicked() {
      var value = parseInt(wave.getState().get('count', '0'));
      wave.getState().submitDelta({'count': value + 1});
    }

    function stateUpdated() {
      if(!wave.getState().get('count')) {
        div.innerHTML = "The count is 0."
      }
      else {
        div.innerHTML = "The count is " + wave.getState().get('count');
      }
    }

    function init() {
      if (wave && wave.isInWaveContainer()) {
        wave.setStateCallback(stateUpdated);
      }
    }
    gadgets.util.registerOnLoadHandler(init);

    // Reset value of "count" to 0
    function resetCounter(){
      wave.getState().submitDelta({'count': '0'});
    }

    </script>
    <input type=button value="Click Me!" id="butCount" onClick="buttonClicked()">
    <input type=button value="Reset" id="butReset" onClick="resetCounter()">
  ]]>
  </Content>
</Module>

Now, with this gadget, everyone participating in a wave can click on the button and increment the counter. But, that is only one part of the gadget. You need to host this code somewhere on a public web server in order for Google to be able to reach it. Now that you’re done writing the code, let’s save that file as “buttonShared.xml” and host it somewhere. Now, to insert this gadget into a Google Wave post, click on the Add Gadget by URL button and enter the full path to the “buttonShared.xml” file. You should see your button and the label next to it appear in your wave.

If you are a registered developer for Google, you can also install Extensions. Extensions are basically installers for gadgets. All you have to do is write a manifest XML file with the information about your gadget, where you want it to reside in Google Wave and the path to the gadget. Here’s an example manifest file for our button gadget:

<extension
    name="Button Gadget"
    thumbnailUrl="http://your.url.here/path/to/screenshot_buttongadget.png"
    description="Get everyone to click your button. See how high you can go!">
  <author name="Your name here"/>
  <menuHook location="toolbar" text="Add Button Gadget"
      iconUrl="https://your.url.here/path/to/icon_insertButton.png">
    <insertGadget url="http://your.url.here/path/to/buttonShared.xml"/>
  </menuHook>
</extension>

Google Wave will then insert your button to your interface toolbar and you can directly click on it to insert a button to your wave. There’s a whole series of actions you can do with those extensions that I will not post here. You can refer to the link section at the end of this post to get the full Google page about these examples.

Robots

Robots are an interesting concept in Google Wave. They are participants in a wave. They can be silent or they can be interactive. They have total control of the wave. If you want the robot to, for example, post certain things on Twitter, all you have to do is add it to the wave and it’ll start doing its thing. The way you register robots for Google Wave is through Google App Engine. Please refer to my post about Google App Engine and BlazeDS for how to setup the Google App Engine plugin for Eclipse and get started with a project.

You will need the Google Wave library. You can find it at http://code.google.com/p/wave-robot-java-client/downloads/list. You will need them all so go ahead and download all the JAR files.

Once you have your Google App Engine project setup, written, compiled and deployed to Google App Engine, simply add a new contact to your list. The contact will be <application-id>@appspot.com. For example, I’ll call mine “mytweetbot”, so the contact I will be adding to my list is mytweetbot@appspot.com.

For the sake of this example, I will simply use Google’s example for a simple bot called Parroty. Create a new Google project and call it Parroty. Go ahead and set the package to “parroty”. Once you created your project, copy the downloaded JAR files to your /war/WEB-INF/lib directory.

Create a new class in /src/parroty called ParrotyServlet. In the Create new class dialog, click on the “Browse” button next to the Superclass and search for AbstractRobotServlet. It should be in the com.google.wave.api package. Make sure you got the Inherited Abstract Methods and Generate Comments checked and click Finish.

By default, the superclass will add the processEvents method. That’s your entry point for your robot. Here, we’ll simply make the bot repeat everything said on the wave.

package com.google.wave.api.samples;

import com.google.wave.api.*;

public class ParrotyServlet extends AbstractRobotServlet {

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

        if (bundle.wasSelfAdded()) {
            Blip blip = wavelet.appendBlip();
            TextView textView = blip.getDocument();
            textView.append("I'm alive!");
        }

        for (Event e: bundle.getEvents()) {
            if (e.getType() == EventType.WAVELET_PARTICIPANTS_CHANGED) {
                Blip blip = wavelet.appendBlip();
                TextView textView = blip.getDocument();
                textView.append("Hi, everybody!");
            }
        }
    }
}

In Google’s terms, a Bundle is the whole wave information, the Wavelet is what contains the wave Blips, a Blip is an entry on a wave and the TextView is an object you put in a Blip to render text.

In order to make your robot react to certain events happening on the wave, you need to tell your robot what to listen to. To do that, create a new directory in your /war directory called “_wave”. In this new directory, create an XML file called “capabilities.xml”. This file should look like this:

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

This tells your robot to be listening to the WAVELET_PARTICIPANTS_CHANGED event.

The only thing remaining is to map our servlet. By default, Google Wave will look for the /_wave/robot/jsonrpc servlet to interact with your robot. Let’s map our class to that servlet:

<?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">
    <servlet>
        <servlet-name>Parroty</servlet-name>
        <servlet-class>parroty.ParrotyServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Parroty</servlet-name>
        <url-pattern>/_wave/robot/jsonrpc</url-pattern>
    </servlet-mapping>
</web-app>

This part is optional, but it’s also cool. You can provide a bean to your robot with its user profile. Here’s the code to the profile servlet for the Parroty robot:

import com.google.wave.api.ProfileServlet;

public class Profile extends ProfileServlet {
    @Override
    public String getRobotName() {
        return "My Robot Name";
    }

    @Override
    public String getRobotAvatarUrl() {
         return "http://My Robot Image URL";
    }

    @Override
        public String getRobotProfilePageUrl() {
        return "http://My Robot Profile URL";
    }
}

Since everything with the Google Wave robots are versioned, you need to increment your <w:version> tag in the “capabilities.xml” file to tell Google Wave that something changed. Otherwise, it’ll be cached and you’ll have to wait until the cache is refreshed to see your changes. Same thing if you want to change the set of events you want your application to listen to.

Now, all what’s left is to deploy your application to Google App Engine, and add your new contact as a participant TO A NEW WAVE. This is important. It won’t work properly if you just add the bot to an already existing wave.

Links

Wave Gadget Guide: http://code.google.com/apis/wave/extensions/gadgets/guide.html
Wave Gadget Reference: http://code.google.com/apis/wave/extensions/gadgets/reference.html
Extensions Guide: http://code.google.com/apis/wave/extensions/installers/index.html.
Java Client API Tutorial: http://code.google.com/apis/wave/extensions/robots/java-tutorial.html
Java Client API Reference: http://wave-robot-java-client.googlecode.com/svn/trunk/doc/index.html

I hope you find that information useful! Please feel free to comment and/or ask questions, I’ll try to answer as quickly as possible.

For those who are interested in developing for Google Wave but don’t have an invite yet, I have a limited amount of them. Please reply to this post with your email address and I’ll send you an invite. I have to add that you need to have a Gmail account to register to Google Wave.

Thanks for reading!

Simon