/** * 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.server.tracing.virtual; import static org.helios.apmrouter.server.tracing.virtual.VirtualState.HARDDOWN; import static org.helios.apmrouter.server.tracing.virtual.VirtualState.INIT; import java.util.Collection; import java.util.Comparator; import java.util.Date; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import javax.management.ObjectName; import org.helios.apmrouter.jmx.JMXHelper; import org.helios.apmrouter.metric.ICEMetric; import org.helios.apmrouter.metric.IMetric; import org.helios.apmrouter.metric.MetricType; import org.helios.apmrouter.trace.ITracer; import org.helios.apmrouter.trace.MetricSubmitter; import org.helios.apmrouter.trace.TracerImpl; /** * <p>Title: VirtualTracer</p> * <p>Description: A server-side, in-vm and in APMRouter server tracer that traces metrics * and availability under the identity of a {@link VirtualAgent} for a specific monitored resource.</p> * <p>Company: Helios Development Group LLC</p> * @author Whitehead (nwhitehead AT heliosdev DOT org) * <p><code>org.helios.apmrouter.server.tracing.virtual.VirtualTracer</code></p> */ public class VirtualTracer extends TracerImpl implements VirtualTracerMBean, MetricSubmitter, Comparable<VirtualTracer> { /** The virtual tracer's state */ private final AtomicReference<VirtualState> tracerState = new AtomicReference<VirtualState>(INIT); /** the assigned virtual agent serial */ protected final long serial; /** The last touch timestamp which is the timestamp of the last trace through this tracer */ protected final AtomicLong touched; /** The designated soft down period, meaning if there has been no activity for this period, the tracer is marked soft down */ protected long softDownPeriod = 30000; /** The designated hard down period, meaning if there has been no activity for this period, the tracer is marked hard down */ protected long hardDownPeriod; /** The virtual tracer name */ protected final String name; /** The delegate metric submitter */ protected final MetricSubmitter _submitter; /** The direct tracer */ protected final ITracer _directTracer; /** the availability name space */ protected final CharSequence[] avns; /** The parent virtual agent */ protected VirtualAgent vAgent; /** The JMX object name for this tracer */ protected final ObjectName objectName; /** The serial number factory for new tracers */ private static final AtomicLong serialFactory = new AtomicLong(0L); /** A counter for the number of metrics sent through this tracer */ protected static final AtomicLong sentMetrics = new AtomicLong(0L); /** * Transitions the state of this tracer * @param state The state to transition to * @return the prior state */ protected VirtualState setState(final VirtualState state) { if(state==null) throw new IllegalArgumentException("The passed state was null", new Throwable()); VirtualState priorState = tracerState.getAndSet(state); if(priorState!=state) { // ================================================================ // Case statement for non-timestamp or state actions to fire // on a valid state change // ================================================================ switch(state) { case HARDDOWN: unregisterJmx(); break; case SOFTDOWN: break; case UP: registerJmx(); break; } // ================================================================ // Notifies agent of state change. // Agent forwards to vaMgr for JMX notification // and re-adds tracer if it has been unregisters // ================================================================ vAgent.onTracerStateChange(this, state, priorState); } return priorState; } /** * {@inheritDoc} * @see org.helios.apmrouter.trace.TracerImpl#getSentMetrics() */ @Override public long getSentMetrics() { return sentMetrics.get(); } /** * {@inheritDoc} * @see org.helios.apmrouter.trace.TracerImpl#resetStats() */ @Override public void resetStats() { sentMetrics.set(0L); } /** * Registers this tracer's management MBean */ void registerJmx() { if(JMXHelper.getHeliosMBeanServer().isRegistered(objectName)) { JMXHelper.unregisterMBean(objectName); } JMXHelper.registerMBean(this, objectName); } /** * Unegisters this tracer's management MBean */ void unregisterJmx() { if(JMXHelper.getHeliosMBeanServer().isRegistered(objectName)) { try { JMXHelper.unregisterMBean(objectName); } catch (Exception ex) {/* No Op */} } } /** * Creates a new VirtualTracer * @param host The host of the virtual agent we're tracing for * @param agent The agent name of the virtual agent we're tracing for * @param tracerName The name assigned to this tracer * @param softDownPeriod The designated soft down period, meaning if there has been no activity for this period, the tracer is marked soft down * @param touched The agent provided touch * @param submitter The physical metric submitter doing the submitting */ public VirtualTracer(String host, String agent, String tracerName, long softDownPeriod, AtomicLong touched, MetricSubmitter submitter) { super(host, agent, null); this.submitter = this; _directTracer = new TracerImpl(host, agent, submitter); objectName = JMXHelper.objectName(String.format(VT_OBJ_NAME, host, agent, tracerName)); _submitter = submitter; serial = serialFactory.incrementAndGet(); name = tracerName; avns = new CharSequence[]{TRACER_NAMESPACE, name}; this.touched = touched; this.softDownPeriod = softDownPeriod; hardDownPeriod = softDownPeriod * 4; registerJmx(); touched.set(System.currentTimeMillis()); traceAvailability(true); } /** * Sets the virtual agent * @param vAgent the virtual agent */ void setAgent(VirtualAgent vAgent) { this.vAgent = vAgent; } /** * {@inheritDoc} * @see org.helios.apmrouter.server.tracing.virtual.VirtualTracerMBean#getLastTouchTimestamp() */ @Override public long getLastTouchTimestamp() { return touched.get(); } /** * {@inheritDoc} * @see org.helios.apmrouter.server.tracing.virtual.VirtualTracerMBean#getLastTouchDate() */ @Override public Date getLastTouchDate() { return new Date(touched.get()); } /** * {@inheritDoc} * @see org.helios.apmrouter.server.tracing.virtual.VirtualTracerMBean#getSerial() */ @Override public long getSerial() { return serial; } /** * {@inheritDoc} * @see org.helios.apmrouter.server.tracing.virtual.VirtualTracerMBean#touch() */ @Override public void touch() { VirtualState state = tracerState.get(); touched.set(System.currentTimeMillis()); if(state!=VirtualState.UP) { setState(VirtualState.UP); } } /** * Checks the state of the tracer to see if a state change is required */ public void checkState() { checkState(System.currentTimeMillis()); } /** * Checks the state of the tracer to see if a state change is required * @param currentTime The current time, which will be brought up to date if equal to -1. */ public void checkState(long currentTime) { long age = currentTime - touched.get(); if(age < softDownPeriod) return; if(age >= softDownPeriod && age < hardDownPeriod) { // SOFTDOWN setState(VirtualState.SOFTDOWN); } else { // HARDDOWN setState(VirtualState.HARDDOWN); } } /** * {@inheritDoc} * @see org.helios.apmrouter.server.tracing.virtual.VirtualTracerMBean#expire() */ @Override public void expire() { if(getState().ordinal()<VirtualState.SOFTDOWN.ordinal()) { touched.set(System.currentTimeMillis()-(softDownPeriod+1)); setState(VirtualState.SOFTDOWN); } } /** * Invalidates this tracer. */ @Override public void invalidate() { if(getState().ordinal()<VirtualState.HARDDOWN.ordinal()) { touched.set(System.currentTimeMillis()-(hardDownPeriod+1)); setState(VirtualState.HARDDOWN); } JMXHelper.unregisterMBean(objectName); } /** * {@inheritDoc} * @see org.helios.apmrouter.server.tracing.virtual.VirtualTracerMBean#isInvalid() */ @Override public boolean isInvalid() { return getState()==VirtualState.HARDDOWN; } /** * {@inheritDoc} * @see org.helios.apmrouter.server.tracing.virtual.VirtualTracerMBean#isExpired() */ @Override public boolean isExpired() { return getState().ordinal()>=VirtualState.SOFTDOWN.ordinal(); } /** * {@inheritDoc} * @see org.helios.apmrouter.server.tracing.virtual.VirtualTracerMBean#getTimeToSoftDown() */ @Override public long getTimeToSoftDown() { long t = (touched.get() + softDownPeriod) - System.currentTimeMillis(); return t<0 ? -1L : t; } /** * {@inheritDoc} * @see org.helios.apmrouter.server.tracing.virtual.VirtualTracerMBean#getTimeToHardDown() */ @Override public long getTimeToHardDown() { long t = (touched.get() + hardDownPeriod) - System.currentTimeMillis(); return t<0 ? -1L : t; } /** * Returns the state of this virtual tracer * @return the state of this virtual tracer */ public VirtualState getState() { return tracerState.get(); } /** * {@inheritDoc} * @see org.helios.apmrouter.server.tracing.virtual.VirtualTracerMBean#getStateName() */ @Override public String getStateName() { return tracerState.get().name(); } /** * Traces the virtual tracer availability, bypassing the virtual tracer's internal controls * @param up true to mark the virtual tracer up, false to mark it down */ protected void traceAvailability(boolean up) { _submitter.submit(ICEMetric.trace(up ? 1L : 0L, host, agent, AVAIL_METRIC_NAME, MetricType.LONG_GAUGE, avns)); } /** * Traces the availability of this tracer as up if it's not down. */ protected void traceAvailability() { traceAvailability(getState().ordinal() < VirtualState.SOFTDOWN.ordinal()); } // /** // * Traces the tracers availability // */ // void traceAvailability() { // if(System.currentTimeMillis()-lastTick.get() < timeoutPeriod) { // touch(); // } // traceAvailabilityX(getState().ordinal() < VirtualState.SOFTDOWN.ordinal()); // } /** * {@inheritDoc} * <p>Since this is a virtual agent, it uses a simple submit and is asynchronous.</p> * @see org.helios.apmrouter.trace.MetricSubmitter#submitDirect(org.helios.apmrouter.metric.IMetric, long) */ @Override public void submitDirect(IMetric metric, long timeout) throws TimeoutException { touch(); if(metric!=null) { touch(); _submitter.submit(metric); sentMetrics.incrementAndGet(); } } /** * {@inheritDoc} * @see org.helios.apmrouter.trace.MetricSubmitter#submit(java.util.Collection) */ @Override public void submit(Collection<IMetric> metrics) { if(metrics!=null && !metrics.isEmpty()) { touch(); _submitter.submit(metrics); sentMetrics.addAndGet(metrics.size()); } } /** * {@inheritDoc} * @see org.helios.apmrouter.trace.MetricSubmitter#submit(org.helios.apmrouter.metric.IMetric[]) */ @Override public void submit(IMetric... metrics) { if(metrics!=null && metrics.length!=0) { touch(); _submitter.submit(metrics); sentMetrics.addAndGet(metrics.length); } } /** * {@inheritDoc} * @see java.lang.Object#toString() */ @Override public String toString() { return String .format("VirtualTracer [serial:%s, name:%s, host:%s, agent:%s, state:%s]", serial, name, host, agent, getState()); } /** * {@inheritDoc} * @see org.helios.apmrouter.server.tracing.virtual.VirtualTracerMBean#getName() */ @Override public String getName() { return name; } /** * {@inheritDoc} * @see org.helios.apmrouter.server.tracing.virtual.VirtualTracerMBean#getKey() */ @Override public String getKey() { return name; } /** * {@inheritDoc} * @see org.helios.apmrouter.server.tracing.virtual.VirtualTracerMBean#getSoftDownPeriod() */ @Override public long getSoftDownPeriod() { return softDownPeriod; } /** * {@inheritDoc} * @see org.helios.apmrouter.server.tracing.virtual.VirtualTracerMBean#setSoftDownPeriod(long) */ @Override public void setSoftDownPeriod(long softDownPeriod) { this.softDownPeriod = softDownPeriod; } /** * {@inheritDoc} * @see org.helios.apmrouter.server.tracing.virtual.VirtualTracerMBean#getHardDownPeriod() */ @Override public long getHardDownPeriod() { return hardDownPeriod; } /** * {@inheritDoc} * @see org.helios.apmrouter.server.tracing.virtual.VirtualTracerMBean#setHardDownPeriod(long) */ @Override public void setHardDownPeriod(long hardDownPeriod) { this.hardDownPeriod = hardDownPeriod; } /** * {@inheritDoc} * @see java.lang.Comparable#compareTo(java.lang.Object) */ @Override public int compareTo(VirtualTracer otherVt) { long myTouch = touched.get(); long otherTouch = touched.get(); if(myTouch < otherTouch) return -1; if(otherTouch < myTouch) return 1; return serial < otherVt.serial ? -1 : 1; } /** * <p>Title: LastTouchDescendingComparator</p> * <p>Description: A {@link VirtualTracer} comparator to sort virtual tracers by the descending last touch time </p> * <p>Company: Helios Development Group LLC</p> * @author Whitehead (nwhitehead AT heliosdev DOT org) * <p><code>org.helios.apmrouter.server.tracing.virtual.VirtualTracer.LastTouchDescendingComparator</code></p> */ public static class LastTouchDescendingComparator implements Comparator<VirtualTracer> { /** * {@inheritDoc} * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */ @Override public int compare(VirtualTracer vt1, VirtualTracer vt2) { long vt1Time = vt1.touched.get(); long vt2Time = vt2.touched.get(); if(vt1Time < vt2Time) return 1; if(vt2Time < vt1Time) return -1; return vt1.serial < vt2.serial ? -1 : 1; } } /** * <p>Title: SoftDownDescendingComparator</p> * <p>Description: A {@link VirtualTracer} comparator to sort virtual tracers by the descending time to soft down.</p> * <p>Company: Helios Development Group LLC</p> * @author Whitehead (nwhitehead AT heliosdev DOT org) * <p><code>org.helios.apmrouter.server.tracing.virtual.VirtualTracer.SoftDownDescendingComparator</code></p> */ public static class SoftDownDescendingComparator implements Comparator<VirtualTracer> { /** * {@inheritDoc} * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */ @Override public int compare(VirtualTracer vt1, VirtualTracer vt2) { long vt1Time = vt1.getTimeToSoftDown(); long vt2Time = vt2.getTimeToSoftDown(); if(vt1Time<0) vt1Time = Long.MAX_VALUE; if(vt2Time<0) vt2Time = Long.MAX_VALUE; if(vt1Time < vt2Time) return 1; if(vt2Time < vt1Time) return -1; return vt1.serial < vt2.serial ? -1 : 1; } } /** * <p>Title: HardDownDescendingComparator</p> * <p>Description: A {@link VirtualTracer} comparator to sort virtual tracers by the descending time to hard down.</p> * <p>Company: Helios Development Group LLC</p> * @author Whitehead (nwhitehead AT heliosdev DOT org) * <p><code>org.helios.apmrouter.server.tracing.virtual.VirtualTracer.HardDownDescendingComparator</code></p> */ public static class HardDownDescendingComparator implements Comparator<VirtualTracer> { /** * {@inheritDoc} * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */ @Override public int compare(VirtualTracer vt1, VirtualTracer vt2) { long vt1Time = vt1.getTimeToHardDown(); long vt2Time = vt2.getTimeToHardDown(); if(vt1Time<0) vt1Time = Long.MAX_VALUE; if(vt2Time<0) vt2Time = Long.MAX_VALUE; if(vt1Time < vt2Time) return 1; if(vt2Time < vt1Time) return -1; return vt1.serial < vt2.serial ? -1 : 1; } } /** * {@inheritDoc} * @see org.helios.apmrouter.trace.ITracer#getDirectTracer() */ @Override public ITracer getDirectTracer() { return _directTracer; } }