-
-
Notifications
You must be signed in to change notification settings - Fork 761
Understanding RoomService
@RoomService is a room-aware alternative to @ManagedService. It uses the same lifecycle annotations (@Ready, @Message, @Disconnect) but automatically creates and manages a Room via the RoomManager, giving you presence tracking, message history, and direct messaging out of the box.
Use @RoomService when your application is centred around rooms (chat, collaboration, multiplayer). Use @ManagedService when you need a generic real-time endpoint without room semantics.
@RoomService(path = "/chat/{roomId}", maxHistory = 50) public class ChatRoom { @Ready public void onJoin(AtmosphereResource r) { // Client joined the room — presence event is sent automatically } @Message(encoders = {JacksonEncoder.class}, decoders = {JacksonDecoder.class}) public Message onMessage(Message message) { return message; // broadcast to all room members } @Disconnect public void onLeave(AtmosphereResourceEvent event) { // Client left — presence event is sent automatically } }
That's it — no RoomManager boilerplate, no RoomProtocolInterceptor setup. The annotation processor handles everything.
@RoomService( path = "/chat/{roomId}", // mapping path — supports path parameters maxHistory = 0 // messages to keep in room history (0 = disabled) )
| Attribute | Type | Default | Description |
|---|---|---|---|
path |
String |
"/" |
URL mapping path. Supports {param} path templates |
maxHistory |
int |
0 |
Number of messages to keep in room history. Set to > 0 to replay history to joining clients |
When the annotation processor encounters @RoomService:
- Creates a
ManagedAtmosphereHandlerfor the annotated class (same as@ManagedService) - Registers the default managed-service interceptors (UUID cache, lifecycle, heartbeat, message tracking, suspend tracker)
- Maps the handler to the specified
path - Calls
RoomManager.getOrCreate(framework)to get the singletonRoomManager - Creates the
RoomviaroomManager.room(path) - If
maxHistory > 0, callsroom.enableHistory(maxHistory)
Because @RoomService uses the same ManagedAtmosphereHandler under the hood, all lifecycle annotations work identically to @ManagedService:
| Annotation | When invoked |
|---|---|
@Ready |
Client connects and is suspended |
@Message |
Client sends a message |
@Disconnect |
Client disconnects (close or timeout) |
@Heartbeat |
Heartbeat received |
Use {param} in the path to create dynamic rooms:
@RoomService(path = "/game/{gameId}") public class GameRoom { @PathParam("gameId") private String gameId; @Message public String onMove(String move) { return move; // broadcast to all players in this game } }
Each unique path (e.g. /game/abc, /game/xyz) gets its own Room and Broadcaster.
Enable history so new members receive recent messages when they join:
@RoomService(path = "/chat/general", maxHistory = 100) public class GeneralChat { @Message(encoders = {JacksonEncoder.class}, decoders = {JacksonDecoder.class}) public Message onMessage(Message message) { return message; } }
The last 100 messages are replayed to any client that connects to this room.
Like @ManagedService, you can inject Atmosphere objects into @RoomService classes:
@RoomService(path = "/collab/{docId}") public class CollabRoom { @Inject private AtmosphereResource resource; @Inject private AtmosphereResourceEvent event; @PathParam("docId") private String docId; @Ready public void onJoin() { // resource and docId are available here } }
@RoomService |
@ManagedService |
|
|---|---|---|
| Purpose | Room-centric real-time apps | General-purpose real-time endpoints |
| Room auto-creation | ✅ Automatic via RoomManager
|
❌ Manual setup required |
| Message history | ✅ maxHistory attribute |
❌ Must configure BroadcasterCache manually |
| Presence tracking | ✅ Built-in via Room | ❌ Must implement manually |
| Lifecycle annotations |
@Ready, @Message, @Disconnect, @Heartbeat
|
Same |
| Path parameters | ✅ {param}
|
✅ {param}
|
| Encoders/Decoders | Via @Message
|
Via @Message or annotation attributes |
| Interceptors | Default managed set | Configurable via interceptors attribute |
| Broadcaster config | Default | Configurable via broadcaster attribute |
Rule of thumb: If your clients join/leave named groups, use @RoomService. If you need a raw pub/sub endpoint or fine-grained Broadcaster control, use @ManagedService.
The client code is the same as for any @ManagedService endpoint. Connect with atmosphere.js and send JSON:
const subscription = await atmosphere.atmosphere.subscribe( { url: `${location.protocol}//${location.host}/chat/general`, transport: 'websocket', fallbackTransport: 'long-polling', trackMessageLength: true, contentType: 'application/json' }, { open: (response) => console.log('Joined room'), message: (response) => { const msg = JSON.parse(response.responseBody); console.log(`${msg.author}: ${msg.message}`); } } ); subscription.push(JSON.stringify({ author: 'Alice', message: 'Hello room!' }));
For the Room Protocol (join/leave/presence/direct messaging), see Understanding Rooms — Client Integration.
- Understanding @ManagedService — the general-purpose annotation
- Understanding Rooms — Room API, presence, history, authorization
- Understanding Broadcaster — the underlying pub/sub bus