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.Map; import java.util.Set; import org.apache.log4j.Logger; import com.linkedin.databus.core.monitoring.StatsCollectorCallback; /** * Collection of T objects. The individual objects are Stats objects either * (a) tracking one physical source (DB) in the case V2 relays or * (b) tracking one physical partition in the case of V3 relays. * */ public class StatsCollectors<T extends StatsCollectorMergeable<T>> { /** Elapsed time since the last merge that will trigger a new merge. */ public static final long OLD_MERGE_THRESHOLD_MS = 10; /** Contains per-physical-partition stats. */ private final HashMap<String, T> _statsCollectors; /** * In the case of multiple buffers, this holds the aggregate of all individual buffer * stats (_statsCollectors above). (Possibly also relevant for backward compatibility * when collectors don't have a physical source partition name?) */ private T _statsCollector; volatile long _lastMergeTstamp = 0; private final Logger _log; /** * Callback for addition/removal of stats objects. */ private StatsCollectorCallback<T> _statsCallback; public StatsCollectors() { this(null); } /** * Convert single instance of incoming and outgoing stats Collectors * @param defaultCollector */ public StatsCollectors(T statsCollector) { _statsCollectors = new HashMap<String,T> (16); _statsCollector = statsCollector; _log = Logger.getLogger(StatsCollectors.class.getName() + "-" + (null != statsCollector ? statsCollector.getClass().getSimpleName() : "unknown")); } public synchronized void addStatsCollector(String name, T coll) { _log.info("adding stats collector: " + name + " -> " + coll); _statsCollectors.put(name, coll); if (_statsCallback != null) _statsCallback.addedStats(coll); } public synchronized T getStatsCollector(String name) { return _statsCollectors.get(name); } /** * Returns the aggregate of all individual (per-partition) buffer stats. */ public T getStatsCollector() { //avoid frequent merges if ((System.currentTimeMillis() - _lastMergeTstamp) > OLD_MERGE_THRESHOLD_MS) { mergeStatsCollectors(); } return _statsCollector; } public synchronized T removeStatsCollector(String name) { _log.info("removing stats collector: " + name); T c = _statsCollectors.remove(name); if (_statsCallback != null) _statsCallback.removedStats(c); return c; } public ArrayList<String> getStatsCollectorKeys() { ArrayList<String> l = new ArrayList<String>(); synchronized(this) { Set<String> set = _statsCollectors.keySet(); for(String s: set) { l.add(s); } } return l; } public ArrayList<T> getStatsCollectors() { ArrayList<T> l = new ArrayList<T>(); synchronized (this) { for (Map.Entry<String,T> entry: _statsCollectors.entrySet()) { l.add(entry.getValue()); } } return l; } public void mergeStatsCollectors() { if (_statsCollector != null) { ArrayList<T> stats = getStatsCollectors(); _lastMergeTstamp = System.currentTimeMillis(); if (stats != null) { //_statsCollector thread safety assumed : but reset and merge should be atomic _statsCollector.resetAndMerge(stats); } } } public synchronized StatsCollectorCallback<T> getStatsCollectorCallback() { return _statsCallback; } /* * There are two ordering possibilities: either the SensorFactory and sensor callbacks get created first, * followed by call(s) to addStatsCollector() above (the "normal" ordering); or the consumer's client impl * creates stats first and calls addStatsCollector() before the SensorFactory and sensor callbacks exist * (less common but still valid). In the latter case we need to ensure that addedStats() gets called after * the callbacks are created, so we do that here. */ public synchronized void setStatsCollectorCallback(StatsCollectorCallback<T> c) { _statsCallback = c; if (_statsCallback != null) { for (Map.Entry<String,T> entry : _statsCollectors.entrySet()) { _statsCallback.addedStats(entry.getValue()); } } } }