James Williams
LinkedInMastodonGithub

Griffon on Google Wave

You can log this post under something that while fun to do just to say you can, it's not very practical. For starters, startup time for most applets isn't exactly what you'd call speedy. Also, because of Griffon's use of Groovy, it adds another 5MB of jars for the initial download. Every extra second of waiting gives a potential user a reason to leave before the download is complete. So remember that you've been warned. The smallest test you can do to demonstrate Wave is a simple count incrementer that updates all clients when the count is incremented. Our view and model are relatively simple:

TestWaveView.groovy

application(title:'TestWave',
  //size:[320,480],
  pack:true,
  //location:[50,50],
  locationByPlatform:true,
  iconImage: imageIcon('/griffon-icon-48x48.png').image,
  iconImages: [imageIcon('/griffon-icon-48x48.png').image,
               imageIcon('/griffon-icon-32x32.png').image,
               imageIcon('/griffon-icon-16x16.png').image]
) {
    vbox {
        label(text:bind{model.number})
        button(text:"Increment", actionPerformed:{controller.increment()})
    }
}

TestWaveModel.groovy

import groovy.beans.Bindable

class TestWaveModel {
   @Bindable number
   def timer
}

Our controller is where the interesting stuff happens. We are using Javascript proxies to evaluate a javascript function in out gadget xml to increment the count. That function increments the count on the gadget state. Separately, our Griffon applet polls a javascript getValue function which retrieves the state of the variable. The optimal solution would have been to register a function with the stateChanged callback to invoke stuff on the Java side. The problem is that to do so, the javascript has to reach into the applet and invoke a function on the controller. It can be done, it's just a little easier to poll from the controller. The value is pulled every second.

TestWaveController.groovy

import java.applet.*
import java.util.*
import java.util.timer.*
import netscape.javascript.*

class TestWaveController {
    // these will be injected by Griffon
    def model
    def view

    void mvcGroupInit(Map args) {
        // this method is called after model and view are injected
    getValueFromWave()

    model.timer = new Timer()
    def task = new GroovyTimerTask()
    task.closure = {getValueFromWave()}
    model.timer.schedule(task, 2000,1000)
    }

    def increment = {
    try {
        def win = (JSObject)JSObject.getWindow((Applet)app)
            win.eval("javascript:increment()")
    } catch(Exception ex) {
        ex.printStackTrace()
    }
}

def getValueFromWave = {
    try {
        def win = (JSObject)JSObject.getWindow((Applet)app)
        model.number = win.eval("javascript:getValue()")
    } catch (Exception ex) { }
    }
}

Lastly, our gadget/applet franken-code. As referenced before, the increment function grabs a number variable from the gadget state and sets it to 0 if it doesn't exist. It increments it and puts it back of the wave state. getValue simply returns the value of num.

<?xml version="1.0" encoding="UTF-8"?>
<Module>http://dl.getdropbox.com/u/738191/staging/applet.html  
<ModulePrefs title="Hello Wave">    
    <Require feature="wave" />   
</ModulePrefs>  
<Content type="html">    
    <![CDATA[       
        <script>                    
            function increment() {          
                var num = wave.getState().get("num", 0);            
                num++;          
                wave.getState().submitDelta({"num":num});       
            }       
            function getValue() {           
                return wave.getState().get("num", 0);       
            } 
        </script>
        <script src="http://java.com/js/deployJava.js"></script>
        <script>    
            var attributes = {id: 'TestWave',                      
                codebase:'<codebase>',              
                code:'griffon.applet.GriffonApplet',            
                archive:'griffon-rt-0.2-BETA.jar,TestWave.jar,plugin.jar,groovy-all-1.6.4.jar',                      
                width:'480', height:'320'};    
            var parameters = {fontSize:16,                      
                java_arguments: "-Djnlp.packEnabled=true",                      
                jnlp_href:'<codebase>/applet.jnlp',                      
                draggable:'true',                      
                image:'griffon.png',                      
                boxmessage:'Loading TestWave',                      
                boxbgcolor:'#FFFFFF', boxfgcolor:'#000000',                      
                codebase_lookup: 'false'} ;    
            var version = '1.5.0' ;    
            deployJava.runApplet(attributes, parameters, version);
        </script>
        <!--<APPLET CODEBASE='<codebase>'        
                CODE='griffon.applet.GriffonApplet'        
                ARCHIVE='griffon-rt-0.2-BETA.jar,TestWave.jar,plugin.jar,groovy-all-1.6.4.jar'        
                WIDTH='240' HEIGHT='320'>    
                <PARAM NAME="java_arguments" VALUE="-Djnlp.packEnabled=true">    
                <PARAM NAME='jnlp_href' VALUE='<codebase>/applet.jnlp'>    
                <PARAM NAME='dragggable' VALUE='true'>    
                <PARAM NAME='image' VALUE='griffon.png'>    
                <PARAM NAME='boxmessage' VALUE='Loading TestWave'>    
                <PARAM NAME='boxbgcolor' VALUE='#FFFFFF'>    
                <PARAM NAME='boxfgcolor' VALUE='#000000'>    
                <PARAM NAME='codebase_lookup' VALUE='false'></APPLET-->         
    ]]>  
</Content></Module>

If you're one of the lucky people with Wave accounts, wave to me at j@wavesandbox.com or James.L.Williams@googlewave.com and I'll give you a demo.