package org.kalipo.web.websocket; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.atmosphere.config.service.*; import org.atmosphere.cpr.AtmosphereResource; import org.atmosphere.cpr.AtmosphereResourceEvent; import org.atmosphere.cpr.Broadcaster; import org.atmosphere.cpr.BroadcasterFactory; import org.atmosphere.interceptor.AtmosphereResourceStateRecovery; import org.atmosphere.interceptor.HeartbeatInterceptor; import org.kalipo.service.WebSocketService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import java.util.concurrent.ConcurrentHashMap; @ManagedService( path = "/websocket/live/{thread: [a-zA-Z_0-9]+}", interceptors = { HeartbeatInterceptor.class, AtmosphereResourceStateRecovery.class, } ) public class LiveChannelService { public static final String URL = "/websocket/live/"; private final Logger log = LoggerFactory.getLogger(LiveChannelService.class); private final ConcurrentHashMap<String, Integer> usersInThread = new ConcurrentHashMap<>(); @PathParam("thread") private String threadId; // For demonstrating injection. @Inject private BroadcasterFactory broadcasterFactory; @Heartbeat public void onHeartbeat(final AtmosphereResourceEvent event) { log.trace("Heartbeat send by {}", event.getResource()); } /** * Invoked when the connection as been fully established and suspended, e.g ready for receiving messages. */ @Ready public void onReady(AtmosphereResource r) { log.debug("Browser {} connected", r.uuid()); log.debug("BroadcasterFactory used {}", broadcasterFactory.getClass().getName()); log.debug("Broadcaster injected {}", r.getBroadcaster().getID()); if (!usersInThread.containsKey(threadId)) { usersInThread.put(threadId, 0); } usersInThread.put(threadId, usersInThread.get(threadId) + 1); broadcastStats(r.getBroadcaster()); } /** * Invoked when the client disconnect or when an unexpected closing of the underlying connection happens. */ @Disconnect public void onDisconnect(AtmosphereResourceEvent event) { if (event.isCancelled()) { log.debug("Browser {} unexpectedly disconnected", event.getResource().uuid()); } else if (event.isClosedByClient()) { log.debug("Browser {} closed the connection", event.getResource().uuid()); } if (usersInThread.containsKey(threadId)) { usersInThread.put(threadId, Math.max(0, usersInThread.get(threadId) - 1)); broadcastStats(event.broadcaster()); } } private static ObjectMapper jsonMapper = new ObjectMapper(); private void broadcastStats(Broadcaster broadcaster) { try { WebSocketService.Wrapper wrapper = new WebSocketService.Wrapper(WebSocketService.Type.STATS.name(), usersInThread.get(threadId)); broadcaster.broadcast(jsonMapper.writeValueAsString(wrapper)); } catch (JsonProcessingException e) { // nothing } } // /** // * Simple annotated class that demonstrate how {@link org.atmosphere.config.managed.Encoder} and {@link org.atmosphere.config.managed.Decoder // * can be used. // * // * @param message an instance of {@link Message} // * @return // * @throws IOException // */ // @org.atmosphere.config.service.Message(encoders = {JacksonEncoder.class}, decoders = {JacksonDecoder.class}) // public Message onMessage(Message message) throws IOException { // log.info("{} just send {}", message.getAuthor(), message.getMessage()); // return message; // } }