/** * Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.web.analytics.push; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.eclipse.jetty.continuation.Continuation; import com.google.common.base.Objects; /** * Maps client IDs to Jetty continuations associated with a client connection. */ public class LongPollingConnectionManager { /** Listener for dispatching notifications to the clients, keyed by client ID. */ private final Map<String, LongPollingUpdateListener> _updateListeners = new ConcurrentHashMap<String, LongPollingUpdateListener>(); /** * Creates a new connection. * * @param userId The ID of the user who owns the connection * @param clientId The connection ID * @param timeoutTask Connection timeout task that the listener must reset every time the connection is set up * @return A listener that publishes to the client when it receives a notification */ /* package */ LongPollingUpdateListener handshake(String userId, String clientId, ConnectionTimeoutTask timeoutTask) { LongPollingUpdateListener listener = new LongPollingUpdateListener(clientId, userId, timeoutTask); _updateListeners.put(clientId, listener); return listener; } /** * Associates a continuation with a client connection so asynchronous updates can be pushed to the client. * * @param userId The ID of the user * @param clientId The client ID of the connection * @param continuation For sending an async response to the client * @return true if the connection was successful, false if the client ID doesn't correspond to * an existing connection */ /* package */ boolean longPollHttpConnect(String userId, String clientId, Continuation continuation) { // TODO check args LongPollingUpdateListener listener = _updateListeners.get(clientId); if (listener != null) { if (!Objects.equal(userId, listener.getUserId())) { throw new IllegalArgumentException("User ID " + userId + " doesn't correspond to client ID: " + clientId); } listener.connect(continuation); return true; } else { return false; } } // for testing /* package */ boolean isClientConnected(String clientId) { LongPollingUpdateListener listener = _updateListeners.get(clientId); return listener != null && listener.isConnected(); } /** * Called by the HTTP container when a long polling connection times out before any updates are sent. * This doesn't end the client's connection or remove the associated client ID. Normally the client will immediately * establish a new long-polling HTTP connection. * * @param clientId The client ID associated with the timed out connection * @param continuation The continuation associated with the timed out HTTP connection */ /* package */ void longPollHttpTimeout(String clientId, Continuation continuation) { LongPollingUpdateListener listener = _updateListeners.get(clientId); if (listener != null) { listener.timeout(continuation); } } /** * Invoked when the client disconnects. * * @param clientId ID of the client connection that disconnected */ /* package */ void disconnect(String clientId) { LongPollingUpdateListener listener = _updateListeners.remove(clientId); if (listener != null) { listener.disconnect(); } } }