/**
* Helios, OpenSource Monitoring
* Brought to you by the Helios Development Group
*
* Copyright 2007, Helios Development Group and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
*/
package org.helios.apmrouter.dataservice.json.catalog;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.management.ManagementFactory;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.sql.DataSource;
import org.helios.apmrouter.catalog.EntryStatus;
import org.helios.apmrouter.catalog.domain.Metric;
import org.helios.apmrouter.catalog.jdbc.h2.MetricTrigger;
import org.helios.apmrouter.catalog.jdbc.h2.NewElementTriggers;
import org.helios.apmrouter.collections.ConcurrentLongSlidingWindow;
import org.helios.apmrouter.dataservice.json.JsonResponse;
import org.helios.apmrouter.dataservice.json.marshalling.JSONMarshaller;
import org.helios.apmrouter.metric.IMetric;
import org.helios.apmrouter.metric.catalog.ICEMetricCatalog;
import org.helios.apmrouter.metric.catalog.IMetricCatalog;
import org.helios.apmrouter.server.ServerComponentBean;
import org.helios.apmrouter.util.SystemClock;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.jboss.netty.channel.Channel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedMetric;
import org.springframework.jmx.support.MetricType;
/**
* <p>Title: MetricURISubscriptionService</p>
* <p>Description: Service to manage subscriptions to metric events in the form of {@link MetricURI}s.</p>
* <p>Company: Helios Development Group LLC</p>
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* <p><code>org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionService</code></p>
*/
public class MetricURISubscriptionService extends ServerComponentBean implements UncaughtExceptionHandler, MetricURISubscriptionServiceMXBean {
/** Flag to indicate if the worker threads should keep running */
protected boolean keepRunning = false;
/** The hibernate session factory */
protected SessionFactory sessionFactory = null;
/** The catalog data source */
protected DataSource catalogDataSource = null;
/** The number of new metric event processing threads */
protected int newMetricEventThreads = 1;
/** The number of metric state change event processing threads */
protected int metricStateChangeEventThreads = 1;
/** The number of real-time data event processing threads */
protected int realTimeDataEventThreads = 1;
/** A serial number factory for new metric queue processor threads */
protected final AtomicInteger newMetricSerial = new AtomicInteger();
/**
* A serial number factory for metric state change event queue processor
* threads
*/
protected final AtomicInteger metricStateChangeSerial = new AtomicInteger();
/** A serial number factory for real-time data queue processor threads */
protected final AtomicInteger realTimeDataSerial = new AtomicInteger();
/** The new metric event processing thread group */
protected final ThreadGroup newMetricEventThreadGroup = new ThreadGroup("NewMetricEventThreadGroup");
/** The metric state change event processing thread group */
protected final ThreadGroup metricStateChangeEventThreadGroup = new ThreadGroup("MetricStateChangeEventThreadGroup");
/** The real-time data event processing thread group */
protected final ThreadGroup realTimeDataEventThreadGroup = new ThreadGroup("RealTimeDataEventThreadGroup");
/**
* A sliding window of the last 50 elapsed times for new metric event
* processing in ns.
*/
protected final ConcurrentLongSlidingWindow newMetricEventProcessingTime = new ConcurrentLongSlidingWindow(50);
/**
* A sliding window of the last 50 elapsed times for metric state change
* event processing in ns.
*/
protected final ConcurrentLongSlidingWindow metricStateChangeEventProcessingTime = new ConcurrentLongSlidingWindow(50);
/**
* A sliding window of the last 50 elapsed times for real-time data event
* processing in ns.
*/
protected final ConcurrentLongSlidingWindow realTimeDataEventProcessingTime = new ConcurrentLongSlidingWindow(50);
/** The metric catalog */
protected final IMetricCatalog metricCatalog = ICEMetricCatalog
.getInstance();
/** The Json marshaller */
protected JSONMarshaller marshaller = null;
/**
* The default number of threads to concurrently process the metric event
* queue
*/
public static final int DEFAULT_METRIC_QUEUE_THREAD_COUNT = ManagementFactory
.getOperatingSystemMXBean().getAvailableProcessors();
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.server.ServerComponentBean#doStart()
*/
@Override
protected void doStart() throws Exception {
keepRunning = true;
resetMetrics();
startRealTimeDataEventProcessor();
startNewMetricEventProcessor();
startMetricStateChangeEventProcessor();
}
/**
* Starts the real-time data event queue processor threads
*/
protected void startRealTimeDataEventProcessor() {
for (int i = 0; i < realTimeDataEventThreads; i++) {
Thread t = new Thread(realTimeDataEventThreadGroup,
new RealTimeDataEventProcessor(),
"RealTimeDataEventProcessorThread#"
+ newMetricSerial.incrementAndGet());
t.setDaemon(true);
t.setUncaughtExceptionHandler(this);
t.start();
}
info("Started [", realTimeDataEventThreads,
"] RealTime Data Event Queue Processing Threads");
}
/**
* Starts the new metric event queue processor threads
*/
protected void startNewMetricEventProcessor() {
for (int i = 0; i < newMetricEventThreads; i++) {
Thread t = new Thread(newMetricEventThreadGroup,
new NewMetricEventProcessor(),
"NewMetricEventProcessorThread#"
+ newMetricSerial.incrementAndGet());
t.setDaemon(true);
t.setUncaughtExceptionHandler(this);
t.start();
}
info("Started [", newMetricEventThreads,
"] New Metric Event Queue Processing Threads");
}
/**
* Starts the metric state change event queue processor threads
*/
protected void startMetricStateChangeEventProcessor() {
for (int i = 0; i < metricStateChangeEventThreads; i++) {
Thread t = new Thread(metricStateChangeEventThreadGroup,
new MetricStateChangeEventProcessor(),
"MetricStateChangeEventProcessorThread#"
+ metricStateChangeSerial.incrementAndGet());
t.setDaemon(true);
t.setUncaughtExceptionHandler(this);
t.start();
}
info("Started [", metricStateChangeEventThreads,
"] Metric State Change Event Queue Processing Threads");
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.server.ServerComponentBean#doStop()
*/
@Override
protected void doStop() {
keepRunning = false;
realTimeDataEventThreadGroup.interrupt();
metricStateChangeEventThreadGroup.interrupt();
newMetricEventThreadGroup.interrupt();
info("Interrupted Metric Event Queue Processor ThreadGroups for stop");
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionServiceMXBean#getSubscriptions()
*/
@Override
public MetricURISubscription[] getSubscriptions() {
return MetricURISubscription.subscriptions.values().toArray(
new MetricURISubscription[MetricURISubscription.subscriptions
.size()]);
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionServiceMXBean#getSubscriptionCount()
*/
@Override
public int getSubscriptionCount() {
return MetricURISubscription.subscriptions.size();
}
/**
* Subscribes the passed {@link Channel} to the specified {@link MetricURI}.
* @param metricUri The {@link MetricURI} to subscribe to
* @param response The json response to format the response (with the correct RID)
* @param channel The {@link Channel} to subscribe
*/
public void subscribeMetricURI(MetricURI metricUri, JsonResponse response, Channel channel) {
Session session = null;
try {
session = sessionFactory.openSession();
MetricURISubscription sub = MetricURISubscription.getMetricURISubscription(session, metricUri);
sub.subscribeChannel(channel, response, sub);
} catch (Exception ex) {
error("Failed to subscribe to [", metricUri.getMetricUri(), "] for channel [", channel, "] ", ex);
} finally {
if (session != null) {
try {
session.close();
} catch (Exception ex) {/* No Op */
}
}
}
}
/**
* Unsubscribes the passed {@link Channel} from the subscription to the specified {@link MetricURI}.
* @param metricUri The {@link MetricURI} to unsubscribe from
* @param channel The {@link Channel} to unsubscribe
*/
public void cancelMetricURISubscription(MetricURI metricUri, Channel channel) {
MetricURISubscription sub = MetricURISubscription.getMetricURISubscriptionOrNull(metricUri);
if (sub != null) {
sub.unSubscribeChannel(channel);
}
}
/**
* Returns the current metrics that are members of the the passed MetricURI
* @param metricUri The MetricURI to get the members of
* @return a list of metrics
*/
public List<Metric> getMetricsForURI(MetricURI metricUri) {
Session session = null;
try {
session = sessionFactory.openSession();
return metricUri.execute(session);
} finally {
try { session.close(); } catch (Exception ex) {}
}
}
/**
* Processes the resolution of a client supplied {@link MetricURI} into a list of matching metrics
* @param metricUri The metric URI to resolve
* @param response The json response to format the response (with the correct RID)
* @param channel The channel to write the response to
* @param subscribe If true, also subscribes the session to the metric URI, otherwise only retrieves the data
*/
public void resolveMetricURI(MetricURI metricUri, JsonResponse response, Channel channel, boolean subscribe) {
SystemClock.startTimer();
List<Metric> metrics = getMetricsForURI(metricUri);
response.setContent(metrics).send(channel);
if (subscribe)
subscribeMetricURI(metricUri, response, channel);
info("Metric URI Query ", SystemClock.endTimer());
}
/**
* Sets the object Json marshaller
* @param marshaller the object Json marshaller
*/
@Autowired(required = true)
public void setMarshaller(JSONMarshaller marshaller) {
this.marshaller = marshaller;
}
/**
* Sets the hibernate session factory
* @param sessionFactory the hibernate session factory
*/
@Autowired(required = true)
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* Sets the catalog data source
*
* @param catalogDataSource
* the catalog DataSource to set
*/
@Autowired(required = true)
public void setCatalogDataSource(DataSource catalogDataSource) {
this.catalogDataSource = catalogDataSource;
}
/**
* {@inheritDoc}
*
* @see java.lang.Thread.UncaughtExceptionHandler#uncaughtException(java.lang.Thread,
* java.lang.Throwable)
*/
@Override
public void uncaughtException(Thread t, Throwable e) {
error("Failed to process metric queue entry on thread [", t, "]", e);
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionServiceMXBean#getNewMetricQueueProcessingErrors()
*/
@Override
@ManagedMetric(category = "MetricURISubscriptionService", displayName = "NewMetricQueueProcessingErrors", metricType = MetricType.COUNTER, description = "The number of errors processing the new metric queued events")
public long getNewMetricQueueProcessingErrors() {
return getMetricValue("NewMetricQueueProcessingErrors");
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionServiceMXBean#getMetricStateChangeQueueProcessingErrors()
*/
@Override
@ManagedMetric(category = "MetricURISubscriptionService", displayName = "MetricStateChangeQueueProcessingErrors", metricType = MetricType.COUNTER, description = "The number of errors processing the metric state change queued events")
public long getMetricStateChangeQueueProcessingErrors() {
return getMetricValue("MetricStateChangeQueueProcessingErrors");
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionServiceMXBean#getNewMetricEventQueueDepth()
*/
@Override
@ManagedMetric(category = "MetricURISubscriptionService", displayName = "NewMetricEventQueueDepth", metricType = MetricType.GAUGE, description = "The number of pending new metric queued events")
public long getNewMetricEventQueueDepth() {
return NewElementTriggers.newMetricQueue.size();
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionServiceMXBean#getMetricStateChangeEventQueueDepth()
*/
@Override
@ManagedMetric(category = "MetricURISubscriptionService", displayName = "MetricStateChangeEventQueueDepth", metricType = MetricType.GAUGE, description = "The number of pending metric state change queued events")
public long getMetricStateChangeEventQueueDepth() {
return NewElementTriggers.metricStateChangeQueue.size();
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionServiceMXBean#getRealTimeDataEventQueueDepth()
*/
@Override
@ManagedMetric(category = "MetricURISubscriptionService", displayName = "RealTimeDataEventQueueDepth", metricType = MetricType.GAUGE, description = "The number of pending real-time data queued events")
public long getRealTimeDataEventQueueDepth() {
return NewElementTriggers.realTimeDataQueue.size();
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionServiceMXBean#getMetricStateChangeBroadcasts()
*/
@Override
@ManagedMetric(category = "MetricURISubscriptionService", displayName = "MetricStateChangeBroadcasts", metricType = MetricType.COUNTER, description = "The number of broadcast metric state change events")
public long getMetricStateChangeBroadcasts() {
return getMetricValue("MetricStateChangeBroadcasts");
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionServiceMXBean#getNewMetricBroadcasts()
*/
@Override
@ManagedMetric(category = "MetricURISubscriptionService", displayName = "NewMetricBroadcasts", metricType = MetricType.COUNTER, description = "The number of broadcast new metric events")
public long getNewMetricBroadcasts() {
return getMetricValue("NewMetricBroadcasts");
}
/**
* <p>
* Title: RealTimeDataEventProcessor
* </p>
* <p>
* Description: Queue processor for real-time data events
* </p>
* <p>
* Company: Helios Development Group LLC
* </p>
*
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* <p>
* <code>org.helios.apmrouter.dataservice.json.catalog.RealTimeDataEventProcessor</code>
* </p>
*/
protected class RealTimeDataEventProcessor implements Runnable {
@Override
public void run() {
while (keepRunning) {
try {
// This will be Object[]{long[] data, IMetric metric};
Object[] dataEvent = NewElementTriggers.realTimeDataQueue.take();
final long startTime = System.nanoTime();
incr("RealTimeDataEvents");
IMetric metric = (IMetric) dataEvent[1];
long metricId = metric.getToken();
Set<MetricURISubscription> subs = MetricURISubscription.getSubscriptionsForMetric(metricId);
if (subs == null) {
realTimeDataEventProcessingTime.insert(System.nanoTime() - startTime);
continue;
}
for (MetricURISubscription sub : subs) {
sub.sendRealTimeDataEvent(dataEvent);
}
incr("RealTimeDataEventBroadcasts", subs.size());
realTimeDataEventProcessingTime.insert(System.nanoTime() - startTime);
} catch (Exception ex) {
if (Thread.interrupted())
Thread.interrupted();
if (keepRunning) {
incr("DataEventQueueProcessingErrors");
}
} finally {
}
}
}
}
/**
* <p>
* Title: NewMetricEventProcessor
* </p>
* <p>
* Description: Queue processor for new metric events
* </p>
* <p>
* Company: Helios Development Group LLC
* </p>
*
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* <p>
* <code>org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionService.NewMetricEventProcessor</code>
* </p>
*/
protected class NewMetricEventProcessor implements Runnable {
/** The hibernate session factory */
final SessionFactory _sessionFactory = sessionFactory;
/** The catalog data source */
final DataSource _catalogDataSource = catalogDataSource;
/**
* Consumes events from the new metric queue and processes them as
* follows to find interested subscribers for the consumed new metric
* event:
* <ol>
* <li>Executes a 2 phase lookup to find interested subscribers:
* <ol>
* <li>Builds a {@link MetricType}/{@link EntryStatus}/
* {@link MetricURISubscriptionType} representing the consumed event.</li>
* <li>Acquires an iterator of {@link MetricURISubscription} instances
* that match the created mask. If none, drop event.</li>
* <li>For each {@link MetricURISubscription} in the returned iterator:
* <ol>
* <li>If the metric ID is already in the subscription's metric id set
* (unlikely), then drops the event.</li>
* <li>If the metric ID is <b>not</b> in the subscription's metric id
* set, the subscription's criteria query is executed to see if the new
* metric id is a member of the subscription.</li>
* <li>Once it has been determined that there is at least one interested
* subscriber, the event is resolved into the actual {@link Metric}
* instance.</li>
* <li>If the metric ID <b>is</b> a member of the subscription's
* criteria, it is added to the subscription's metric id set and the
* {@link Metric} instance is published to the subscriber.</li>
* </ol>
* </li>
* </ol>
* </li>
* </ol>
* {@inheritDoc}
*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
while (keepRunning) {
try {
Object[] newMetricEvent = NewElementTriggers.newMetricQueue.take();
incr("NewMetricEvents");
final String newMetricStr = Arrays.toString(newMetricEvent);
debug("NewMetric:", newMetricStr);
final long startTime = System.nanoTime();
long metricId = (Long) newMetricEvent[MetricTrigger.METRIC_COLUMN_ID];
int metricType = ((Number) newMetricEvent[MetricTrigger.TYPE_COLUMN_ID]).intValue();
Iterator<MetricURISubscription> subIter = MetricURISubscription.getMatchingSubscriptions(
org.helios.apmrouter.metric.MetricType.valueOf(metricType).getMask(),
EntryStatus.ALL_STATUS_MASK, // new metrics are always active, but we want the search to be neutral for status, so we turn all the bits on
MetricURISubscriptionType.NEW_METRIC.getMask()
);
if (subIter == null) {
debug("No subscribers for new metric [", newMetricEvent[3] , "/", newMetricEvent[6], "]");
continue;
}
Metric lazyMetric = null;
while (subIter.hasNext()) {
MetricURISubscription subscription = subIter.next();
if (subscription.hasMetricId(metricId)) {
continue;
}
if (!subscription.resolveMembership(metricId, _catalogDataSource)) {
debug("Metric [", newMetricEvent[3] , "/", newMetricEvent[6], "] did not resolve for sub [", subscription.getMetricURI().getMetricUri(), "]");
continue;
}
subscription.addMetricId(metricId);
if (lazyMetric == null) {
lazyMetric = getMetric(metricId, _sessionFactory);
if (lazyMetric == null) {
warn("Metric [", newMetricEvent[3] , "/", newMetricEvent[6], "] failed catalog lookup");
// this means we got a metric id, but could not find it in the metric catalog
incr("FailedNewMetricCatalogLookups");
break; // we don't want to handle this error here.
}
}
subscription.sendSubscribersNewMetric(lazyMetric);
debug("Broadcast Metric [", newMetricEvent[3] , "/", newMetricEvent[6], "] for sub [", subscription.getMetricURI().getMetricUri(), "] with [", subscription.subscribedChannels.size(), "] channels");
incr("NewMetricBroadcasts");
}
final long elapsed = System.nanoTime() - startTime;
newMetricEventProcessingTime.insert(elapsed);
} catch (Exception ex) {
if (Thread.interrupted())
Thread.interrupted();
if (keepRunning) {
incr("NewMetricQueueProcessingErrors");
}
}
}
}
}
/**
* <p>
* Title: MetricStateChangeEventProcessor
* </p>
* <p>
* Description: Queue processor for metric state change events
* </p>
* <p>
* Company: Helios Development Group LLC
* </p>
*
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* <p>
* <code>org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionService.MetricStateChangeEventProcessor</code>
* </p>
*/
protected class MetricStateChangeEventProcessor implements Runnable {
/** The hibernate session factory */
final SessionFactory _sessionFactory = sessionFactory;
/** The catalog data source */
final DataSource _catalogDataSource = catalogDataSource;
/**
* Consumes events from the metric state change event queue and
* processes them as follows to find interested subscribers for the
* consumed metric state change event:
* <ol>
* <li>Executes a 2 phase lookup to find interested subscribers:
* <ol>
* <li>Builds a {@link MetricType}/{@link EntryStatus}/
* {@link MetricURISubscriptionType} representing the consumed event.</li>
* <li>Acquires an iterator of {@link MetricURISubscription} instances
* that match the created mask. If none, drop event.</li>
* <li>For each {@link MetricURISubscription} in the returned iterator:
* <ol>
* <li>If the metric ID is <b>not</b> in the subscription's metric id
* set, the subscription's criteria query is executed to see if the new
* metric id is a member of the subscription.</li>
* <li>Once it has been determined that there is at least one interested
* subscriber, the event is resolved into the actual {@link Metric}
* instance.</li>
* <li>If the metric ID <b>is</b> a member of the subscription's
* criteria, it is added to the subscription's metric id set and the
* {@link Metric} instance is published to the subscriber.</li>
* </ol>
* </li>
* </ol>
* </li>
* </ol>
* {@inheritDoc}
*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
while (keepRunning) {
try {
Object[] metricStateChangeEvent = NewElementTriggers.metricStateChangeQueue.take();
incr("MetricStateChangeEvents");
final long startTime = System.nanoTime();
long metricId = (Long) metricStateChangeEvent[MetricTrigger.METRIC_COLUMN_ID];
int metricType = ((Number) metricStateChangeEvent[MetricTrigger.TYPE_COLUMN_ID]).intValue();
byte newState = ((Number) metricStateChangeEvent[MetricTrigger.STATE_COLUMN_ID]).byteValue();
EntryStatus newStatus = EntryStatus.forByte(newState);
// We're getting ALL, only filtering by metric type
Iterator<MetricURISubscription> subIter = MetricURISubscription
.getMatchingSubscriptions(
org.helios.apmrouter.metric.MetricType
.valueOf(metricType).getMask(),
EntryStatus.ALL_STATUS_MASK,
MetricURISubscriptionType.ALL_SUB_TYPES_MASK);
if (subIter == null)
continue;
Metric lazyMetric = null;
while (subIter.hasNext()) {
MetricURISubscription subscription = subIter.next();
if (subscription.hasMetricId(metricId)) {
if (subscription.isInterestedInState(newState)) {
// the subscription is still interested in the
// metric, so no membership change
if (subscription.isInterestedInStateChanges()) {
// there's no membership change, but the
// subscriber wants to know about
// state changes of metrics in its existing
// membership
subscription.sendStateChangeEvent(metricId,
EntryStatus.forByte(newState));
}
} else {
// the subscription is no longer interested in
// the metric after the state change
// send exit
subscription.removeMetricId(metricId);
}
continue;
}
if (!subscription.resolveMembership(metricId,
_catalogDataSource)) {
// subscription did not have this metric, but
// resolveMembership determined it was not elligible
continue;
}
// subscription did not have this metric, and
// resolveMembership determined it is elligible
subscription.addMetricId(metricId);
if (lazyMetric == null) {
lazyMetric = getMetric(metricId, _sessionFactory);
if (lazyMetric == null) {
incr("MetricStateChangeQueueProcessingErrors");
break; // we don't want to handle this error
// here.
}
}
subscription.sendSubscribersNewMetric(lazyMetric);
// subscription.sendSubscribersMetricStateChange(newStatus.name(),
// lazyMetric);
incr("MetricStateChangeBroadcasts");
}
final long elapsed = System.nanoTime() - startTime;
metricStateChangeEventProcessingTime.insert(elapsed);
} catch (Exception ex) {
if (Thread.interrupted())
Thread.interrupted();
if (keepRunning) {
incr("MetricStateChangeQueueProcessingErrors");
}
}
}
}
}
/**
* Looks up the {@link Metric} for the passed metric ID
*
* @param metricId
* The metric ID to look up
* @param sf
* The factory to provide the lookup session
* @return the located metric or null
*/
public Metric getMetric(long metricId, SessionFactory sf) {
Session session = null;
try {
session = sf.openSession();
// return (Metric)session.get(Metric.class, metricId);
return (Metric) session.get("metric", metricId);
} catch (Exception ex) {
error("Failed to resolve metricId to Metric through hibernate", ex);
return null;
} finally {
if (session != null)
try {
session.close();
} catch (Exception x) {/* No Op */
}
}
}
/**
* Returns the number of new metric event queue processing threads
*
* @return the number of new metric event queue processing threads
*/
@Override
@ManagedAttribute(description = "The number of threads to concurrently process the new metric event queue")
public int getNewMetricEventThreads() {
return newMetricEventThreads;
}
/**
* Sets the number of new metric event queue processing threads
*
* @param newMetricEventThreads
* the number of new metric event queue processing threads
*/
@Override
@ManagedAttribute(description = "The number of threads to concurrently process the new metric event queue")
public void setNewMetricEventThreads(int newMetricEventThreads) {
this.newMetricEventThreads = newMetricEventThreads;
}
/**
* Returns the number of metric state change event queue processing threads
*
* @return the number of metric state change event queue processing threads
*/
@Override
@ManagedAttribute(description = "The number of threads to concurrently process the metric state change event queue")
public int getMetricStateChangeEventThreads() {
return metricStateChangeEventThreads;
}
/**
* Sets the number of metric state change event queue processing threads
*
* @param metricStateChangeEventThreads
* the number of metric state change event queue processing
* threads
*/
@Override
@ManagedAttribute(description = "The number of threads to concurrently process the metric state change event queue")
public void setMetricStateChangeEventThreads(
int metricStateChangeEventThreads) {
this.metricStateChangeEventThreads = metricStateChangeEventThreads;
}
// /**
// * {@inheritDoc}
// * @see java.lang.Runnable#run()
// */
// @Override
// public void run() {
// info("Started MetricQueueThreadProcessor");
// while(keepRunning) {
// try {
// Object[] newMetricEvent =
// NewElementTriggers.metricStateChangeQueue.take();
// long metricId = (Long)newMetricEvent[MetricTrigger.METRIC_COLUMN_ID];
// if(STATE_CHANGE_METRIC_EVENT.equals(newMetricEvent[0])) {
// byte newState = (Byte)newMetricEvent[2];
// } else if(NEW_METRIC_EVENT.equals(newMetricEvent[0])) {
//
// }
// // if(notification.getType().startsWith(MetricTrigger.NEW_METRIC)) {
// // Object[] newRow = (Object[])notification.getUserData();
// // String FQN = notification.getType().replace(MetricTrigger.NEW_METRIC,
// "");
// // System.err.println("Processing new metric notification [" + FQN +
// "]");
// // long metricId = (Long)newRow[MetricTrigger.METRIC_COLUMN_ID];
// // MetricURISubscription.onNewMetric(metricId, FQN, newRow,
// catalogDataSource);
// // }
// // process
// } catch (Exception ex) {
// if(Thread.interrupted()) Thread.interrupted();
// }
// }
// }
/**
* Returns the rolling average of the last 50 new metric event processing
* elapsed times in ns.
*
* @return the rolling average of the last 50 new metric event processing
* elapsed times in ns.
*/
@Override
@ManagedMetric(category = "MetricURISubscriptionNewMetrics", displayName = "AverageNewMetricProcessingTimeNs", metricType = MetricType.GAUGE, description = "The rolling average of the last 50 new metric event processing elapsed times in ns.")
public long getAverageNewMetricProcessingTimeNs() {
return newMetricEventProcessingTime.avg();
}
/**
* Returns the rolling average of the last 50 new metric event processing
* elapsed times in ms.
*
* @return the rolling average of the last 50 new metric event processing
* elapsed times in ms.
*/
@Override
@ManagedMetric(category = "MetricURISubscriptionNewMetrics", displayName = "AverageNewMetricProcessingTimeMs", metricType = MetricType.GAUGE, description = "The rolling average of the last 50 new metric event processing elapsed times in ms.")
public long getAverageNewMetricProcessingTimeMs() {
return TimeUnit.MILLISECONDS.convert(
newMetricEventProcessingTime.avg(), TimeUnit.NANOSECONDS);
}
/**
* Returns the last metric event processing elapsed time in ns.
*
* @return the last metric event processing elapsed time in ns.
*/
@Override
@ManagedMetric(category = "MetricURISubscriptionNewMetrics", displayName = "LastNewMetricProcessingTimeNs", metricType = MetricType.GAUGE, description = "The last new metric event processing elapsed time in ns.")
public long getLastNewMetricProcessingTimeNs() {
return newMetricEventProcessingTime.getNewest();
}
/**
* Returns the last metric event processing elapsed time in ms.
*
* @return the last metric event processing elapsed time in ms.
*/
@Override
@ManagedMetric(category = "MetricURISubscriptionNewMetrics", displayName = "LastNewMetricProcessingTimeMs", metricType = MetricType.GAUGE, description = "The last new metric event processing elapsed time in ms.")
public long getLastNewMetricProcessingTimeMs() {
return TimeUnit.MILLISECONDS.convert(
getLastNewMetricProcessingTimeNs(), TimeUnit.NANOSECONDS);
}
// ===============================================================
/**
* Returns the rolling average of the last 50 metric state change processing
* elapsed times in ns.
*
* @return the rolling average of the last 50 metric state change processing
* elapsed times in ns.
*/
@Override
@ManagedMetric(category = "MetricURISubscriptionStateChanges", displayName = "AverageMetricStateChangeProcessingTimeNs", metricType = MetricType.GAUGE, description = "The rolling average of the last 50 metric state change processing elapsed times in ns.")
public long getAverageMetricStateChangeProcessingTimeNs() {
return metricStateChangeEventProcessingTime.avg();
}
/**
* Returns the rolling average of the last 50 metric state change processing
* elapsed times in ms.
*
* @return the rolling average of the last 50 metric state change processing
* elapsed times in ms.
*/
@Override
@ManagedMetric(category = "MetricURISubscriptionStateChanges", displayName = "AverageMetricStateChangeProcessingTimeMs", metricType = MetricType.GAUGE, description = "The rolling average of the last 50 metric state change processing elapsed times in ms.")
public long getAverageMetricStateChangeProcessingTimeMs() {
return TimeUnit.MILLISECONDS.convert(
metricStateChangeEventProcessingTime.avg(),
TimeUnit.NANOSECONDS);
}
/**
* Returns the last metric event processing elapsed time in ns.
*
* @return the last metric event processing elapsed time in ns.
*/
@Override
@ManagedMetric(category = "MetricURISubscriptionStateChanges", displayName = "LastMetricStateChangeProcessingTimeNs", metricType = MetricType.GAUGE, description = "The last metric state change processing elapsed time in ns.")
public long getLastMetricStateChangeProcessingTimeNs() {
return metricStateChangeEventProcessingTime.getNewest();
}
/**
* Returns the last metric event processing elapsed time in ms.
*
* @return the last metric event processing elapsed time in ms.
*/
@Override
@ManagedMetric(category = "MetricURISubscriptionStateChanges", displayName = "LastMetricStateChangeProcessingTimeMs", metricType = MetricType.GAUGE, description = "The last metric state change processing elapsed time in ms.")
public long getLastMetricStateChangeProcessingTimeMs() {
return TimeUnit.MILLISECONDS.convert(getLastMetricStateChangeProcessingTimeNs(),TimeUnit.NANOSECONDS);
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionServiceMXBean#getNewMetricEvents()
*/
@Override
@ManagedMetric(category = "MetricURISubscriptionNewMetrics", displayName = "NewMetricEvents", metricType = MetricType.COUNTER, description = "The total number of new metric events received")
public long getNewMetricEvents() {
return getMetricValue("NewMetricEvents");
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionServiceMXBean#getFailedNewMetricCatalogLookups()
*/
@Override
@ManagedMetric(category = "MetricURISubscriptionNewMetrics", displayName = "FailedNewMetricCatalogLookups", metricType = MetricType.COUNTER, description = "The total number of failed metric catalog lookups")
public long getFailedNewMetricCatalogLookups() {
return getMetricValue("FailedNewMetricCatalogLookups");
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionServiceMXBean#getMetricStateChangeEvents()
*/
@Override
@ManagedMetric(category = "MetricURISubscriptionStateChanges", displayName = "MetricStateChangeEvents", metricType = MetricType.COUNTER, description = "The total number of metric state change events received")
public long getMetricStateChangeEvents() {
return getMetricValue("MetricStateChangeEvents");
}
// DataEventQueueProcessingErrors, RealTimeDataEventBroadcasts,
// realTimeDataEventProcessingTime
/**
* {@inheritDoc}
* @see org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionServiceMXBean#getRealTimeDataQueueProcessingErrors()
*/
@Override
@ManagedMetric(category = "RealTimeDataEvents", displayName = "DataEventQueueProcessingErrors", metricType = MetricType.COUNTER, description = "The total number of real-time data event queue processing errors")
public long getRealTimeDataQueueProcessingErrors() {
return getMetricValue("DataEventQueueProcessingErrors");
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionServiceMXBean#getRealTimeDataEventBroadcasts()
*/
@Override
@ManagedMetric(category = "RealTimeDataEvents", displayName = "RealTimeDataEventBroadcasts", metricType = MetricType.COUNTER, description = "The total number of real-time data event queue broadcasts")
public long getRealTimeDataEventBroadcasts() {
return getMetricValue("RealTimeDataEventBroadcasts");
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionServiceMXBean#getAverageRealTimeDataProcessingTimeNs()
*/
@Override
@ManagedMetric(category = "RealTimeDataEvents", displayName = "AverageRealTimeDataProcessingTimeNs", metricType = MetricType.GAUGE, description = "The average elapsed time of the last 50 real-time data events in ns.")
public long getAverageRealTimeDataProcessingTimeNs() {
return realTimeDataEventProcessingTime.avg();
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionServiceMXBean#getAverageRealTimeDataProcessingTimeMs()
*/
@Override
@ManagedMetric(category = "RealTimeDataEvents", displayName = "AverageRealTimeDataProcessingTimeMs", metricType = MetricType.GAUGE, description = "The average elapsed time of the last 50 real-time data events in ms.")
public long getAverageRealTimeDataProcessingTimeMs() {
return TimeUnit.MILLISECONDS.convert(realTimeDataEventProcessingTime.avg(), TimeUnit.NANOSECONDS);
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionServiceMXBean#getLastRealTimeDataProcessingTimeNs()
*/
@Override
@ManagedMetric(category = "RealTimeDataEvents", displayName = "LastRealTimeDataProcessingTimeNs", metricType = MetricType.GAUGE, description = "The last elapsed time of a real-time data event in ns.")
public long getLastRealTimeDataProcessingTimeNs() {
return realTimeDataEventProcessingTime.getLast();
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionServiceMXBean#getLastRealTimeDataProcessingTimeMs()
*/
@Override
@ManagedMetric(category = "RealTimeDataEvents", displayName = "LastRealTimeDataProcessingTimeMs", metricType = MetricType.GAUGE, description = "The last elapsed time of a real-time data event in ms.")
public long getLastRealTimeDataProcessingTimeMs() {
return TimeUnit.MILLISECONDS.convert(realTimeDataEventProcessingTime.getLast(), TimeUnit.NANOSECONDS);
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.dataservice.json.catalog.MetricURISubscriptionServiceMXBean#getRealTimeDataEvents()
*/
@Override
@ManagedMetric(category = "RealTimeDataEvents", displayName = "EventsProcessed", metricType = MetricType.COUNTER, description = "The total number of real-time data events processed")
public long getRealTimeDataEvents(){
return getMetricValue("RealTimeDataEvents");
}
}