By default, when your client subscribes to an Atmosphere endpoint, it receives a default “Broadcaster” object which will subscribe to all topics. You’d do well to read all about it.
However, you probably want to target messages: Think, “topics” and “subtopics”. The goal of this post is to demonstrate a basic example of subscribing and broadcasting to a “topic” using Atmosphere’s Broadcasters, as they comprise the core publish/subscribe “topic” mechanism in Atmosphere
All source code for this example is available on the github.
We’ll set up a chat similar to the previous example. However, this time, we’ll also add a 2nd Websocket connection, and it’ll get pushed only messages whose content contains the String “pie”. A 2nd div will show all the messages pushed to this “pie-only” subscription. It’ll look thusly:
Here’s how we’ll do it, starting on the server.
- ChatService exposes an endpoint at “/atmosphere/chat”. The websocket connections will start at that URL
- ChatService will listen for incoming subscriptions, and when it gets a subscription to “/atmosphere/chat/pie”, it’ll assign the “/pie” Broadcaster to that subscription.
- All other subscriptions get assigned to an “/all” Broadcaster.
- When new messages are posted, the onRequest() method will broadcast the message to the “/all” subscriptions
- In addition, if the incoming message contains the String “pie”, it’ll broadcast the message to the “/pie” subscriptions
On the client, we’ll set up two requests, one to “/atmosphere/chat/all”, and one to “/atmosphere/chat/pie”. Note that “/all” here isn’t a magic word. I simply chose not to broadcast to Atmosphere’s default “/” because if I did that, any “/pie” subscriptions would get those messages as well. Not good.
- In index.gsp, 2 divs are created, one to hold “all” messages and one to hold all the “pie” messages
- application.js creates 2 “request” objects as described above
- various event handlers are attached to those objects
- those objects are subscribed to the Atmosphere object
- the “onMessage()” event handlers attached to those objects receive broadcasts from the server and push content to their respective divs
Let’s look closer, starting at the server.
Starting at ChatService.groovy, our “root” endpoint is at /atmosphere/chat, which is configured here:
We then have the required onRequest() method, which handles subscriptions (via GET) and message pushes (via POST):
onRequest() : GET
In the first “if()” statement that looks at the request’s “pathInfo”, we see the first use of Atmosphere’s “BroadcasterFactory.lookup( String path )” method, which will create a new Broadcaster at the specified path if it doesn’t exist, and fetch it if it does exist. Rather than let the Event object’s Broadcaster object remain as the default, we assign it to the Broadcaster at the specified path.
When POST requests come in — when messages are pushed from client to server — we pull out the data, broadcast to “/all”, and then decide whether to broadcast to the “/pie” Broadcaster.
The required “onStateChange()” method is standard and makes no provision for the Broadcaster or message. It flushes the data to the client
Again, you’ll want to read all about Broadcasters.
On the client side, we have conceptually simple code — as described above — though we do write a fair amount of JS to configure the websocket connections.
First, the two Request objects:
These are purposefully verbose, though in the real world you’d not have this much duplication, instead perhaps using a base object and extending it with request-specific configuration. The important thing here is simply that each requests targets a specific sub-URL hanging off the main “/atmosphere/chat” endpoint.
Note: The sub-URL (“/all” and “/pie”) are not meaningful to atmosphere. Rather, as we saw above, the onRequest() object merely uses that as a convenient mechanism for assigning a specific Broadcaster. We could also have used additional URL data, headers, etc to determine different Broadcasters!
The onMessage() handler, which completes the event chain from a client message post, to the server, and back to any subscribed clients, is in our case quite simple:
It yanks data out of the pushed message and then calls a method which ultimately appends the data to the DOM in the appropriate div.
When you read the Broadcaster docs, you’ll start to understand that I’m scratching the surface here. You might be wondering: How might I implement a person-to-person chat? If I were writing a Facebook clone, how might I emulate the popups whenever a post I’ve “subscribed to gets an update? If I were writing a GMail clone, how would I emulate the yellow “New Messages” block that appears when you’re reading one message and new responses come in?
You’d start at Broadcasters.
Future posts in this series will take a deeper dive into Atmosphere Broadcasters and explore different strategies for assigning Broadcasters.