Atmosphere + Jersey: How do I have multiple broadcasters?
Asked Answered
T

2

4

I have a working Jersey/Atmosphere/Guice application which has two Atmosphere Resources. The first is pretty much a clone of the example chat application:

@Path("/chat")
@AtmosphereService(broadcaster = JerseyBroadcaster.class, path = "/chat")
public class ChatResource {

    @Suspend(contentType = "application/json")
    @GET
    public String suspend() {
       return "";
    } 

    @Broadcast(writeEntity = false)
    @POST
    @Produces("application/json")
    public Response broadcast(Message message) {
        return new Response(message.author, message.message);
    }
}

The second is a test notification resource which will be sent server-side events:

@Path("/notifications")
@AtmosphereService(broadcaster = JerseyBroadcaster.class, path = "/notifications")
public class NotificationsResource {

    @Suspend(contentType = "application/json")
    @GET
    public String suspend() {
       return "";
    } 
}

Everything is wired up correctly and works fine. However in order for me to send a server side event I issue:

MetaBroadcaster.getDefault().broadcastTo("/*", new Response(...));

Clearly, this will send the broadcast message to both resources. What I want to do is send the server side events only to the notifications resource:

MetaBroadcaster.getDefault().broadcastTo("/notifications", new NotificationResponse(...));

However, that doesn't work. I always receive the following error:

org.atmosphere.cpr.MetaBroadcaster - No Broadcaster matches /notifications.

That's because there is only one broadcaster registered; the JerseyBroadcaster on /*.

The question is: how do I make it so that these two resources have different broadcasters with different IDs/Names?

Tsan answered 8/2, 2014 at 18:26 Comment(0)
F
7

In the resource, suspend using the channel you want (the 'true' parameter to lookup() forces the channel to be created if it doesn't exist):

@Suspend( contentType = MediaType.APPLICATION_JSON, period = MAX_SUSPEND_MSEC )
@GET
public Broadcastable suspend( @Context final BroadcasterFactory factory )
{
    return new Broadcastable( factory.lookup( MY_CHANNEL, true ) );
}

In the other code, which can be pretty much anywhere, broadcast to that channel:

Broadcaster broadcaster = BroadcasterFactory.getDefault().lookup( MY_CHANNEL );
if( broadcaster != null ) {
    broadcaster.broadcast( message );
}

If you're going to be broadcasting from a resource method, you can annotate it instead (as shown in ChatResource's broadcast() method).

Frizzy answered 11/3, 2014 at 16:30 Comment(0)
F
3

Just inject Broadcaster using the @PathParam annotation:

private
@PathParam("topic")
Broadcaster topic;

You can also use the @Context annotation.

Figwort answered 10/2, 2014 at 17:4 Comment(4)
But I don't have a topic, so that won't be a path parameter. What I think you're suggesting would require me to have the end point on "/notifications/{topic}", which is the pub/sub example that I've seen elsewhere. I don't want to do that though, I want to split by the static parts. Though maybe I can add an @Context to the Broadcaster reference and that might work...Tsan
No, that's not going to work, because I'm not broadcasting from the Resource, I'm broadcasting from a method deep in the bowels of the code, which itself is behind and async queue. Thanks for your suggestion however, I appreciate it.Tsan
Ok then it means the broadcaster hasn't been created yet. So your external code may just wait until the Broadcaster is created. You can implemented a listener for doing that.Figwort
I'll investigate listeners. However, the external code (i.e. the web application) isn't really the problem here. Somewhere in my server application an event occurs. I want to broadcast that event to all of the web applications on WS or long-polling to the /notifications end-point. The /chat endpoint should not receive these events. I think I'm struggling to explain myself here :)Tsan

© 2022 - 2024 — McMap. All rights reserved.