-
-
Notifications
You must be signed in to change notification settings - Fork 755
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