Package com.opengamma.livedata.cogda.server

Source Code of com.opengamma.livedata.cogda.server.CogdaLiveDataServer

/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.livedata.cogda.server;

import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import org.fudgemsg.FudgeContext;
import org.fudgemsg.FudgeMsg;
import org.fudgemsg.FudgeMsgEnvelope;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.Lifecycle;

import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.opengamma.core.user.AuthenticationUtils;
import com.opengamma.core.user.OGUser;
import com.opengamma.core.user.UserSource;
import com.opengamma.core.user.impl.SimpleOGUser;
import com.opengamma.id.ExternalId;
import com.opengamma.id.VersionCorrection;
import com.opengamma.livedata.LiveDataSpecification;
import com.opengamma.livedata.LiveDataValueUpdate;
import com.opengamma.livedata.UserPrincipal;
import com.opengamma.livedata.server.LastKnownValueStore;
import com.opengamma.livedata.server.LastKnownValueStoreProvider;
import com.opengamma.livedata.server.LiveDataServer;
import com.opengamma.transport.FudgeConnection;
import com.opengamma.transport.FudgeConnectionReceiver;
import com.opengamma.transport.socket.ServerSocketFudgeConnectionReceiver;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.fudgemsg.OpenGammaFudgeContext;
import com.opengamma.util.metric.MetricProducer;

/**
* The base server process for any Cogda Live Data Server.
* <p/>
* By default, it will operate in a completely unauthenticated mode. User names and passwords
* in connection requests will be ignored, and all fields will be accessible by any connection.
* However, in combination with an injected {@link UserSource} (see {@link #setUserSource(UserSource)}),
* the server will authenticate users and authorize access.
* If the server has a {@link UserSource} provided, but the {@code checkPassword} parameter
* is set to false (see {@link #setCheckPassword(boolean)}) then only authorization will be provided and it is assumed
* that authentication is handled elsewhere in the overall application and so user credentials
* can be assumed to be valid without a password being provided.
* <p/>
* Because the {@link UserSource} will be hit for every authorization question, it is <strong>critical</strong>
* that the source caches requests in some form.
*/
public class CogdaLiveDataServer implements LiveDataServer, FudgeConnectionReceiver, Lifecycle, MetricProducer {

  /** Logger. */
  private static final Logger s_logger = LoggerFactory.getLogger(CogdaLiveDataServer.class);
  /**
   * The default port on which the server will listen for inbound connections.
   */
  public static final int DEFAULT_LISTEN_PORT = 11876;
  private int _portNumber = DEFAULT_LISTEN_PORT;
 
  private final ServerSocketFudgeConnectionReceiver _connectionReceiver;
  private final LastKnownValueStoreProvider _lastKnownValueStoreProvider;
  private final ConcurrentMap<LiveDataSpecification, LastKnownValueStore> _lastKnownValueStores =
      new ConcurrentHashMap<LiveDataSpecification, LastKnownValueStore>();
 
  private final Set<CogdaClientConnection> _clients = new CopyOnWriteArraySet<CogdaClientConnection>();
  // TODO kirk 2012-07-23 -- This is absolutely the wrong executor here.
  private final Executor _valueUpdateSendingExecutor = Executors.newFixedThreadPool(5);
  private UserSource _userSource;
  private boolean _checkPassword = true;
 
  // Metrics:
  private Meter _tickMeter = new Meter();
 
  public CogdaLiveDataServer(LastKnownValueStoreProvider lkvStoreProvider) {
    this(lkvStoreProvider, OpenGammaFudgeContext.getInstance());
  }
 
  public CogdaLiveDataServer(LastKnownValueStoreProvider lkvStoreProvider, FudgeContext fudgeContext) {
    ArgumentChecker.notNull(lkvStoreProvider, "lkvStoreProvider");
    ArgumentChecker.notNull(fudgeContext, "fudgeContext");
    _lastKnownValueStoreProvider = lkvStoreProvider;
    _connectionReceiver = new ServerSocketFudgeConnectionReceiver(fudgeContext, this);
    _connectionReceiver.setLazyFudgeMsgReads(false);
  }

  @Override
  public synchronized void registerMetrics(MetricRegistry summaryRegistry, MetricRegistry detailedRegistry, String namePrefix) {
    _tickMeter = summaryRegistry.meter(namePrefix + ".ticks");
  }

  /**
   * Gets the portNumber.
   * @return the portNumber
   */
  public int getPortNumber() {
    return _portNumber;
  }

  /**
   * Sets the portNumber. Defaults to {@link #DEFAULT_LISTEN_PORT}.
   * This <b>must</b> be set <b>before</b> {@link #start()} is called.
   * @param portNumber  the portNumber
   */
  public void setPortNumber(int portNumber) {
    _portNumber = portNumber;
  }

  /**
   * Gets the lastKnownValueStoreProvider.
   * @return the lastKnownValueStoreProvider
   */
  public LastKnownValueStoreProvider getLastKnownValueStoreProvider() {
    return _lastKnownValueStoreProvider;
  }

  /**
   * Gets the userSource.
   * @return the userSource
   */
  public UserSource getUserSource() {
    return _userSource;
  }

  /**
   * Sets the userSource.
   * @param userSource  the userSource
   */
  public void setUserSource(UserSource userSource) {
    _userSource = userSource;
  }

  /**
   * Whether passwords will be checked.
   * @return true if passwords will be checked.
   */
  public boolean isCheckPassword() {
    return _checkPassword;
  }

  /**
   * Sets whether passwords will be checked.
   * Setting to false means that only authorization will be performed
   * rather than authentication.
   * @param checkPassword  false to turn off password checking on connections.
   */
  public void setCheckPassword(boolean checkPassword) {
    _checkPassword = checkPassword;
  }

  @Override
  public void connectionReceived(FudgeContext fudgeContext, FudgeMsgEnvelope message, FudgeConnection connection) {
    CogdaClientConnection clientConnection = new CogdaClientConnection(fudgeContext, this, connection);
    // We're blocked on connection acceptance. Therefore it's entirely fine
    // to do the handshake here as we won't get any more messages until
    // it's done.
    clientConnection.handshakeMessage(fudgeContext, message);
    _clients.add(clientConnection);
  }

  @Override
 
  public void start() {
    _connectionReceiver.setPortNumber(getPortNumber());
    _connectionReceiver.start();
  }

  @Override
  public void stop() {
    _connectionReceiver.stop();
  }

  @Override
  public boolean isRunning() {
    return _connectionReceiver.isRunning();
  }
 
  public void liveDataReceived(LiveDataValueUpdate valueUpdate) {
    _tickMeter.mark();
   
    // REVIEW kirk 2013-03-27 -- Does this loop need to be done in an executor
    // task or something? If nothing else, connection.liveDataReceived() can
    // block.
   
    // This could probably be much much faster, but we're designed initially for low-frequency
    // updates. Someone smarter should optimize the data structures here.
    for (CogdaClientConnection connection : _clients) {
      boolean needsPump = connection.liveDataReceived(valueUpdate);
      if (needsPump) {
        final CogdaClientConnection finalConnection = connection;
        _valueUpdateSendingExecutor.execute(new Runnable() {
          @Override
          public void run() {
            finalConnection.sendAllUpdates();
          }
        });
      }
    }
  }
 
  protected OGUser getOGUser(String userId) {
    if (getUserSource() == null) {
      // Nothing will work without an OG User. So we return a mock one.
      SimpleOGUser simpleUser = new SimpleOGUser(userId);
      simpleUser.getEntitlements().add("*");
      return simpleUser;
    }
    try {
      return getUserSource().getUser(userId, VersionCorrection.LATEST);
     
    } catch (RuntimeException ex) {
      s_logger.warn("Authentication could not find user {}", userId);
      return null;
    }
  }
 
  // Callbacks from the client.
  public UserPrincipal authenticate(String userId, String password) {
    if (getUserSource() == null) {
      // No user source. Allow all connections.
      return UserPrincipal.getLocalUser(userId);
    }
   
    OGUser ogUser = getOGUser(userId);
    if (ogUser == null) {
      s_logger.info("Not allowing login for {} because no user in UserSource", userId);
      return null;
    }
   
    if (isCheckPassword() && !AuthenticationUtils.passwordsMatch(ogUser, password)) {
      s_logger.info("Not allowing login for {} because passwords don't match", userId);
      return null;
    }
    return UserPrincipal.getLocalUser(userId);
  }
 
  public List<String> getAvailableServers() {
    return Collections.emptyList();
  }
 
  public FudgeMsg getCapabilities() {
    return OpenGammaFudgeContext.getInstance().newMessage();
  }
 
  public boolean isValidLiveData(ExternalId subscriptionId, String normalizationScheme) {
    return getLastKnownValueStoreProvider().isAvailable(subscriptionId, normalizationScheme);
  }
 
  public LastKnownValueStore getLastKnownValueStore(ExternalId subscriptionId, String normalizationScheme) {
    LiveDataSpecification ldspec = new LiveDataSpecification(normalizationScheme, subscriptionId);
    // TODO kirk 2012-07-23 -- Check to see if valid.
   
    LastKnownValueStore store = _lastKnownValueStores.get(ldspec);
    if (store == null) {
      LastKnownValueStore fresh = getLastKnownValueStoreProvider().newInstance(subscriptionId, normalizationScheme);
      LastKnownValueStore fromMap = _lastKnownValueStores.putIfAbsent(ldspec, fresh);
      if (fromMap == null) {
        store = fresh;
      } else {
        store = fromMap;
      }
    }
    return store;
  }
 
  public void removeClient(CogdaClientConnection connection) {
    _clients.remove(connection);
  }
 

  public int getNumClients() {
    return _clients.size();
  }
 
  public Set<String> getActiveUsers() {
    Set<String> result = new TreeSet<String>();
    synchronized (_clients) {
      for (CogdaClientConnection connection : _clients) {
        result.add(connection.getUserPrincipal().toString());
      }
    }
    return result;
  }
}
TOP

Related Classes of com.opengamma.livedata.cogda.server.CogdaLiveDataServer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.