Skip to content
Alex Vigdor edited this page Aug 28, 2018 · 2 revisions

Groovity events

Groovity-events is a module offering a mechanism for multiplexing asynchronous communications between browser and server over a single WebSocket. Groovity-events comes with a javascript library that provides the browser-side integration layer, and the server side of the web socket is implemented using the accept/offer tags that are part of groovity's general purpose async computing framework.

From the browser perspective, using groovity-events is a lot like using "socket.io", a well-known framework for Node.js servers that implements similar functionality. Using javascript, the browser can either subscribe to named events that might come from the server, or emit events to the server with an optional callback. Only two javascript events are predefined, 'open' and 'close', which allow your application to react to the websocket connections status; all other events are defined by your application.

To install the javascript library on a web page, an "events" tag is provided, that also gives you a chance to change the name of the socket.

<html>
<head>
  <g:events/>
  <script type="text/javascript">
    //emit with no callback is fire and forget from client to server
    socket.emit('page-load', window.location.href)

    //registering an 'open' handler will fire on first connect AND after a reconnect
    socket.on('open', function(){
      setOnline(true)
      //emit with a callback mimics a classic request/response
      socket.emit('fetch-initial-data', window.location.href, function(data){
    		renderInitial(data);
    	})
    })

    //registering a 'close' handler will fire every time the websocket is closed
    socket.on('close', function(){
        setOnline(false)
    })

    //here, on is used to subscribe to server-initiated events
    socket.on('data-update', function(data){
        update(data)
    })

    socket.on('private-message', function(data){
        showPM(data)
    })
  </script>

Programming the server side of groovity events is performed with emit and on tags that are analogous to the client socket methods.

The body of the on tag is used to process the payload of incoming events, and it may take a second parameter in order to consult the websocket session in addition to the message data. The return value of the body is serialized and passed to the javascript callback on the client side.

on(event:'page-load'){ page ->
		log(info:"Page load ${page}")
}

on(event:'fetch-initial-data'){ id, session ->
    load('/some/lib').getData(id, session.userPrincipal.name)
}

Server-initiated events are kicked off using the emit tag for distribution to any number of users, or can be targeted to a specific user. For example, a data watcher can be used to trigger pushes to clients:

static alertWatcher
static pmWatcher

static start(){
	def factory = load('/data/factory')
	alertWatcher = factory.watch('alert'){ ptr ->
    //public broadcast of alert event
		emit(event: 'alert'){ factory(ptr) }
	}
	pmWatcher = factory.watch('privateMessage'){ ptr ->
    //private offer of event only to recipient user
		emit(event: 'private-message', user: pm.recipient.name){ factory(ptr) }
	}
}

static destroy(){
  alertWatcher.cancel(true)
  pmWatcher.cancel(true)
}

Notice that the client-side javascript does not give you the ability to specifically register for user-specific events, as this would be vulnerable to abuse; instead, the server component of the events framework automatically doubles every event subscription for authenticated users to receive private or public events. So you must have a security policy enabled on your events endpoint to be able to deliver server-initiated per-user events. You can control the policy applied to the events websocket by setting the system property or configuration variable "groovity.event.policy".

For example, the groovity-portal framework contains a script that forces the event socket to use the portal authentication policy:

static init(){
	System.setProperty('groovity.event.policy','/groovity/portal/lib/authPolicy')
}
Clone this wiki locally