package com.linkedin.databus.core.monitoring.mbean;
/*
*
* Copyright 2013 LinkedIn Corp. All rights reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.apache.log4j.Logger;
import com.linkedin.databus.core.DbusEventInternalReadable;
import com.linkedin.databus.core.DbusEventInternalReadable.EventScanStatus;
import com.linkedin.databus.core.DbusEventUtils;
import com.linkedin.databus.core.monitoring.events.DbusEventsTotalStatsEvent;
import com.linkedin.databus.core.util.ConfigApplier;
import com.linkedin.databus.core.util.ConfigBuilder;
import com.linkedin.databus.core.util.InvalidConfigException;
import com.linkedin.databus.core.util.JmxUtil;
import com.linkedin.databus.core.util.ReadWriteSyncedObject;
/**
* Container for all monitoring mbeans
*/
public class DbusEventsStatisticsCollector extends ReadWriteSyncedObject
implements DbusEventsStatisticsCollectorMBean,StatsCollectorMergeable<DbusEventsStatisticsCollector>
{
public static final String MODULE = DbusEventsStatisticsCollector.class.getName();
public static final Logger LOG = Logger.getLogger(MODULE);
public static final String NO_PEER = "none";
private final DbusEventsTotalStats _totalStats;
private final HashMap<Integer, DbusEventsTotalStats> _perSourceStats;
private final HashMap<String, DbusEventsTotalStats> _perPeerStats;
private final MBeanServer _mbeanServer;
private final ObjectName _collectorObjName;
private final int _id;
private final String _name;
private final String _sanitizedName;
private final String _curPeer;
private final String _perSourceNamePrefix;
private final String _perPeerNamePrefix;
private final AtomicBoolean _enabled;
//cache the last event source index object
private Integer _lastUsedSrcIdx = Integer.valueOf(1);
public DbusEventsStatisticsCollector(int relayId, String name, boolean enabled, boolean threadSafe,
MBeanServer mbeanServer)
{
this(relayId, name, enabled, threadSafe, NO_PEER, mbeanServer);
}
protected DbusEventsTotalStats makeDbusEventsTotalStats(int ownerId,
String dimension,
boolean enabled,
boolean threadSafe,
DbusEventsTotalStatsEvent initData)
{
return new DbusEventsTotalStats(ownerId, dimension, enabled, threadSafe, initData);
}
private DbusEventsStatisticsCollector(int relayId, String name, boolean enabled,
boolean threadSafe, String client,
MBeanServer mbeanServer
)
{
super(threadSafe);
_id = relayId;
_name = name ;
_sanitizedName = AbstractMonitoringMBean.sanitizeString(_name);
_mbeanServer = mbeanServer;
_curPeer = client;
_enabled = new AtomicBoolean(enabled);
_perSourceNamePrefix = _sanitizedName + ".source.";
_perPeerNamePrefix = _sanitizedName + ".peer.";
_totalStats = makeDbusEventsTotalStats(_id, _sanitizedName + ".total", enabled, false,
null);
_perSourceStats = new HashMap<Integer, DbusEventsTotalStats>(100);
_perPeerStats = new HashMap<String, DbusEventsTotalStats>(1000);
ObjectName jmxName = null;
try
{
Hashtable<String, String> mbeanProps = new Hashtable<String, String>(5);
mbeanProps.put("name", _sanitizedName);
mbeanProps.put("type", DbusEventsStatisticsCollector.class.getSimpleName());
mbeanProps.put("relay", Integer.toString(relayId));
jmxName = new ObjectName(AbstractMonitoringMBean.JMX_DOMAIN, mbeanProps);
}
catch (Exception e)
{
LOG.error("Error creating JMX object name", e);
}
_collectorObjName = jmxName;
registerAsMBeans();
}
protected void registerAsMBeans()
{
if (null != _mbeanServer && null != _collectorObjName)
{
try
{
if (_mbeanServer.isRegistered(_collectorObjName))
{
LOG.warn("unregistering stale mbean: " + _collectorObjName);
_mbeanServer.unregisterMBean(_collectorObjName);
}
_mbeanServer.registerMBean(this, _collectorObjName);
_totalStats.registerAsMbean(_mbeanServer);
if (LOG.isDebugEnabled())
{
LOG.debug("MBean registered " + _collectorObjName);
}
}
catch (Exception e)
{
LOG.error("JMX registration failed", e);
}
}
}
public void unregisterMBeans()
{
if (null != _mbeanServer && null != _collectorObjName)
{
try
{
JmxUtil.unregisterMBeanSafely(_mbeanServer, _collectorObjName, LOG);
_totalStats.unregisterMbean(_mbeanServer);
for (String clientName: _perPeerStats.keySet())
{
_perPeerStats.get(clientName).unregisterMbean(_mbeanServer);
}
for (Integer srcId: _perSourceStats.keySet())
{
_perSourceStats.get(srcId).unregisterMbean(_mbeanServer);
}
if (LOG.isDebugEnabled())
{
LOG.debug("MBean unregistered " + _collectorObjName);
}
}
catch (Exception e)
{
LOG.error("JMX deregistration failed", e);
}
}
}
/** Creates a copy */
public DbusEventsStatisticsCollector createForPeerConnection(String client)
{
return new DbusEventsStatisticsCollector(_id, client, true, false, client, null);
}
@Override
public DbusEventsTotalStats getTotalStats()
{
return _totalStats;
}
/*//FIXME snagaraj
@Override
public DbusEventsTotalStats getPhysicalSourceStats(String physicalParitionStr)
{
PhysicalPartition ppart = PhysicalPartition.createFromSimpleString(physicalParitionStr);
Lock readLock = acquireReadLock();
try
{
DbusEventsTotalStats result =
ppart.equals(_collectorPartition) ? _totalStats :
null == _perPhysicalPartitionStats ? null : _perPhysicalPartitionStats.get(ppart);
return result;
}
finally
{
releaseLock(readLock);
}
}
*/
@Override
public List<Integer> getSources()
{
Lock readLock = acquireReadLock();
try
{
return new ArrayList<Integer>(_perSourceStats.keySet());
}
finally
{
releaseLock(readLock);
}
}
@Override
public DbusEventsTotalStats getSourceStats(int srcId)
{
Lock readLock = acquireReadLock();
try
{
if (srcId != _lastUsedSrcIdx) _lastUsedSrcIdx = srcId;
DbusEventsTotalStats result = _perSourceStats.get(_lastUsedSrcIdx);
return result;
}
finally
{
releaseLock(readLock);
}
}
@Override
public List<String> getPeers()
{
Lock readLock = acquireReadLock();
try
{
ArrayList<String> result = new ArrayList<String>(_perPeerStats.keySet());
return result;
}
finally
{
releaseLock(readLock);
}
}
@Override
public DbusEventsTotalStats getPeerStats(String peer)
{
Lock readLock = acquireReadLock();
try
{
DbusEventsTotalStats result = _perPeerStats.get(peer);
return result;
}
finally
{
releaseLock(readLock);
}
}
public void registerDataEvent(DbusEventInternalReadable e)
{
if (!_enabled.get()) return;
short srcId = e.srcId();
if (! DbusEventUtils.isControlSrcId(srcId))
{
_totalStats.registerDataEvent(e);
DbusEventsTotalStats data = getOrAddPerSourceCollector(srcId, null);
data.registerDataEvent(e);
}
else
{
_totalStats.registerSysEvent(e);
}
if (NO_PEER != _curPeer)
{
DbusEventsTotalStats clientStats = getOrAddPerPeerCollector(_curPeer, null);
if (!DbusEventUtils.isControlSrcId(srcId))
{
clientStats.registerDataEvent(e);
}
else
{
clientStats.registerSysEvent(e);
}
}
}
public void registerDataEventFiltered(DbusEventInternalReadable e)
{
if (!_enabled.get()) return;
short srcId = e.srcId();
if (!DbusEventUtils.isControlSrcId(srcId))
{
_totalStats.registerDataEventFiltered(e);
DbusEventsTotalStats data = getOrAddPerSourceCollector(srcId, null);
data.registerDataEventFiltered(e);
if (NO_PEER != _curPeer)
{
DbusEventsTotalStats clientStats = getOrAddPerPeerCollector(_curPeer, null);
clientStats.registerDataEventFiltered(e);
}
}
}
@Override
public void merge(DbusEventsStatisticsCollector other)
{
_totalStats.mergeStats(other._totalStats);
mergeInternalStats(other);
}
//helper methods for merge
void mergeInternalStats(DbusEventsStatisticsCollector other)
{
Lock otherReadLock = other.acquireReadLock();
Lock writeLock = acquireWriteLock(otherReadLock);
try
{
for (Map.Entry<Integer, DbusEventsTotalStats> srcIdEntry: other._perSourceStats.entrySet())
{
mergePerSource(srcIdEntry.getKey(), srcIdEntry.getValue(), writeLock);
}
for (Map.Entry<String, DbusEventsTotalStats> peerEntry: other._perPeerStats.entrySet())
{
mergePerPeer(peerEntry.getKey(), peerEntry.getValue(), writeLock);
}
}
catch (RuntimeException r)
{
// Do not let the exception propagate to the GlobalStatsThread
LOG.error("Received an error during merging internal stats", r);
}
finally
{
releaseLock(writeLock);
releaseLock(otherReadLock);
}
}
@Override
public void reset()
{
_totalStats.reset();
resetInternalStats();
}
//helper methods
void resetInternalStats()
{
Lock writeLock = acquireWriteLock();
try
{
for (Map.Entry<Integer, DbusEventsTotalStats> srcIdEntry: _perSourceStats.entrySet())
{
srcIdEntry.getValue().reset();
}
for (Map.Entry<String, DbusEventsTotalStats> peerEntry: _perPeerStats.entrySet())
{
peerEntry.getValue().reset();
}
}
finally
{
releaseLock(writeLock);
}
}
@Override
public void resetAndMerge(List<DbusEventsStatisticsCollector> objList)
{
//make a copy ; reset; aggregate; then do atomic copy
DbusEventsTotalStats t = _totalStats.clone(true);
t.reset();
for (DbusEventsStatisticsCollector o: objList)
{
t.mergeStats(o.getTotalStats());
}
//update this object's state atomically
Lock writeLock = acquireWriteLock();
try
{
//atomic clone of t into totalStats;
_totalStats.cloneData(t);
resetInternalStats();
for (DbusEventsStatisticsCollector o: objList)
{
mergeInternalStats(o);
}
}
finally
{
releaseLock(writeLock);
}
}
@Override
public boolean isEnabled()
{
return _enabled.get();
}
@Override
public void setEnabled(boolean enabled)
{
_enabled.set(enabled);
}
public String getSanitizedName()
{
return _sanitizedName;
}
private DbusEventsTotalStats getOrAddPerSourceCollector(int srcId, Lock writeLock)
{
Lock myWriteLock = null;
if (null == writeLock) myWriteLock = acquireWriteLock();
try
{
if (srcId != _lastUsedSrcIdx) _lastUsedSrcIdx = srcId;
DbusEventsTotalStats data = _perSourceStats.get(_lastUsedSrcIdx);
if (null == data)
{
data = new AggregatedDbusEventsTotalStats(_id, _perSourceNamePrefix + srcId, true, isThreadSafe(), null);
_perSourceStats.put(_lastUsedSrcIdx, data);
if (null != _mbeanServer)
{
data.registerAsMbean(_mbeanServer);
}
}
return data;
}
finally
{
releaseLock(myWriteLock);
}
}
private DbusEventsTotalStats getOrAddPerPeerCollector(String peer, Lock writeLock)
{
Lock myWriteLock = null;
if (null == writeLock) myWriteLock = acquireWriteLock();
try
{
DbusEventsTotalStats peerStats = _perPeerStats.get(peer);
if (null == peerStats)
{
peerStats = new AggregatedDbusEventsTotalStats(_id, _perPeerNamePrefix + peer, true,
isThreadSafe(), null);
_perPeerStats.put(peer, peerStats);
if (null != _mbeanServer)
{
peerStats.registerAsMbean(_mbeanServer);
}
}
return peerStats;
}
finally
{
releaseLock(myWriteLock);
}
}
private void mergePerSource(Integer srcId, DbusEventsTotalStats other,
Lock writeLock)
{
DbusEventsTotalStats curBean = getOrAddPerSourceCollector(srcId, writeLock);
curBean.mergeStats(other);
}
private void mergePerPeer(String peerName, DbusEventsTotalStats other,
Lock writeLock)
{
DbusEventsTotalStats curBean = getOrAddPerPeerCollector(peerName, writeLock);
curBean.mergeStats(other);
}
public class RuntimeConfig implements ConfigApplier<RuntimeConfig>
{
private final boolean _enabled;
public RuntimeConfig(boolean enabled)
{
_enabled = enabled;
}
/** A flag if events statistics collection is enabled */
public boolean isEnabled()
{
return _enabled;
}
@Override
public void applyNewConfig(RuntimeConfig oldConfig)
{
DbusEventsStatisticsCollector.this.setEnabled(this.isEnabled());
}
@Override
public boolean equals(Object other)
{
if (null == other || ! (other instanceof RuntimeConfig)) return false;
return equalsConfig((RuntimeConfig)other);
}
@Override
public int hashCode()
{
return _enabled ? 1 : 0;
}
@Override
public boolean equalsConfig(RuntimeConfig otherConfig)
{
return isEnabled() == otherConfig.isEnabled();
}
}
public static class RuntimeConfigBuilder implements ConfigBuilder<RuntimeConfig>
{
private boolean _enabled = true;
private DbusEventsStatisticsCollector _managedInstance = null;
public RuntimeConfigBuilder()
{
super();
}
public boolean isEnabled()
{
return _enabled;
}
public void setEnabled(boolean enabled)
{
_enabled = enabled;
}
@Override
public RuntimeConfig build() throws InvalidConfigException
{
if (null != _managedInstance) return _managedInstance.new RuntimeConfig(_enabled);
throw new InvalidConfigException("No ContainerStatisticsCollector instance assigned");
}
public DbusEventsStatisticsCollector getManagedInstance()
{
return _managedInstance;
}
public void setManagedInstance(DbusEventsStatisticsCollector managedInstance)
{
_managedInstance = managedInstance;
}
}
public static class StaticConfig
{
private final RuntimeConfigBuilder _runtime;
public StaticConfig(RuntimeConfigBuilder runtime)
{
_runtime = runtime;
}
/** Runtime configuration options */
public RuntimeConfigBuilder getRuntime()
{
return _runtime;
}
}
public static class Config implements ConfigBuilder<StaticConfig>
{
private final RuntimeConfigBuilder _runtime;
public Config()
{
super();
_runtime = new RuntimeConfigBuilder();
}
public RuntimeConfigBuilder getRuntime()
{
return _runtime;
}
@Override
public StaticConfig build() throws InvalidConfigException
{
return new StaticConfig(_runtime);
}
}
public String getName()
{
return _name;
}
public void registerEventError(EventScanStatus writingEventStatus)
{
_totalStats.registerEventError(writingEventStatus);
if (NO_PEER != _curPeer)
{
DbusEventsTotalStats clientStats = getOrAddPerPeerCollector(_curPeer, null);
clientStats.registerEventError(writingEventStatus);
}
}
public void registerCreationTime(long s)
{
_totalStats.registerCreationTime(s);
if (NO_PEER != _curPeer)
{
DbusEventsTotalStats clientStats = getOrAddPerPeerCollector(_curPeer, null);
clientStats.registerCreationTime(s);
}
}
public void registerBufferMetrics(long r, long s,long since, long freeSpace)
{
//only global stats make sense
_totalStats.registerBufferMetrics(r,s,since,freeSpace);
}
public void registerTimestampOfFirstEvent(long ts)
{
//only global makes sense
_totalStats.registerTimestampOfFirstEvent(ts);
}
public int getOwnerId()
{
return _id;
}
@Override
public String toString()
{
return "DbusEventsStatisticsCollector [_totalStats=" + _totalStats + ", _id=" + _id
+ ", _name=" + _name + ", _sanitizedName=" + _sanitizedName + ", _enabled="
+ _enabled + "]";
}
}