/** * 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.Set; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import com.codahale.metrics.Meter; import com.codahale.metrics.MetricRegistry; import com.opengamma.livedata.firehose.InputStreamFactory; import com.opengamma.transport.FudgeMessageSender; import com.opengamma.util.metric.MetricProducer; /** * A class that is able to decode the raw stream of bytes for a fire hose provider * and produce them in a binary form for another purpose. * The canonical example is one which will read a socket-based stream, decode that * stream into discrete messages, and then distribute onto an MOM system for later * processing into a full {@code FireHoseLiveData} object. */ public abstract class CogdaRecordChunker implements MetricProducer { /** * An estimate of the number of symbols that will be seen. * Used to establish the size of the internal statics maintenance structures. */ private static final int ESTIMATE_SYMBOL_COUNT = 10000; private final ConcurrentMap<String, Meter> _symbolStatistics = new ConcurrentHashMap<String, Meter>(ESTIMATE_SYMBOL_COUNT); private MetricRegistry _detailedRegistry; private String _metricNamePrefix; private Meter _tickMeter; private Lock _metricsModificationLock = new ReentrantLock(); /** * The destination for decoded messsages. */ private FudgeMessageSender _fudgeSender; /** * The source for new connections for data to parse. */ private InputStreamFactory _inputStreamFactory; @Override public synchronized void registerMetrics(MetricRegistry summaryRegistry, MetricRegistry detailedRegistry, String namePrefix) { _detailedRegistry = detailedRegistry; _metricNamePrefix = namePrefix; _tickMeter = summaryRegistry.meter(namePrefix + ".total"); } /** * Gets the fudgeSender. * @return the fudgeSender */ public FudgeMessageSender getFudgeSender() { return _fudgeSender; } /** * Sets the fudgeSender. * @param fudgeSender the fudgeSender */ public void setFudgeSender(FudgeMessageSender fudgeSender) { _fudgeSender = fudgeSender; } /** * Gets the inputStreamFactory. * @return the inputStreamFactory */ public InputStreamFactory getInputStreamFactory() { return _inputStreamFactory; } /** * Sets the inputStreamFactory. * @param inputStreamFactory the inputStreamFactory */ public void setInputStreamFactory(InputStreamFactory inputStreamFactory) { _inputStreamFactory = inputStreamFactory; } /** * Should be called by the sub-class whenever a tick is received. * Allows the parent class to update statistics visible by the MBean. * @param symbol The symbol a tick is received on. */ protected void symbolSeen(String symbol) { // Note the paradigm here. We avoid any overlap by the .putIfAbsent call, // but we really want to avoid excess garbage generation so we still do the .get // first. The case where map won't be populated is extremely rare and only // on startup. Meter perSymbolMeter = _symbolStatistics.get(symbol); if (perSymbolMeter == null) { _metricsModificationLock.lock(); try { perSymbolMeter = _symbolStatistics.get(symbol); if (perSymbolMeter == null) { perSymbolMeter = _detailedRegistry.meter(_metricNamePrefix + "." + symbol); _symbolStatistics.put(symbol, perSymbolMeter); } } finally { _metricsModificationLock.unlock(); } } perSymbolMeter.mark(); if (_tickMeter != null) { _tickMeter.mark(); } } public int getNumActiveSymbols() { return _symbolStatistics.size(); } /** * Returns a copy of all symbols seen since startup. * @return All symbols seen since startup. */ public Set<String> getAllSymbols() { Set<String> allSymbols = new TreeSet<String>(_symbolStatistics.keySet()); return allSymbols; } /** * Return a description (e.g. hostname/port) for the remote endpoint * for this connection. * @return An MBean-visible remote connection name */ public String getRemoteServerConnectionName() { return getInputStreamFactory().getDescription(); } }