package org.marketcetera.marketdata.core.manager.impl;
import java.lang.management.ManagementFactory;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.ThreadSafe;
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.apache.commons.lang.builder.CompareToBuilder;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.marketcetera.marketdata.core.Messages;
import org.marketcetera.core.Pair;
import org.marketcetera.core.QueueProcessor;
import org.marketcetera.core.publisher.ISubscriber;
import org.marketcetera.event.Event;
import org.marketcetera.event.HasInstrument;
import org.marketcetera.marketdata.*;
import org.marketcetera.marketdata.IFeedComponent.FeedType;
import org.marketcetera.marketdata.core.MarketDataProvider;
import org.marketcetera.marketdata.core.MarketDataProviderMBean;
import org.marketcetera.marketdata.core.ProviderStatus;
import org.marketcetera.marketdata.core.manager.*;
import org.marketcetera.marketdata.core.module.MarketDataCoreModuleFactory;
import org.marketcetera.marketdata.core.module.ReceiverModule;
import org.marketcetera.marketdata.core.module.ReceiverModuleFactory;
import org.marketcetera.marketdata.core.provider.AbstractMarketDataProvider;
import org.marketcetera.marketdata.core.request.MarketDataRequestAtom;
import org.marketcetera.marketdata.core.request.MarketDataRequestToken;
import org.marketcetera.module.*;
import org.marketcetera.trade.Instrument;
import org.marketcetera.util.log.SLF4JLoggerProxy;
import org.marketcetera.util.misc.ClassVersion;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
/* $License$ */
/**
* Routes market data requests to available providers.
*
* @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a>
* @version $Id: MarketDataManagerImpl.java 16901 2014-05-11 16:14:11Z colin $
* @since 2.4.0
*/
@ThreadSafe
@ClassVersion("$Id: MarketDataManagerImpl.java 16901 2014-05-11 16:14:11Z colin $")
public class MarketDataManagerImpl
implements MarketDataManager,MarketDataProviderRegistry
{
/**
* Gets the singleton instance of this class.
*
* @return a <code>MarketDataManagerImpl</code> value or <code>null</code> if this class has not yet been instantiated
*/
public static MarketDataManagerImpl getInstance()
{
return instance;
}
/**
* Create a new MarketDataManagerImpl instance.
*/
public MarketDataManagerImpl()
{
instance = this;
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.manager.MarketDataProviderRegistry#setStatus(org.marketcetera.marketdata.provider.MarketDataProvider, org.marketcetera.marketdata.FeedStatus)
*/
@Override
public void setStatus(MarketDataProvider inProvider,
ProviderStatus inStatus)
{
Messages.PROVIDER_REPORTS_STATUS.info(this,
inProvider.getProviderName(),
inStatus);
Lock statusUpdateLock = requestLockObject.writeLock();
try {
ObjectName providerObjectName = getObjectNameFor(inProvider);
statusUpdateLock.lockInterruptibly();
if(inStatus == ProviderStatus.AVAILABLE) {
// TODO check for duplicate provider name and warn
addProvider(inProvider);
if(!mbeanServer.isRegistered(providerObjectName)) {
mbeanServer.registerMBean((MarketDataProviderMBean)inProvider,
providerObjectName);
}
} else {
removeProvider(inProvider);
}
} catch (InterruptedException e) {
throw new MarketDataRequestFailed(e);
} catch (JMException e) {
Messages.JMX_REGISTRATION_ERROR.warn(this,
e,
inProvider.getProviderName());
} finally {
statusUpdateLock.unlock();
}
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.manager.MarketDataManager#requestMarketData(org.marketcetera.marketdata.request.MarketDataRequest, org.marketcetera.api.systemmodel.Subscriber)
*/
@Override
public long requestMarketData(MarketDataRequest inRequest,
ISubscriber inSubscriber)
{
SLF4JLoggerProxy.debug(this,
"Received: {}",
inRequest);
// route the request to available providers or to a particular provider
Collection<MarketDataProvider> successfulProviders = new ArrayList<MarketDataProvider>();
MarketDataRequestToken token = new Token(inSubscriber,
inRequest);
if(inRequest.getProvider() != null) {
// a specific provider was requested - use that provider only
MarketDataProvider provider = getMarketDataProviderForName(inRequest.getProvider());
if(provider == null) {
throw new MarketDataProviderNotAvailable();
}
SLF4JLoggerProxy.debug(this,
"Submitting {} to {}",
token,
provider);
try {
provider.requestMarketData(token);
} catch (RuntimeException e) {
throw new MarketDataException(e);
}
successfulProviders.add(provider);
} else {
boolean liveRequestSubmitted = false;
for(MarketDataProvider provider : getActiveMarketDataProviders()) {
if(liveRequestSubmitted && provider.getFeedType() != FeedType.LIVE) {
SLF4JLoggerProxy.debug(this,
"Request has been submitted to all live feeds, no more requests will be issued");
break;
}
try {
SLF4JLoggerProxy.debug(this,
"Submitting {} to {} [{}]",
token,
provider,
provider.getFeedType());
provider.requestMarketData(token);
successfulProviders.add(provider);
if(provider.getFeedType() == FeedType.LIVE) {
liveRequestSubmitted = true;
}
} catch (RuntimeException e) {
Messages.UNABLE_TO_REQUEST_MARKETDATA.warn(this,
e,
inRequest,
provider.getProviderName());
// continue to try from the next provider
}
}
if(successfulProviders.isEmpty()) {
throw new NoMarketDataProvidersAvailable();
}
}
Lock requestLock = requestLockObject.writeLock();
try {
requestLock.lockInterruptibly();
for(MarketDataProvider provider : successfulProviders) {
providersByToken.put(token,
provider);
}
tokensByTokenId.put(token.getId(),
token);
} catch (InterruptedException e) {
Messages.MARKETDATA_REQUEST_INTERRUPTED.warn(this,
inRequest);
throw new MarketDataRequestTimedOut(e);
} finally {
requestLock.unlock();
}
return token.getId();
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.manager.MarketDataManager#cancelMarketDataRequest(org.marketcetera.api.systemmodel.Subscriber)
*/
@Override
public void cancelMarketDataRequest(long inRequestId)
{
SLF4JLoggerProxy.debug(this,
"Canceling request {}",
inRequestId);
Lock cancelLock = requestLockObject.writeLock();
try {
cancelLock.lockInterruptibly();
MarketDataRequestToken token = tokensByTokenId.remove(inRequestId);
if(token != null) {
for(MarketDataProvider provider : providersByToken.removeAll(token)) {
SLF4JLoggerProxy.debug(this,
"Canceling request {} with {}",
inRequestId,
provider);
provider.cancelMarketDataRequest(token);
}
}
} catch (InterruptedException ignored) {
} finally {
cancelLock.unlock();
}
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.core.manager.MarketDataManager#requestMarketDataSnapshot(org.marketcetera.trade.Instrument, org.marketcetera.marketdata.Content, java.lang.String)
*/
@Override
public Event requestMarketDataSnapshot(Instrument inInstrument,
Content inContent,
String inProvider)
{
if(inProvider == null) {
SortedSet<Pair<FeedType,Event>> snapshotCandidates = Sets.newTreeSet(SnapshotComparator.INSTANCE);
for(MarketDataProvider provider : getActiveMarketDataProviders()) {
Event snapshotCandidate = provider.getSnapshot(inInstrument,
inContent);
if(snapshotCandidate != null) {
snapshotCandidates.add(Pair.create(provider.getFeedType(),
snapshotCandidate));
}
}
if(snapshotCandidates.isEmpty()) {
return null;
}
return snapshotCandidates.first().getSecondMember();
} else {
MarketDataProvider provider = getMarketDataProviderForName(inProvider);
if(provider == null) {
throw new MarketDataProviderNotAvailable();
}
return provider.getSnapshot(inInstrument,
inContent);
}
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.core.manager.MarketDataManager#getAvailableCapability()
*/
@Override
public Set<Capability> getAvailableCapability()
{
Set<Capability> capabilities = Sets.newHashSet();
boolean includesLiveData = false;
for(MarketDataProvider provider : getActiveMarketDataProviders()) {
if(provider.getFeedType() == FeedType.LIVE) {
includesLiveData = true;
}
if(includesLiveData && provider.getFeedType() != FeedType.LIVE) {
// we've looked at at least one live provider and providers are non-live from here on, so quite
break;
}
capabilities.addAll(provider.getCapabilities());
}
return capabilities;
}
/**
* Constructs a unique <code>ObjectName</code> for the given provider.
*
* @param inProvider a <code>MarketDataProvider</code> value
* @return an <code>ObjectName</code> value
* @throws MalformedObjectNameException if the provider name is invalid
*/
private ObjectName getObjectNameFor(MarketDataProvider inProvider)
throws MalformedObjectNameException
{
return new ObjectName("org.marketcetera.marketdata.provider:name=" + inProvider.getProviderName());
}
/**
* Gets a market data provider for the given name.
*
* @param inProviderName a <code>String</code> value
* @return a <code>MarketDataProvider</code> value
*/
private MarketDataProvider getMarketDataProviderForName(String inProviderName)
{
populateProviderList();
return activeProvidersByName.get(inProviderName);
}
/**
* Gets the currently active market data providers.
*
* @return a <code>Collection<MarketDataProvider></code> value
*/
private Collection<MarketDataProvider> getActiveMarketDataProviders()
{
populateProviderList();
return providersByPriority;
}
/**
* Removes the provider from the collection of active providers.
*
* @param inProvider a <code>MarketDataProvider</code> value
*/
private void removeProvider(MarketDataProvider inProvider)
{
activeProvidersByName.remove(inProvider.getProviderName());
providersByPriority.remove(inProvider);
}
/**
* Adds the given provider to the collection of active providers.
*
* @param inProvider a <code>MarketDataProvider</code> value
*/
private void addProvider(MarketDataProvider inProvider)
{
activeProvidersByName.put(inProvider.getProviderName(),
inProvider);
providersByPriority.add(inProvider);
}
/**
* Populates the provider list with provider proxys that are implemented as old school modules.
*/
private void populateProviderList()
{
synchronized(activeProvidersByName) {
if(ModuleManager.getInstance() != null) {
List<ModuleURN> providerUrns = ModuleManager.getInstance().getProviders();
for(ModuleURN providerUrn : providerUrns) {
String providerName = providerUrn.providerName();
if(providerUrn.providerType().equals(MDATA) && !providerName.equals(MarketDataCoreModuleFactory.IDENTIFIER) && !activeProvidersByName.containsKey(providerName)) {
List<ModuleURN> instanceUrns = ModuleManager.getInstance().getModuleInstances(providerUrn);
if(!instanceUrns.isEmpty()) {
ModuleURN instanceUrn = instanceUrns.get(0);
ModuleInfo info = ModuleManager.getInstance().getModuleInfo(instanceUrn);
if(info.getState() == ModuleState.STARTED) {
ModuleProvider provider = new ModuleProvider(providerName,
AbstractMarketDataModule.getFeedForProviderName(providerName));
SLF4JLoggerProxy.debug(this,
"Creating market data provider proxy for {}",
providerName);
addProvider(provider);
}
}
}
}
}
}
}
/**
* Identifies a particular market data request.
*
* @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a>
* @version $Id: MarketDataManagerImpl.java 16901 2014-05-11 16:14:11Z colin $
* @since 2.4.0
*/
@Immutable
@ClassVersion("$Id: MarketDataManagerImpl.java 16901 2014-05-11 16:14:11Z colin $")
private class Token
implements MarketDataRequestToken
{
/* (non-Javadoc)
* @see org.marketcetera.marketdata.request.MarketDataRequestToken#getId()
*/
@Override
public long getId()
{
return id;
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.request.MarketDataRequestToken#getSubscriber()
*/
@Override
public ISubscriber getSubscriber()
{
return subscriber;
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.request.MarketDataRequestToken#getRequest()
*/
@Override
public MarketDataRequest getRequest()
{
return request;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return new StringBuilder().append("Token [").append(id).append("] ").append(request).toString();
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode()
{
return new HashCodeBuilder().append(id).toHashCode();
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj)
{
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Token)) {
return false;
}
Token other = (Token) obj;
return new EqualsBuilder().append(id,other.getId()).isEquals();
}
/**
* Create a new Token instance.
*
* @param inSubscriber an <code>ISubscriber</code> value
* @param inRequest a <code>MarketDataRequest</code> value
*/
private Token(ISubscriber inSubscriber,
MarketDataRequest inRequest)
{
subscriber = inSubscriber;
request = inRequest;
}
/**
* unique identifier for this token
*/
private final long id = requestCounter.incrementAndGet();
/**
* subscriber to which updates should be sent
*/
private final ISubscriber subscriber;
/**
* request object indicating what data is desired
*/
private final MarketDataRequest request;
private static final long serialVersionUID = 1L;
}
/**
* Creates an interface between the newer {@link MarketDataProvider} and the older, module-based {@link MarketDataFeed}.
*
* @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a>
* @version $Id: MarketDataManagerImpl.java 16901 2014-05-11 16:14:11Z colin $
* @since 2.4.0
*/
@ClassVersion("$Id: MarketDataManagerImpl.java 16901 2014-05-11 16:14:11Z colin $")
private class ModuleProvider
extends AbstractMarketDataProvider
{
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return description;
}
/**
* Create a new ModuleProvider instance.
*
* @param inProviderName a <code>String</code> value
* @param inFeed an <code>AbstractMarketDataModule<?,?></code> value
*/
private ModuleProvider(String inProviderName,
AbstractMarketDataModule<?,?> inFeed)
{
providerName = inProviderName;
description = providerName + " proxy";
feed = inFeed;
moduleManager = ModuleManager.getInstance();
processor = new Processor();
// TODO register a feed status listener to detect starts and stops and update status
start();
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.core.MarketDataProvider#getProviderName()
*/
@Override
public String getProviderName()
{
return providerName;
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.core.MarketDataProvider#getCapabilities()
*/
@Override
public Set<Capability> getCapabilities()
{
return feed.getCapabilities();
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.core.MarketDataProvider#getFeedType()
*/
@Override
public FeedType getFeedType()
{
return feed.getFeedType();
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.core.provider.AbstractMarketDataProvider#doStart()
*/
@Override
protected void doStart()
{
processor.start();
String receiverInstanceName = "MDMPROXY_" + providerName;
moduleManager.createModule(ReceiverModuleFactory.PROVIDER_URN,
receiverInstanceName);
receiver = ReceiverModule.getModuleForInstanceName(receiverInstanceName);
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.core.provider.AbstractMarketDataProvider#doStop()
*/
@Override
protected void doStop()
{
processor.stop();
if(receiver != null) {
try {
moduleManager.stop(receiver.getURN());
moduleManager.deleteModule(receiver.getURN());
} catch (RuntimeException ignored) {
} finally {
removeProvider(this);
}
receiver = null;
}
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.core.provider.AbstractMarketDataProvider#doCancel(org.marketcetera.marketdata.core.request.MarketDataRequestAtom)
*/
@Override
protected void doCancel(MarketDataRequestAtom inAtom)
{
Request request = requestsByAtom.remove(inAtom);
if(request != null) {
requestsByFlow.remove(request.flow);
request.cancel();
}
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.core.provider.AbstractMarketDataProvider#doMarketDataRequest(org.marketcetera.marketdata.MarketDataRequest, org.marketcetera.marketdata.core.request.MarketDataRequestAtom)
*/
@Override
protected void doMarketDataRequest(MarketDataRequest inCompleteRequest,
MarketDataRequestAtom inRequestAtom)
throws InterruptedException
{
Request request = new Request(inRequestAtom,
inCompleteRequest);
requestsByAtom.put(inRequestAtom,
request);
requestsByFlow.put(request.flow,
request);
SLF4JLoggerProxy.debug(this,
"{} created {} for {}",
this,
request,
inRequestAtom);
}
/**
* Represents a market data request submitted to a proxy market data provider.
*
* @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a>
* @version $Id: MarketDataManagerImpl.java 16901 2014-05-11 16:14:11Z colin $
* @since 2.4.0
*/
@ClassVersion("$Id: MarketDataManagerImpl.java 16901 2014-05-11 16:14:11Z colin $")
private class Request
implements ISubscriber
{
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return new StringBuilder().append("proxy request for ").append(atom).append(" via flow ").append(flow).toString();
}
/* (non-Javadoc)
* @see org.marketcetera.core.publisher.ISubscriber#isInteresting(java.lang.Object)
*/
@Override
public boolean isInteresting(Object inData)
{
if(inData instanceof Pair<?,?>) {
Pair<?,?> data = (Pair<?,?>)inData;
if(data.getFirstMember() instanceof DataFlowID) {
DataFlowID dataFlowID = (DataFlowID)data.getFirstMember();
if(dataFlowID.equals(flow)) {
return true;
}
}
}
return false;
}
/* (non-Javadoc)
* @see org.marketcetera.core.publisher.ISubscriber#publishTo(java.lang.Object)
*/
@Override
public void publishTo(Object inData)
{
if(inData instanceof Pair<?,?>) {
Pair<?,?> pairData = (Pair<?,?>)inData;
Object secondMember = pairData.getSecondMember();
if(secondMember instanceof Event) {
Pair<MarketDataRequestAtom,Event> toProcess = Pair.create(atom,(Event)secondMember);
processor.add(toProcess);
return;
}
}
throw new UnsupportedOperationException();
}
/**
* Cancels this request.
*/
private void cancel()
{
receiver.unsubscribe(this);
try {
moduleManager.getDataFlowInfo(flow);
moduleManager.cancel(flow);
} catch (DataFlowNotFoundException ignored) {}
}
/**
* Create a new Request instance.
*
* @param inAtom a <code>MarketDataRequestAtom</code> value
* @param inCompleteRequest a <code>MarketDataRequest</code> value
*/
private Request(MarketDataRequestAtom inAtom,
MarketDataRequest inCompleteRequest)
{
atom = inAtom;
receiver.subscribe(this);
flow = moduleManager.createDataFlow(new DataRequest[] { new DataRequest(feed.getURN(),generateRequestFromAtom(inAtom,inCompleteRequest)),new DataRequest(receiver.getURN()) });
}
/**
* Generates a <code>MarktDataRequest</code> from the given request atom.
*
* @param inAtom a <code>MarketDataRequestAtom</code> value
* @param inCompleteRequest a <code>MarketDataRequest</code> value
* @return a <code>MarketDataRequest</code> value
*/
private MarketDataRequest generateRequestFromAtom(MarketDataRequestAtom inAtom,
MarketDataRequest inCompleteRequest)
{
return MarketDataRequestBuilder.newRequest().withAssetClass(inCompleteRequest.getAssetClass()).withSymbols(inAtom.getSymbol()).withContent(inAtom.getContent()).withExchange(inAtom.getExchange()).create();
}
/**
* data flow for this request
*/
private final DataFlowID flow;
/**
* data requested
*/
private final MarketDataRequestAtom atom;
}
/**
* Processes returned data for a proxied market data provider.
*
* @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a>
* @version $Id: MarketDataManagerImpl.java 16901 2014-05-11 16:14:11Z colin $
* @since 2.4.0
*/
@ClassVersion("$Id: MarketDataManagerImpl.java 16901 2014-05-11 16:14:11Z colin $")
private class Processor
extends QueueProcessor<Pair<MarketDataRequestAtom,Event>>
{
/* (non-Javadoc)
* @see org.marketcetera.core.QueueProcessor#processData(java.lang.Object)
*/
@Override
protected void processData(Pair<MarketDataRequestAtom,Event> inData)
throws Exception
{
MarketDataRequestAtom atom = inData.getFirstMember();
Event event = inData.getSecondMember();
Instrument instrument = null;
if(event instanceof HasInstrument) {
HasInstrument hi = (HasInstrument)event;
instrument = hi.getInstrument();
if(mappedSymbols.add(atom.getSymbol())) {
addSymbolMapping(atom.getSymbol(),
hi.getInstrument());
}
publishEvents(atom.getContent(),
instrument,
event);
}
}
/* (non-Javadoc)
* @see org.marketcetera.core.QueueProcessor#shutdownOnException(java.lang.Exception)
*/
@Override
protected boolean shutdownOnException(Exception inException)
{
return !(inException instanceof InterruptedException);
}
/**
* Create a new Processor instance.
*/
private Processor()
{
super(providerName+"ProxyProcessor");
}
/**
* Adds data to the processing queue to be processed.
*
* @param inData a <code>Pair<MarketDataRequestAtom,Event></code> value
*/
private void add(Pair<MarketDataRequestAtom,Event> inData)
{
getQueue().add(inData);
}
/**
* symbols that have already been mapped
*/
private final Set<String> mappedSymbols = Sets.newHashSet();
}
/**
* processing queue for market data
*/
private final Processor processor;
/**
* market data requests by the atom being requested
*/
private final Map<MarketDataRequestAtom,Request> requestsByAtom = Maps.newHashMap();
/**
* market data requests by data flow associated with a request
*/
private final Map<DataFlowID,Request> requestsByFlow = Maps.newHashMap();
/**
* receiver module for this proxied provider
*/
private ReceiverModule receiver;
/**
* module manager object used to create data flows
*/
private final ModuleManager moduleManager;
/**
* provider name
*/
private final String providerName;
/**
* provider description
*/
private final String description;
/**
* underlying feed module to which requests are sent
*/
private final AbstractMarketDataModule<?,?> feed;
}
/**
* Compares two snapshots to determine the precedence.
*
* @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a>
* @version $Id: MarketDataManagerImpl.java 16901 2014-05-11 16:14:11Z colin $
* @since 2.4.0
*/
@ClassVersion("$Id: MarketDataManagerImpl.java 16901 2014-05-11 16:14:11Z colin $")
private static class SnapshotComparator
implements Comparator<Pair<FeedType,Event>>
{
/* (non-Javadoc)
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
@Override
public int compare(Pair<FeedType,Event> inLeft,
Pair<FeedType,Event> inRight)
{
if(inLeft.getFirstMember() == FeedType.LIVE) {
if(inRight.getFirstMember() != FeedType.LIVE) {
return -1;
}
} else {
if(inRight.getFirstMember() == FeedType.LIVE) {
return 1;
}
}
return new CompareToBuilder().append(inLeft.getSecondMember().getTimeMillis(),inRight.getSecondMember().getTimeMillis()).toComparison();
}
/**
* static, threadsafe instance
*/
private static final SnapshotComparator INSTANCE = new SnapshotComparator();
}
/**
* Compares two providers to determine which is the preferred.
*
* @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a>
* @version $Id: MarketDataManagerImpl.java 16901 2014-05-11 16:14:11Z colin $
* @since 2.4.0
*/
@ClassVersion("$Id: MarketDataManagerImpl.java 16901 2014-05-11 16:14:11Z colin $")
private static class ProviderComparator
implements Comparator<MarketDataProvider>
{
/* (non-Javadoc)
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
@Override
public int compare(MarketDataProvider inLeft,
MarketDataProvider inRight)
{
if(inLeft.getFeedType() == FeedType.LIVE) {
if(inRight.getFeedType() != FeedType.LIVE) {
return -1;
}
}
if(inRight.getFeedType() == FeedType.LIVE) {
return 1;
}
return new CompareToBuilder().append(inLeft.getProviderName(),inRight.getProviderName()).toComparison();
}
/**
* static, threadsafe instance
*/
private static final ProviderComparator INSTANCE = new ProviderComparator();
}
/**
* stores active providers by their desirability
*/
private SortedSet<MarketDataProvider> providersByPriority = Sets.newTreeSet(ProviderComparator.INSTANCE);
/**
* static instance used to provide access to non-Spring objects
*/
private static MarketDataManagerImpl instance;
/**
* tracks all active providers by provider name
*/
@GuardedBy("requestLockObject")
private final Map<String,MarketDataProvider> activeProvidersByName = new HashMap<String,MarketDataProvider>();
/**
* used to control access to critical data
*/
private final ReadWriteLock requestLockObject = new ReentrantReadWriteLock();
/**
* tracks market data tokens by token id
*/
private final Map<Long,MarketDataRequestToken> tokensByTokenId = new HashMap<Long,MarketDataRequestToken>();
/**
* associates each request token to the provider or providers to which it was directed
*/
private final Multimap<MarketDataRequestToken,MarketDataProvider> providersByToken = HashMultimap.create();
/**
* used to generate unique ids
*/
private final AtomicLong requestCounter = new AtomicLong(0);
/**
* used to set up MX interfaces
*/
private final MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
/**
* provider name for market data provider modules
*/
private static final String MDATA = "mdata";
}