Skip to Content

We are one step closer to our goal: the seamless integration of Google Wave into SAP Applications

Short Timeline

  1. Display a wave in portal or WDA Application: Dagfinn Parnas Blog SAP and Google Wave – Embedding waves in SAP NetWeaver Portal
  2. Set/Get Data from SAP ERP using wave robot: my Blog VCD #16 – The REST Bot: Behind the scenes
  3. Active creation and manipulation of an embedded wave: available since Active Robot API V2 -> this Blog
  4. Inform the WDA Application about wave changes: coming soon -> Push-(Notification-) Services announced for NW 7.02 (7.03?)

What is the difference between a robot and an “active robot”?

A robot is an automated participant on a wave. A robot can read the contents of a wave in which it participates, modify the wave’s contents, add or remove participants, and create new blips and new waves. In short, a robot can perform many of the actions that any other participant can perform

More Infos Google Wave Robots API

But what is now an “active robot”? Until last week it was not possible to manipulate or create waves from outside Google Wave. This has changed with the new Robot API v2:

Robots may not only act as passive entities, responding to events as they occur, but may initiate their own operations as well. The Active Robot API allows robots to send operations to Wave outside of the event-driven model. In particular, this API allows for the following:

  1. Performing scheduled tasks (e.g. cron jobs) at specified intervals
  2. Creating new waves within Google Wave in response to actions within Google Wave itself
  3. (Most importantly) Responding to outside events or services by updating waves or creating new ones

More Infos The Active Robot API

Demo

How it works

First we have to create a regular robot that does … exactly nothing. We don’t want to react on wave changes in this demo. Possible but not necessary.

The robot just creates an instance of itself:

from waveapi import robotfrom waveapi import appengine_robot_runnerif __name__ == '__main__':   
          bot = robot.Robot('se38xxx', image_url='http://se38xxx.appspot.com/assets/service.png', profile_url='http://se38xxx.appspot.com/')   
          appengine_robot_runner.run(bot)

The more interesting part is the Post-Handler-Class, where we implement the whole logic:

import cgiimport loggingfrom waveapi
import robotfrom google.appengine.ext
import webappfrom google.appengine.ext.webapp.util
import run_wsgi_appclass
MainPage(webapp.RequestHandler):
          __bot = None       
          def __create_robot(self):       
                    self.__bot = robot.Robot('se38xxx', image_url='http://se38xxx.appspot.com/assets/service.png', profile_url='http://se38xxx.appspot.com/')       
                    CONSUMER_KEY = '493DEMO8625'       
                    CONSUMER_SECRET = 'xmW/6cHk6DEMOwIu1M0poUVd'       
                    self.__bot.setup_oauth(CONSUMER_KEY,                               
                                           CONSUMER_SECRET,                                s
                                                                         erver_rpc_base='http://gmodules.com/api/rpc')   
          def __add_blip(self):       
                    logging.debug("Add Blip")       
                    waveId = cgi.escape(self.request.get('waveid',                                             
                                        default_value=''))               
                    if waveId == '':           
                              return               
                    waveId = 'googlewave.com!w+' + waveId       
                    waveletId = 'googlewave.com!conv+root'               
                    logging.debug("Fetching wavelet: " + waveId+", " + waveletId)               
                    wavelet = self.__bot.fetch_wavelet(waveId, waveletId)       
                    text = cgi.escape(self.request.get('text',                                           
                                      default_value='No text given'))                         
                    wavelet.reply(text)       
                    self.__bot.submit(wavelet)       
                    self.response.out.write('OK Blip created')           
          def __create_wave(self):               
                    wave_user = cgi.escape(self.request.get('wave_user', default_value='xxx@googlewave.com'))                       
                    wavelet = self.__bot.new_wave(domain='googlewave.com',                                      
                                                  participants=[wave_user],                                     
                                                                                            message='New message',                                     
                                                                                            submit=True)       
                    title = cgi.escape(self.request.get('title', default_value='No title given'))               
                    wavelet.title = title       
                    wavelet.root_blip.append('This is an Integration Test')       
                    wavelet.root_blip.append_markup('between SAP WDA and Google Wave')
                    wavelet.reply('And another Blip in Wave ' + wavelet.wave_id)        
                    self.__bot.submit(wavelet)                        
                    self.response.out.write('OK ' + wavelet.wave_id)        
          def post(self):
        action = cgi.escape(self.request.get('action', default_value=''))                
                    if action == '':            
                              return      
        self.__create_robot()
        if action == 'new_wave':            
                              self.__create_wave()                    
                    if action == 'add_blip':            
                              self.__add_blip()
          def main():    
                    application = webapp.WSGIApplication(http://scn.sap.com/people/uwe.fetzer/blog/2010/03/11/wave-islands-in-webdynpro-abap/('/app.*', MainPage), debug=True)    
                    run_wsgi_app(application)
if __name__ == "__main__":    
          main()       

Short explanation

application = webapp.WSGIApplication(http://scn.sap.com/people/uwe.fetzer/blog/2010/03/11/wave-islands-in-webdynpro-abap/('/app.*', MainPage), debug=True)

-> we react on all requests on the path “/app”

CONSUMER_KEY = '493DEMO8625'
CONSUMER_SECRET = 'xmW/6cHk6DEMOwIu1M0poUVd'

-> the OAuth keys we received from the registration of our robot

action = cgi.escape(self.request.get('action', default_value=''))

-> depending on the “action” parameter, we can process “new_wave” or “add_blip” steps. Not an REST interface yet, I know, please no comments on that

Now we can call the Post-Handler from within the WDA application:

METHOD create_wave . 
          DATA: lr_client  TYPE REF TO if_http_client     
              , lv_content TYPE string     
                    , lo_nd_main TYPE REF TO if_wd_context_node     
                    , lo_el_main TYPE REF TO if_wd_context_element     
                    , ls_main TYPE wd_this->element_main     
                    , lv_title TYPE wd_this->element_main-title     
                    , lv_own_user TYPE wd_this->element_main-own_user      
                    .
          "*--- prepare message ---*  
          cl_http_client=>create(    
                    EXPORTING      
                              host = 'se38xxx.appspot.com'    
                    IMPORTING      
                              client = lr_client      
                              ).
          lr_client->request->set_header_field(    
                    name = '~request_uri'    
                    value = '/app/'    
                    ).
          "navigate from <CONTEXT> to <MAIN> via lead selection
    lo_nd_main = wd_context->get_child_node( name = wd_this->wdctx_main ).
          "get element via lead selection
    lo_el_main = lo_nd_main->get_element( ).
          "get single attribute
    lo_el_main->get_attribute(      
                    EXPORTING        
                              name =  `TITLE`      
                    IMPORTING        
                              value = lv_title ).
          "get single attribute
    lo_el_main->get_attribute(      
                    EXPORTING        
                              name =  `OWN_USER`      
                    IMPORTING        
                              value = lv_own_user ).
          lr_client->request->set_form_field(    
                    name  = 'title'    
                    value = lv_title    
                    ).
          lr_client->request->set_form_field(    
                    name  = 'wave_user'    
                    value = lv_own_user    
                    ).
          lr_client->request->set_form_field(    
                    name  = 'action'    
                    value = 'new_wave'    
                    ).
          lr_client->request->set_header_field(    
                    name  = '~request_method'    
                    value = 'POST'    
                    ).
          "*--- send ---*
          lr_client->send( ).  
          lr_client->receive( ).  
          lv_content = lr_client->response->get_cdata( ).  
          lr_client->close( ).
          "*--- store created wave id ---*
          DATA: lv_waveid TYPE wd_this->element_main-waveid      
              , lv_dummy TYPE string      
                    .
          "@TODO fill attribute
          IF lv_content(2) = 'OK'.    
                    SPLIT lv_content AT '+' INTO lv_dummy lv_waveid.  
          ENDIF.
          "set single attribute
          lo_el_main->set_attribute(    
                    name =  `WAVEID`    
                    value = lv_waveid ).
ENDMETHOD.

To embedd the wave I have an Iframe on the page which call an BSP (with the received WaveID as parameter):

<%@page language="abap"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    <title>Google Wave Embed API Example: Simple Wave</title>
    <script src="http://wave-api.appspot.com/public/embed.js" type="text/javascript"></script>
    <script type="text/javascript">
    function initialize() {
    var wavePanel = new WavePanel('https://wave.google.com/wave/');
    var uiConfig = new WavePanel.UIConfig();
    uiConfig.setFooterEnabled(true);
    uiConfig.setHeaderEnabled(true);
    uiConfig.setToolbarEnabled(true);
    uiConfig.setBgcolor('#FFF');
    uiConfig.setColor('#000');
    uiConfig.setFont('verdana');
    uiConfig.setFontSize('12px');
    wavePanel.setUIConfigObject(uiConfig);
    wavePanel.loadWave('googlewave.com!w+<%= waveid %>');
    wavePanel.init(document.getElementById('waveframe'));
    }
    </script>
  </head>
  <body onload="initialize()">
    <div id="waveframe" style="width: 100%; height: 400px"></div>
  </body>
</html>

Outlook

As I have described in the timline, the only missing part is the hook back from wave to the WDA application. But Thomas J. promissed it will be part of NW 7.02 (or 7.03?)

Now it’s your turn. What can we do with this solution?

G+

To report this post you need to login first.

2 Comments

You must be Logged on to comment or reply to a post.

Leave a Reply