/**
* 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.sender;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URI;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import org.cliffc.high_scale_lib.NonBlockingHashMapLong;
import org.helios.apmrouter.OpCode;
import org.helios.apmrouter.collections.ConcurrentLongSlidingWindow;
import org.helios.apmrouter.collections.ILongSlidingWindow;
import org.helios.apmrouter.jmx.ConfigurationHelper;
import org.helios.apmrouter.jmx.JMXHelper;
import org.helios.apmrouter.jmx.ScheduledThreadPoolFactory;
import org.helios.apmrouter.jmx.ThreadPoolFactory;
import org.helios.apmrouter.jmx.threadinfo.ExtendedThreadManager;
import org.helios.apmrouter.metric.AgentIdentity;
import org.helios.apmrouter.metric.IMetric;
import org.helios.apmrouter.metric.catalog.ICEMetricCatalog;
import org.helios.apmrouter.metric.catalog.IMetricCatalog;
import org.helios.apmrouter.sender.netty.codec.IMetricEncoder;
import org.helios.apmrouter.sender.netty.handler.ChannelStateAware;
import org.helios.apmrouter.sender.netty.handler.ChannelStateListener;
import org.helios.apmrouter.subscription.MetricURIEvent;
import org.helios.apmrouter.subscription.MetricURISubscriptionEventListener;
import org.helios.apmrouter.trace.DirectMetricCollection;
import org.helios.apmrouter.util.RepeatingEventHandler;
import org.helios.apmrouter.util.SimpleLogger;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChannelState;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.DefaultChannelGroup;
import org.json.JSONObject;
/**
* <p>
* Title: AbstractSender
* </p>
* <p>
* Description: Abstract base class for sender implementations
* </p>
* <p>
* Company: Helios Development Group LLC
* </p>
*
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* <p>
* <code>org.helios.apmrouter.sender.AbstractSender</code>
* </p>
*/
public abstract class AbstractSender implements AbstractSenderMXBean, ISender,
ChannelPipelineFactory, ChannelStateAware {
/** A map of created senders keyed by the URI */
protected static final Map<URI, ISender> senders = new ConcurrentHashMap<URI, ISender>();
/** The metric encoder */
protected static final IMetricEncoder metricEncoder = new IMetricEncoder();
/** The count of metric sends */
protected final AtomicLong sent = new AtomicLong(0);
/** The count of dropped metric sends */
protected final AtomicLong dropped = new AtomicLong(0);
/** The count of failed metric sends */
protected final AtomicLong failed = new AtomicLong(0);
/** The count of timed out pings */
protected final AtomicLong pingTimeOuts = new AtomicLong(0);
/** The logical connected state of this sender */
protected final AtomicBoolean connected = new AtomicBoolean(false);
/** A set of subscribed MetricURISubscriptionEventListeners */
protected final Set<MetricURISubscriptionEventListener> subListeners = new CopyOnWriteArraySet<MetricURISubscriptionEventListener>();
/** Sliding window of ping times */
protected final ILongSlidingWindow pingTimes = new ConcurrentLongSlidingWindow(
64);
/** The URI of the apmrouter server to connect to */
protected final URI serverURI;
/** The sending channel */
protected Channel senderChannel;
/** To prevent endless output of repeating errors */
protected final RepeatingEventHandler<String> exceptionCountingHandler = new RepeatingEventHandler<String>();
/** Channel group to close channels */
protected ChannelGroup closeGroup = new DefaultChannelGroup("ShutDownGroup");
/** The server socket to send to */
protected InetSocketAddress socketAddress;
/** The server socket to listen on */
protected InetSocketAddress listeningSocketAddress;
/** The sender's scheduler */
protected final ScheduledThreadPoolExecutor scheduler = ScheduledThreadPoolFactory
.newScheduler("AgentScheduler");
/** The frequency in ms. of heartbeat pings to the apmrouter server */
protected long heartbeatPingPeriod = 15000;
/** The heartbeat ping timeout in ms. */
protected long heartbeatTimeout = 1000;
/** The metric URI sub/unsub timeout in ms. */
protected long metricUriOpTimeout = 2000;
/**
* The number of consecutive heartbeat ping timeouts that trigger a
* disconnected state
*/
protected int heartbeatTimeoutDiscTrigger = 2;
/** The number of consecutive heartbeat ping timeouts */
protected final AtomicLong consecutiveTimeouts = new AtomicLong(0);
/** The number of tokens received and applied to the catalog */
protected final AtomicLong processedTokens = new AtomicLong(0);
/** The metric catalog for token updates */
protected final IMetricCatalog metricCatalog;
/** The channel close future */
protected ChannelFuture closeFuture = null;
/** The channel state listener */
protected final ChannelStateListener channelStateListener = new ChannelStateListener();
/** The netty server worker pool */
protected final Executor workerPool;
/** The netty channel factory */
protected ChannelFactory channelFactory;
/** A map of subscribed URIs keyed by the rid of the request */
protected final NonBlockingHashMapLong<URI> metricSubs = new NonBlockingHashMapLong<URI>();
/** Final shutdown flag */
protected static final AtomicBoolean shutdown = new AtomicBoolean(false);
static {
if (!ExtendedThreadManager.isInstalled()) {
ExtendedThreadManager.install();
}
}
/** The ping schedule handle */
protected ScheduledFuture<?> pingScheduleHandle = null;
/**
* Creates a new AbstractSender
*
* @param serverURI
* The URI of the apmrouter server to connect to
*/
protected AbstractSender(URI serverURI) {
exceptionCountingHandler.register("PingFailed", 5, 50, 60000*5, "Ping to server failed. This message will log %s more times and then periodically\n");
this.serverURI = serverURI;
socketAddress = new InetSocketAddress(serverURI.getHost(),
serverURI.getPort());
heartbeatPingPeriod = ConfigurationHelper.getLongSystemThenEnvProperty(
HBEAT_PERIOD_PROP, DEFAULT_HBEAT_PERIOD);
heartbeatTimeout = ConfigurationHelper.getLongSystemThenEnvProperty(
HBEAT_TO_PROP, DEFAULT_HBEAT_TO);
metricUriOpTimeout = ConfigurationHelper.getLongSystemThenEnvProperty(
METRIC_URI_TO_PROP, DEFAULT_METRIC_URI_TO);
resetPingSchedule();
metricCatalog = ICEMetricCatalog.getInstance();
final String threadPrefix = "Worker/" + serverURI.getHost() + "/"
+ serverURI.getPort() + "#";
// workerPool = Executors.newCachedThreadPool(new ThreadFactory(){
// final AtomicInteger serial = new AtomicInteger();
// @Override
// public Thread newThread(Runnable r) {
// Thread t = new Thread(r, threadPrefix + serial.incrementAndGet());
// t.setDaemon(false);
// return t;
// }
// });
workerPool = ThreadPoolFactory.newCachedThreadPool(getClass()
.getPackage().getName(), getClass().getSimpleName() + "Worker/"
+ serverURI.getHost() + "/" + serverURI.getPort());
try {
JMXHelper.registerMBean(JMXHelper.objectName(new StringBuilder(
"org.helios.apmrouter.sender:protocol=")
.append(serverURI.getScheme()).append(",host=")
.append(serverURI.getHost()).append(",port=")
.append(serverURI.getPort())), this);
} catch (Exception e) {
System.err
.println("Failed to publish management interface for sender ["
+ serverURI + "]. Continuing without");
e.printStackTrace(System.err);
}
final Thread shutdownThread = new Thread("SHUTDOWN-THREAD") {
@Override
public void run() {
try {
shutdown.set(true);
if (!senderChannel
.write(ChannelBuffers.wrappedBuffer(new byte[] { OpCode.BYE
.op() }), socketAddress).await(1000)) {
SimpleLogger.warn("Failed to say BYE");
}
closeGroup.close().await(500);
channelFactory.releaseExternalResources();
SimpleLogger.info("Exiting...");
} catch (Exception ex) {
}
}
};
shutdownThread.setDaemon(true);
Runtime.getRuntime().addShutdownHook(shutdownThread);
}
/**
* Cancels the existing ping schedule if one exists and starts a new one
*/
protected void resetPingSchedule() {
if (pingScheduleHandle != null) {
pingScheduleHandle.cancel(true);
pingScheduleHandle = null;
}
log("Scheduling pings every [" + heartbeatPingPeriod + "] with a ["
+ heartbeatTimeout + "] timeout.");
pingScheduleHandle = scheduler.scheduleAtFixedRate(new Runnable() {
final long finalTimeout = heartbeatTimeout;
@Override
public void run() {
if (!ping(finalTimeout)) {
pingTimeOuts.incrementAndGet();
}
}
}, 1, heartbeatPingPeriod, TimeUnit.MILLISECONDS);
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.sender.AbstractSenderMXBean#getSentMetrics()
*/
@Override
public long getSentMetrics() {
return sent.get();
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.sender.AbstractSenderMXBean#getDroppedMetrics()
*/
@Override
public long getDroppedMetrics() {
return dropped.get();
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.sender.AbstractSenderMXBean#getFailedMetrics()
*/
@Override
public long getFailedMetrics() {
return failed.get();
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.sender.AbstractSenderMXBean#getAveragePingTime()
*/
@Override
public long getAveragePingTime() {
return pingTimes.avg();
}
/**
* Sends a ping request to the passed address
*
* @param address
* The address to ping
* @param timeout
* the timeout in ms.
* @return true if ping was confirmed within the timeout, false otherwise
*/
@Override
public boolean ping(SocketAddress address, long timeout) {
try {
StringBuilder key = new StringBuilder();
ChannelBuffer ping = encodePing(key);
senderChannel.write(ping, address);
final CountDownLatch latch = SynchOpSupport.registerSynchOp(
key.toString(), timeout);
SimpleLogger.debug("Sent ping [", key, "]");
boolean success = latch.await(timeout, TimeUnit.MILLISECONDS);
if (success) {
SimpleLogger.debug("Ping Confirmed");
exceptionCountingHandler.reset("PingFailed");
resetConsecutiveTimeouts();
} else {
if(exceptionCountingHandler.report("PingFailed")) {
SimpleLogger.warn(exceptionCountingHandler.getRemainingMessage("PingFailed"));
}
incrConsecutiveTimeouts();
}
return success;
} catch (InterruptedException e) {
return false;
}
}
/**
* Subscribes or Unsubscribes the agent to a MetricURI
*
* @param asynch
* true to send asynch, false to send synch
* @param uri
* the URI to subscribe to or unscubscribe from
* @param sub
* true to subscribe, false to unsubscribe
* @param listeners
* An optional array of listeners that will be subscribed if
* <b>sub</b> is true or unsubscribed if it is false.
* @return the request id of the request
*/
public long metricURI(boolean asynch, CharSequence uri, boolean sub,
MetricURISubscriptionEventListener... listeners) {
if (uri == null || uri.toString().trim().isEmpty())
throw new IllegalArgumentException("The passed URI was null",
new Throwable());
final long rid = SynchOpSupport.nextRequestId();
byte[] bytes = uri.toString().trim().getBytes();
final URI metricUri;
try {
metricUri = new URI(uri.toString());
} catch (Exception ex) {
throw new RuntimeException("Invalid URI [" + uri.toString() + "]",
ex);
}
ChannelBuffer cb = ChannelBuffers.buffer(1 + 8 + 4 + bytes.length);
cb.writeByte(sub ? OpCode.METRIC_URI_SUBSCRIBE.op()
: OpCode.METRIC_URI_UNSUBSCRIBE.op());
cb.writeLong(rid);
cb.writeInt(bytes.length);
cb.writeBytes(bytes);
if (listeners != null) {
for (MetricURISubscriptionEventListener listener : listeners) {
if (sub) {
registerMetricURISubscriptionEventListener(listener);
} else {
unRegisterMetricURISubscriptionEventListener(listener);
}
}
}
if (!asynch) {
String key = "" + rid;
final long _timeout = metricUriOpTimeout;
SimpleLogger.debug("Sent MetricURI ", (sub ? "sub" : "unsub"), "[",
uri, "] rid:", rid);
final CountDownLatch latch = SynchOpSupport.registerSynchOp(key,
_timeout);
final boolean complete;
final Byte success;
senderChannel.write(cb, socketAddress);
try {
complete = latch.await(_timeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException iex) {
throw new RuntimeException("MetricURI Operation Interrupted",
iex);
} finally {
success = SynchOpSupport.cancelFail(rid);
}
if (complete) {
if (success == null) {
throw new RuntimeException("MetricURI ["
+ (sub ? "sub" : "unsub") + " for URI [" + uri
+ "] returned a NULL fail code. WTF ?",
new Throwable());
}
if (success == 0)
throw new RuntimeException(
"MetricURI ["
+ (sub ? "sub" : "unsub")
+ " for URI ["
+ uri
+ "] returned a fail code.\nPlease see server log for failure reason",
new Throwable());
if (sub) {
metricSubs.put(rid, metricUri);
}
return rid;
}
throw new RuntimeException("MetricURI [" + (sub ? "sub" : "unsub")
+ " for URI [" + uri + "] timed out after [" + _timeout
+ "] ms.", new Throwable());
}
senderChannel.write(cb, socketAddress);
if (sub) {
metricSubs.put(rid, metricUri);
}
return rid;
}
/**
* Subscribes to the passed MetricURI
*
* @param asynch
* true to send asynch, false to send synch
* @param uri
* the URI to subscribe to
* @param listeners
* An optional array of listeners that will be subscribed
* @return the request id the subscription was issued with
*/
public long subscribeMetricURI(boolean asynch, CharSequence uri,
MetricURISubscriptionEventListener... listeners) {
return metricURI(asynch, uri, true, listeners);
}
/**
* Unsubscribes from the passed MetricURI
*
* @param asynch
* true to send asynch, false to send synch
* @param uri
* the URI to unsubscribe from
* @param listeners
* An optional array of listeners that will be unsubscribed
* @return the request id the unsubscription was issued with
*/
public long unSubscribeMetricURI(boolean asynch, CharSequence uri,
MetricURISubscriptionEventListener... listeners) {
return metricURI(asynch, uri, false, listeners);
}
/** The JMX notification type for new metric events */
public static final String NEW_METRIC_EVENT = "metric.event.new";
/** The JMX notification type for new metric events */
public static final String STATE_CHANGE_METRIC_EVENT = "metric.event.statechange";
/** The JMX notification type for new metric events */
public static final String DATA_METRIC_EVENT = "metric.event.data";
/**
* Handles a MetricURI Op response
*
* @param buff
* The channel buffer containing the response
*/
protected void onMetricURIOpResponse(ChannelBuffer buff) {
// METRIC_URI_SUB_CONFIRM, METRIC_URI_UNSUB_CONFIRM: // // opCode(1) +
// success(1) + rid(8) = 10
// case METRIC_URI_SUB_CONFIRM:
// case METRIC_URI_UNSUB_CONFIRM:
final byte success = buff.readByte();
final long rid = buff.readLong();
if (SynchOpSupport.cancelLatch(rid)) {
SynchOpSupport.setFail(rid, success);
}
}
/**
* Callback from agent listener when a MetricURI event is received.
*
* @param buff
* the channel buffer containing the MetricURI event
*/
public void onMetricURIEvent(ChannelBuffer buff) {
try {
int byteSize = buff.readInt();
byte[] bytes = new byte[byteSize];
buff.readBytes(bytes);
JSONObject jsonResponse = new JSONObject(new String(bytes));
MetricURIEvent event = MetricURIEvent.forEvent(jsonResponse
.getString("t"));
switch (event) {
case DATA:
for (MetricURISubscriptionEventListener listener : subListeners) {
listener.onMetricData(jsonResponse);
}
break;
case NEW_METRIC:
for (MetricURISubscriptionEventListener listener : subListeners) {
listener.onNewMetric(jsonResponse);
}
break;
case STATE_CHANGE_ENTRY:
for (MetricURISubscriptionEventListener listener : subListeners) {
listener.onMetricStateChangeEntry(jsonResponse);
}
break;
case STATE_CHANGE_EXIT:
for (MetricURISubscriptionEventListener listener : subListeners) {
listener.onMetricStateChangeExit(jsonResponse);
}
break;
case STATE_CHANGE:
for (MetricURISubscriptionEventListener listener : subListeners) {
listener.onMetricStateChange(jsonResponse);
}
break;
default:
break;
}
} catch (Exception ex) {
log("Failed to unmarshall metric URI event" + ex);
}
}
/*
* { "t":"req", "svc":"sub", "op":"start",
* "args":{"es":"jmx","esn":"service:jmx:local://DefaultDomain",
* "f":"org.helios.apmrouter.session:service=SharedChannelGroup"}, "rid":0 }
*
* long rid = buff.readLong(); int uriLength = buff.readInt(); byte[]
* uriBytes = new byte[uriLength]; buff.readBytes(uriBytes); String uri =
* new String(uriBytes);
*/
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.sender.AbstractSenderMXBean#ping(long)
*/
@Override
public boolean ping(long timeout) {
return ping(socketAddress, timeout);
}
/**
* Creates a ping channel buffer and appends the key to the passed buffer
*
* @param key
* The buffer to place the key in
* @return the ping ChannelBuffer
*/
protected ChannelBuffer encodePing(final StringBuilder key) {
String _key = new StringBuilder(AgentIdentity.ID.getHostName())
.append("-").append(AgentIdentity.ID.getAgentName())
.append("-").append(System.nanoTime()).toString();
key.append(_key);
byte[] bytes = _key.getBytes();
ChannelBuffer ping = ChannelBuffers.buffer(1 + 4 + bytes.length);
ping.writeByte(OpCode.PING.op());
ping.writeInt(bytes.length);
ping.writeBytes(bytes);
return ping;
}
/**
* Called whenever a heartbeat ping succeeds.
*/
private void resetConsecutiveTimeouts() {
consecutiveTimeouts.set(0);
if (connected.compareAndSet(false, true)) {
// fire event
}
}
/**
* Increments the consecutive timeout counter, possibly triggering a
* disconnect
*/
private void incrConsecutiveTimeouts() {
long tos = consecutiveTimeouts.incrementAndGet();
if (tos > heartbeatTimeoutDiscTrigger) {
if (connected.compareAndSet(true, false)) {
// fire event
}
}
}
/**
* Decodes a ping from the passed channel buffer, and if the resulting key
* locates a latch in the timeout map, counts it down.
*
* @param cb
* The ChannelBuffer to read the ping from
*/
protected void decodePing(ChannelBuffer cb) {
int byteCount = cb.readInt();
byte[] bytes = new byte[byteCount];
cb.readBytes(bytes);
String key = new String(bytes);
// log("Processing ping response [" + key + "]");
SynchOpSupport.cancelLatch(key);
try {
pingTimes.insert(System.nanoTime()
- Long.parseLong(key.split("-")[2]));
} catch (Exception e) {
}
// pingTimes.insert(System.nanoTime()-pingKey);
}
/**
* Out log
*
* @param msg
* the message to log
*/
public static void log(Object msg) {
System.out.println(msg);
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.sender.ISender#send(org.helios.apmrouter.metric.IMetric,
* long)
*/
@Override
public void send(IMetric metric, long timeout) throws TimeoutException {
DirectMetricCollection dcm = DirectMetricCollection
.newDirectMetricCollection(metric);
dcm.setOpCode(OpCode.SEND_METRIC_DIRECT);
String key = new StringBuilder(metric.getFQN())
.append(metric.getTime()).toString();
send(dcm);
final CountDownLatch latch = SynchOpSupport.registerSynchOp(key,
timeout);
try {
if (!latch.await(timeout, TimeUnit.MILLISECONDS)) {
throw new TimeoutException(
"Direct Metric Trace timed out after " + timeout
+ " ms. "); // [" + metric + "]");
}
} catch (InterruptedException e) {
throw new RuntimeException(
"Thread interrupted while waiting for Direct Metric Trace confirm for "
+ timeout + " ms. [" + metric + "]", e);
}
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.sender.AbstractSenderMXBean#getURI()
*/
@Override
public URI getURI() {
return serverURI;
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.sender.AbstractSenderMXBean#getHeartbeatPingPeriod()
*/
@Override
public long getHeartbeatPingPeriod() {
return heartbeatPingPeriod;
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.sender.AbstractSenderMXBean#setHeartbeatPingPeriod(long)
*/
@Override
public void setHeartbeatPingPeriod(long heartbeatPingPeriod) {
boolean reset = (this.heartbeatPingPeriod != heartbeatPingPeriod);
this.heartbeatPingPeriod = heartbeatPingPeriod;
if (reset) {
resetPingSchedule();
}
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.sender.AbstractSenderMXBean#getHeartbeatTimeout()
*/
@Override
public long getHeartbeatTimeout() {
return heartbeatTimeout;
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.sender.AbstractSenderMXBean#setHeartbeatTimeout(long)
*/
@Override
public void setHeartbeatTimeout(long heartbeatTimeout) {
boolean reset = (this.heartbeatTimeout != heartbeatTimeout);
this.heartbeatTimeout = heartbeatTimeout;
if (reset) {
resetPingSchedule();
}
this.heartbeatTimeout = heartbeatTimeout;
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.sender.AbstractSenderMXBean#getHeartbeatTimeoutTrigger()
*/
@Override
public int getHeartbeatTimeoutTrigger() {
return heartbeatTimeoutDiscTrigger;
}
/**
* Sets the number of consecutive heartbeat timeouts that will trigger a
* disconnect state
*
* @param heartbeatTimeoutDiscTrigger
* the number of consecutive heartbeat timeouts that will trigger
* a disconnect state
*/
@Override
public void setHeartbeatTimeoutTrigger(int heartbeatTimeoutDiscTrigger) {
this.heartbeatTimeoutDiscTrigger = heartbeatTimeoutDiscTrigger;
}
/**
* Returns the number of consecutive heartbeat ping timeouts
*
* @return the number of consecutive heartbeat ping timeouts
*/
@Override
public long getConsecutiveTimeouts() {
return consecutiveTimeouts.get();
}
/**
* Returns the number of processed metric tokens
*
* @return the number of processed metric tokens
*/
@Override
public long getProcessedTokens() {
return processedTokens.longValue();
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.sender.AbstractSenderMXBean#isConnnected()
*/
@Override
public boolean isConnnected() {
return connected.get();
}
// ====================================================
// Moved up from UDPSender
// ====================================================
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.sender.AbstractSenderMXBean#getName()
*/
@Override
public String getName() {
return new StringBuilder(getClass().getSimpleName()).append("[")
.append(socketAddress.getHostName()).append(":")
.append(socketAddress.getPort()).append("]").toString();
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.sender.AbstractSenderMXBean#getMetricURITimeout()
*/
@Override
public long getMetricURITimeout() {
return metricUriOpTimeout;
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.sender.AbstractSenderMXBean#setMetricURITimeout(long)
*/
@Override
public void setMetricURITimeout(long timeout) {
metricUriOpTimeout = timeout;
}
// ==================================================================
// SenderFactory Specific Impls.
// ==================================================================
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.sender.netty.handler.ChannelStateAware#getInterestedChannelStates()
*/
@Override
public abstract ChannelState[] getInterestedChannelStates();
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.sender.netty.handler.ChannelStateAware#onChannelStateEvent(boolean,
* org.jboss.netty.channel.ChannelStateEvent)
*/
@Override
public abstract void onChannelStateEvent(boolean upstream,
ChannelStateEvent stateEvent);
/**
* {@inheritDoc}
*
* @see org.jboss.netty.channel.ChannelPipelineFactory#getPipeline()
*/
@Override
public abstract ChannelPipeline getPipeline();
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.sender.ISender#send(org.helios.apmrouter.trace.DirectMetricCollection)
*/
@Override
public abstract void send(final DirectMetricCollection dcm);
public abstract void doSendHello();
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.trace.MetricSubmitter#submitDirect(org.helios.apmrouter.metric.IMetric,
* long) FIXME: Merge send and submit, they're redundant
*/
@Override
public void submitDirect(IMetric metric, long timeout)
throws TimeoutException {
send(metric, timeout);
}
/**
* Sends a HELLO op to the server
*/
public void sendHello() {
scheduler.execute(new Runnable() {
public void run() {
// log("Sending HELLO");
String key = "Hello";
doSendHello();
final CountDownLatch latch = SynchOpSupport.registerSynchOp(
key, 5000);
try {
if (!latch.await(5000, TimeUnit.MILLISECONDS)) {
SynchOpSupport.cancelLatch("Hello");
scheduler.execute(new Runnable() {
@Override
public void run() {
sendHello();
}
});
}
} catch (InterruptedException e) {
// throw new
// RuntimeException("Thread interrupted while waiting for Direct Metric Trace confirm for "
// + timeout + " ms. [" + metric + "]", e);
}
}
});
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.trace.MetricSubmitter#submit(java.util.Collection)
* FIXME: Merge send and submit, they're redundant
*/
@Override
public void submit(Collection<IMetric> metrics) {
send(DirectMetricCollection.newDirectMetricCollection(metrics
.toArray(new IMetric[0])));
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.trace.MetricSubmitter#submit(org.helios.apmrouter.metric.IMetric[])
* FIXME: Merge send and submit, they're redundant
*/
@Override
public void submit(IMetric... metrics) {
send(DirectMetricCollection.newDirectMetricCollection(metrics));
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.trace.MetricSubmitter#resetStats()
*/
@Override
public void resetStats() {
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.trace.MetricSubmitter#getQueuedMetrics()
*/
@Override
public long getQueuedMetrics() {
return 0;
}
/**
* Registers a {@link MetricURISubscriptionEventListener}
*
* @param listener
* the listener to register
*/
public void registerMetricURISubscriptionEventListener(
MetricURISubscriptionEventListener listener) {
if (listener != null) {
subListeners.add(listener);
}
}
/**
* Unregisters a {@link MetricURISubscriptionEventListener}
*
* @param listener
* the listener to unregister
*/
public void unRegisterMetricURISubscriptionEventListener(
MetricURISubscriptionEventListener listener) {
if (listener != null) {
subListeners.remove(listener);
}
}
/**
* {@inheritDoc}
*
* @see org.helios.apmrouter.sender.AbstractSenderMXBean#getMetricURIEventListenerCount()
*/
@Override
public int getMetricURIEventListenerCount() {
return subListeners.size();
}
}