Skip to content

Understanding Rooms

jfarcand edited this page Feb 18, 2026 · 3 revisions

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.

Tip: The easiest way to use Rooms is the @RoomService annotation, which auto-creates the Room with optional history — no boilerplate required. The examples below show the programmatic API for when you need more control.

Quick Example

Server Side (Spring Boot)

@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());
        });
    }
}

Client Side (atmosphere.js 5.0)

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();

Core API

RoomManager

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();

Room

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();

RoomMember

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

PresenceEvent

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)
});

Room Protocol Interceptor

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

URL-Based Room Joining

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"

Authorization

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.

REST API for Rooms

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();
    }
}

Client Framework Hooks

atmosphere.js 5.0 includes room hooks for popular frameworks:

React

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>
    );
}

Vue

<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>

Svelte

<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>

Virtual Room Members

A VirtualRoomMember is a non-connection-based participant — an AI agent, bot, or server-side service that receives room messages and can respond, without a WebSocket or HTTP connection.

Room lobby = rooms.room("lobby");

// Add an AI assistant to the room
var settings = AiConfig.get();
lobby.joinVirtual(new LlmRoomMember("assistant", settings.client(), settings.model()));

// Any message broadcast in the room is received by the AI,
// which generates a response and broadcasts it back
lobby.broadcast("What is the weather today?");

Virtual members:

  • Receive messages via the onMessage(Room, senderId, message) callback
  • Can call room.broadcast() to reply to all members
  • Fire presence events (PresenceEvent.isVirtual() returns true)
  • Report metadata visible in presence events (e.g., {"type": "llm", "model": "gemini-2.5-flash"})

Use room.leaveVirtual(member) to remove a virtual member. See AI / LLM Streaming — Virtual Room Members for the full guide.

Clone this wiki locally