-
-
Notifications
You must be signed in to change notification settings - Fork 761
Understanding Rooms
Rooms are a first-class concept in Atmosphere 4.0. A Room is a named group of connections with built-in presence tracking, message history, direct messaging, and authorization.
Rooms provide a higher-level abstraction over Broadcasters — you work with named groups and members instead of managing individual resources.
@Configuration public class RoomsConfig { private final AtmosphereFramework framework; public RoomsConfig(AtmosphereFramework framework) { this.framework = framework; } @Bean public RoomManager roomManager() { return RoomManager.getOrCreate(framework); } @EventListener(ApplicationReadyEvent.class) public void setupRooms() { // Register the protocol interceptor for client ↔ server room messages var interceptor = new RoomProtocolInterceptor(); interceptor.configure(framework.getAtmosphereConfig()); framework.interceptor(interceptor); RoomManager manager = roomManager(); Room lobby = manager.room("lobby"); // New joiners get the last 50 messages lobby.enableHistory(50); // Track who joins and leaves lobby.onPresence(event -> { var memberId = event.memberInfo() != null ? event.memberInfo().id() : event.member().uuid(); log.info("{} {} room '{}'", memberId, event.type(), event.room().name()); }); } }
import { Atmosphere, AtmosphereRooms } from 'atmosphere.js'; const atmosphere = new Atmosphere(); const rooms = new AtmosphereRooms(atmosphere, { url: '/atmosphere/chat', transport: 'websocket', }); const lobby = await rooms.join('lobby', { id: 'alice' }, { message: (data, member) => console.log(`${member.id}: ${data}`), join: (event) => console.log(`${event.member.id} joined`), leave: (event) => console.log(`${event.member.id} left`), joined: (name, members) => console.log(`Joined ${name}, members:`, members), }); lobby.broadcast('Hello everyone!'); lobby.sendTo('bob', 'Private message'); lobby.leave();
The entry point for creating and managing rooms. Each room is backed by a dedicated Broadcaster.
// Create from AtmosphereFramework RoomManager rooms = RoomManager.create(framework); // Or get/create a singleton (recommended) RoomManager rooms = RoomManager.getOrCreate(framework); // Get or create a room Room lobby = rooms.room("lobby"); // Check existence rooms.exists("lobby"); // true // List all rooms rooms.all(); // Collection<Room> rooms.count(); // int // Destroy a room rooms.destroy("lobby"); // removes all members rooms.destroyAll();
A named group of connections.
Room room = rooms.room("lobby"); // Members room.join(resource); // Add a connection room.join(resource, new RoomMember("alice")); // Add with identity room.leave(resource); // Remove a connection room.members(); // Set<AtmosphereResource> room.memberInfo(); // Map<uuid, RoomMember> room.memberOf(resource); // Optional<RoomMember> room.size(); // Member count room.isEmpty(); room.contains(resource); // Messaging room.broadcast("Hello!"); // To all members room.broadcast("Hello!", sender); // To all except sender room.sendTo("Hello!", targetUuid); // Direct message by UUID // Presence room.onPresence(event -> { /* JOIN or LEAVE */ }); // History room.enableHistory(50); // Cache last 50 messages // Lifecycle room.destroy(); room.isDestroyed();
Application-level identity for a room member, stable across reconnects:
// Simple var member = new RoomMember("alice"); // With metadata var member = new RoomMember("alice", Map.of( "displayName", "Alice Smith", "avatar", "https://example.com/alice.jpg" )); member.id(); // "alice" member.metadata(); // unmodifiable Map
Fired when a member joins or leaves a room:
room.onPresence(event -> { event.type(); // PresenceEvent.Type.JOIN or LEAVE event.room(); // the Room event.member(); // AtmosphereResource event.memberInfo(); // RoomMember (may be null) });
The RoomProtocolInterceptor bridges the atmosphere.js client room protocol to the server-side Room API. It intercepts JSON messages from clients and routes them to the appropriate Room operations.
// Register manually var interceptor = new RoomProtocolInterceptor(); interceptor.configure(framework.getAtmosphereConfig()); framework.interceptor(interceptor);
The interceptor handles four message types from the client:
| Message Type | Action |
|---|---|
join |
Joins the room, sends ack with member list, broadcasts presence |
leave |
Leaves the room, broadcasts leave presence |
broadcast |
Broadcasts message to all room members (except sender) |
direct |
Sends a direct message to a specific member by ID |
The RoomInterceptor provides automatic room joining based on URL path:
RoomManager rooms = RoomManager.create(framework); framework.interceptor(new RoomInterceptor(rooms)); // Requests to /room/lobby auto-join the "lobby" room // Requests to /room/general auto-join the "general" room
Custom base path:
framework.interceptor(new RoomInterceptor(rooms, "/chat/")); // Now /chat/lobby → room "lobby"
Control who can join, leave, broadcast, or send direct messages:
// 1. Implement RoomAuthorizer public class MyRoomAuthorizer implements RoomAuthorizer { @Override public boolean authorize(AtmosphereResource r, String roomName, RoomAction action) { // Check permissions based on resource, room name, and action return true; } } // 2. Annotate your handler @RoomAuth(authorizer = MyRoomAuthorizer.class) @ManagedService(path = "/chat") public class Chat { /* ... */ }
Available actions: JOIN, LEAVE, BROADCAST, SEND_TO.
Expose room information via REST (Spring Boot example):
@RestController @RequestMapping("/api/rooms") public class ChatRoomsController { private final RoomManager roomManager; public ChatRoomsController(RoomManager roomManager) { this.roomManager = roomManager; } @GetMapping public List<Map<String, Object>> listRooms() { return roomManager.all().stream() .map(room -> Map.of( "name", room.name(), "members", room.size(), "memberDetails", room.memberInfo().values() )) .toList(); } }
atmosphere.js 5.0 includes room hooks for popular frameworks:
import { useRoom } from 'atmosphere.js/react'; function ChatRoom() { const { members, messages, broadcast } = useRoom<ChatMessage>({ request: { url: '/atmosphere/chat', transport: 'websocket' }, room: 'lobby', member: { id: 'alice' }, }); return ( <div> <p>Members: {members.map(m => m.id).join(', ')}</p> {messages.map((msg, i) => <p key={i}>{msg.member.id}: {msg.data}</p>)} <button onClick={() => broadcast({ text: 'Hello!' })}>Send</button> </div> ); }
<script setup lang="ts"> import { useRoom } from 'atmosphere.js/vue'; const { members, messages, broadcast } = useRoom<ChatMessage>( { url: '/atmosphere/chat', transport: 'websocket' }, 'lobby', { id: 'alice' }, ); </script>
<script> import { createRoomStore } from 'atmosphere.js/svelte'; const { store: lobby, broadcast } = createRoomStore( { url: '/atmosphere/chat', transport: 'websocket' }, 'lobby', { id: 'alice' }, ); </script> <p>Members: {$lobby.members.map(m => m.id).join(', ')}</p>