The postings on this site are the contributor's and don’t necessarily represent IBM’s positions, strategies or opinions.
June 7th, 2009 Alex Moffat Posted in GWT, Google Wave, Java | 11 Comments »
I’ve just written my first Google Wave Gadget using GWT with the Gadgets portion of the Google API Libraries for GWT. You can see it in action in the video below. I’ll describe how I got this to work and talk about the source code below the fold. The complete source for this example Wave Gadget can be downloaded.
The documentation for the Google Wave APIs includes a tutorial for developing a Wave Gadget. I pretty much followed this when building my gadget.
The first thing is to set things up to generate a correct gadget specification, as shown in the Getting Started portion of the tutorial. There are two things I had to deal with, the script tag in the Content element and the Require element in the ModulePrefs. I now have a better solution for the script tag and Require element. See Wave Gadgets with GWT – Build improvements. This also solves the isInWaveContainer issue I mention below.
First the script. This was easy. I used a script tag in my GWT module file to include wave.js file instead of using a script element inside the Content element. In the HelloWorld.gwt.xml I added.
<script src="http://wave-api.appspot.com/public/wave.js"/>
Next the Requires element. Unfortunately the GWT Gadget API libraries doesn’t provide a way to do this out of the box. However, it’s easy to add support for one. When you write a Gadget with GWT you declare which features you require by having your entry point class implement various interfaces, such as NeedsAds, that are annotated with GadgetFeature.FeatureName. At compile time these produce the correct Requires elements. I created a NeedsRpc interface and added the appropriate annotation.
@GadgetFeature.FeatureName(value = "rpc")
public interface NeedsRpc {
The entry point class HelloWorld extends Gadget and implements NeedsRpc which gets the correct <Require feature=”rpc”/> element in the ModulePrefs.
@Gadget.ModulePrefs(title = "Hello Wave World")
public class HelloWorld extends Gadget<UserPreferences> implements NeedsRpc {
There are three interesting pieces to the implementation of HelloWorld
Each requires some JSNI coding.
For getting and changing state property values I created a WaveState class with a number of static methods. Getting values is simple
public static native String get(String key) /*-{
return $wnd.wave.getState().get(key);
}-*/;
Changing values is more complex. You submit a json map with the new values for the properties you want to change. I implements a simple system that suffices for the cases I’m using here. There’s probably a neater way to construct the json object.
public static void submitDelta(Map<String, String> newValues) {
JavaScriptObject json = null;
for (Map.Entry<String, String> entry : newValues.entrySet()) {
json = addValue(json, entry.getKey(), entry.getValue());
}
submitDelta(json);
}
private static native JavaScriptObject addValue(JavaScriptObject map, String key, String value) /*-{
if (!map) {
map = {};
}
map[key] = value;
return map;
}-*/;
private static native void submitDelta(JavaScriptObject json) /*-{
$wnd.wave.getState().submitDelta(json);
}-*/;
This method of changing state is similar to the way Blueprint works behind the scenes, though we have access to a more granular set of operations, for instance changing the position of an item in a list. The API available to gadget writers does not expose all of the operations supported by the underlying Wave protocol.
Receiving notification of changes to state is done by registering a callback function. However, it’s up to the Gadget’s code to work out what has actually changed, you don’t get notification about which properties have been modified. For more complex state this could become an interesting challenge.
In my registration method I found that isInWaveContainer() was always returning false for me so I removed the check. The code continues to work but it may be that I’m doing something wrong here.
private native void registerStateChangeCallback() /*-{
var wave = $wnd.wave;
// if (wave && wave.isInWaveContainer()) { isInWaveContainer always returns false?
if (wave) {
wave.setStateCallback(@com.lombardi.waves.helloworld.client.HelloWorld::stateUpdated());
}
}-*/;
If you want to take a closer look the complete source code for the example Google Wave Gadget can be downloaded here. Overall the programming model provided by the API for interacting with the Wave state seems to be a higher level abstraction over the Wave protocol. The Blueprint data repository provides similar access to Lombardi Blueprint developers for handling versioned data, which we presented at Google I/O in 2008.
June 10th, 2009 at 9:36 pm
[...] things to make the process of building a Google Wave Gadget with GWT easier. In my previous post on Building a Google Wave Gadget with GWT I used a script element in the GWT module.xml file to include the wave.js file and I created a [...]
June 17th, 2009 at 2:36 am
Very interesting. I didn’t know Google was allowing you to have multiple accounts. If you find you need any help testing your gadgets, feel free to let me know!
June 17th, 2009 at 8:49 am
Yep, everyone gets a normal and a test account. My gadget development has paused for a while because the server where I host the gadget javascript has failed. Hopefully it will be repaired soon. What I want to try next is gadget to robot communication. I know this is possible but there is little info on how to actually do it.
July 20th, 2009 at 3:33 pm
Dear Alex,
I am currently trying hard to cast my weblily.net music score writer project into a wave gadget.
weblily.net is written with GWT using GWT’s RPC. However, it doesn’t work out. Calls from the gadget container’s proxy for the RPC requests do not get through to my server.
Do you have any experience with wave+gadget+GWT+RPC? In a wave conversation Melissa directed me to your blog. If anyone does know it, you should be the one.
Best regards,
Johannes
PS: I tried to get the gadetRCP sample running, but that one doesn’t work either http://gwt.google.com/samples/GadgetRPC-1.0.2/com.google.gwt.gadgets.sample.gadgetrpc.client.GadgetRPC.gadget.xml
July 21st, 2009 at 1:21 pm
Sorry, I’ve not used GWT RPC in a gadget. You’re probably the expert at this
I think, as you say in the issue you submitted, that the proxy is not properly configured. I’m afraid I’d be reduced to using cross site scripting techniques in this case. At some point the wave developers must (I hope) provide a proxy to let you do this.
August 5th, 2009 at 4:20 am
Hi,
I am also facing the same problem and I can’t use GWT’s RPC in my wave gadget. Did someone find a solution for that?
Cheers.
August 6th, 2009 at 9:45 am
I’ve not heard of anything yet. I think the wave team will be adding a proxy in the future to make this work.
January 6th, 2010 at 7:47 am
It seems that currently the only way for a gadget to communicate to the backend is via robot. A robot can access the Gadget’s shared state (via Gadget.getField and Gadget.setField method). Gadget can hook a callback on the state modification and Robot can react on DOCUMENT_CHANGED event (and some others) to see if the gadget’s state changed. It could be cumbersome, but this is sort of RPC…
February 1st, 2010 at 11:26 am
Hi, it seems to be working with GWT 1.5 only, but not GWT 2.0
any ideas?
February 8th, 2010 at 1:54 pm
[...] http://development.lombardi.com/?p=809 [...]
February 10th, 2010 at 6:32 pm
[...] again Alex Moffat over at Lombardi who first gave me the idea of creating Wave Gadgets in [...]