/**
* 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.collector.core;
import java.lang.Thread.UncaughtExceptionHandler;
import org.helios.apmrouter.jmx.ConfigurationHelper;
import org.helios.apmrouter.jmx.JMXHelper;
import org.helios.apmrouter.server.ServerComponentBean;
import org.helios.apmrouter.trace.ITracer;
import org.helios.apmrouter.trace.TracerFactory;
import org.helios.collector.BlackoutInfo;
import org.springframework.jmx.export.annotation.*;
import javax.management.Notification;
import javax.management.ObjectName;
import java.util.Date;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
//import org.springframework.jmx.support.MetricType;
/**
* <p>Title: AbstractCollector</p>
* <p>Description: Base class for all collectors</p>
* <p>Company: Helios Development Group</p>
* @author Sandeep Malhotra (smalhotra@heliosdev.org)
*
*/
@ManagedNotifications({
@ManagedNotification(notificationTypes={"org.helios.collector.exception.notification"}, name="javax.management.Notification", description="Notification indicating exception during collect callback for any collector"),
@ManagedNotification(notificationTypes={"org.helios.collectors.AbstractCollector.CollectorState"}, name="javax.management.Notification", description="Notification indicating change in CollectorState")
})
@ManagedResource
public abstract class AbstractCollector extends ServerComponentBean implements
Callable<CollectionResult>,
Thread.UncaughtExceptionHandler,
Collector{
//TODO:
// Emit notification - done
// Expose methods and attributes to JMX - done
// streamline lifecycle
// Replace JMX's dependency from Apache Commons pool to BoneCP
private static final AtomicInteger serial = new AtomicInteger();
/** The scheduler shared amongst all collector instances */
protected static final ScheduledThreadPoolExecutor scheduler = (ScheduledThreadPoolExecutor)Executors.newScheduledThreadPool(10, new ThreadFactory(){
final ThreadGroup threadGroup = new ThreadGroup("CollectorsThreadGroup");
final ClassLoader context = AbstractCollector.class.getClassLoader();
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(threadGroup, r, "CollectorsThread#" + serial.incrementAndGet());
t.setDaemon(true);
t.setContextClassLoader(context);
return t;
}
});
/*
WAS: //ScheduledThreadPoolFactory.newScheduler("Collector");
*/
/** The tracer instance */
protected ITracer tracer = null;
/** The scheduler handle for this collector */
protected ScheduledFuture<?> scheduleHandle = null;
/** The collection period in ms. */
protected long collectionPeriod = -1L;
/** Indicates if details of a collector failures should be logged. */
protected boolean logErrors=false;
/**
* Indicates if the base class should trace the status and summary
* results after each collection. Helpful for dubugging purposes
*/
protected boolean logCollectionResult=false;
/**
* An enum that defines the state of the collector. Lifecycle operations
* should automatically set this state in each case.
*/
protected enum CollectorState {
NULL(false),
CONSTRUCTED(false),
INITIALIZING(true),
INITIALIZED(true),
INIT_FAILED(false),
STARTING(true),
STARTED(true),
START_FAILED(false),
STOPPING(false),
STOPPED(false),
COLLECTING(true),
RESETTING(true);
private CollectorState(boolean running) {
this.running = running;
}
private final boolean running;
/**
* Indicates if this state is considered running
* @return true if this state is considered running, false otherwise
*/
public boolean isRunning() {
return running;
}
}
/** Indicates the current state of the collector. */
protected CollectorState state;
/** Last time a collection was started. */
protected long lastTimeCollectionStarted = 0L;
/** Last time a collection finished. */
protected long lastTimeCollectionCompleted = 0L;
/** Last time a collection successfully completed. */
protected long lastTimeCollectionSucceeded = 0L;
/** Last time a collection failed. */
protected long lastTimeCollectionFailed = 0L;
/** Last collection elapsed time. */
protected long lastCollectionElapsedTime = 0L;
/** Total number of times a collection is done. */
protected int totalCollectionCount=0;
/** Number of time a collector succeed. */
protected int totalSuccessCount=0;
/** Number of time the collector failed. */
protected int totalFailureCount=0;
/** Total number of active collectors in this JVM. */
protected static AtomicInteger numberOfActiveCollectors = new AtomicInteger();
private static AtomicLong notificationSerialNumber = new AtomicLong(0);
/** A label to be displayed on Helios dashboard for Availability of this collector */
protected String defaultAvailabilityLabel="TargetAvailability";
/** Number of failures during collect before Helios switches to a alternate (slower) frequency for this collector */
protected int failureThreshold = 5;
/** Number of consecutive errors produced by this collector so far */
protected int consecutiveFailureCount = 0;
/** Flag that indicates whether the alternate frequency is in effect due to recent error while collection. */
protected boolean fallbackFrequencyActivated=false;
/** Number of collections to skip when fallbackFrequencyActivated is true */
protected int actualSkipped=0;
/**
* Number of collect iterations to skip when fallbackFrequencyActivated is true.
* Set this parameter to -1 to explicitly disable dormant mode. This is usually desired when the frequency
* or schedule (cron expression) for this collector is set high. For example (collectors that runs
* once every hour or daily etc...)
*
*/
protected int iterationsToSkip = 5;
/** Number of attempts made so far to restart this collector after the initial start failed. */
protected int restartAttempts = 0;
/**
* Maximum number of attempts that will be made to restart a collector whose initial start has failed.
* Though not recommended but if you want the restart to be tried indefinitely, then set
* this parameter to -1.
*/
protected int maxRestartAttempts = 3;
/** Delay before another attempt is made to restart a collector. Default is 2 minutes. */
protected long restartAttemptDelayFrequency = 1200000L;
/** Start of blackout period - expressed in HH:MM format. HH is in the 24 hour format **/
protected String blackoutStart = null;
/** End of blackout period - expressed in HH:MM format. HH is in the 24 hour format **/
protected String blackoutEnd = null;
/** Object to hold detailed blackout information */
private BlackoutInfo blackoutInfo = null;
/** The property name where the JMX domain name for all collectors would be picked */
protected static final String COLLECTORS_DOMAIN_PROPERTY="helios.collector.jmx.domain";
/** Default JMX domain name for all collectors in case COLLECTORS_DOMAIN_PROPERTY is not specified*/
protected static final String COLLECTORS_DOMAIN_DEFAULT="org.helios.collector";
/** Used during collect process */
protected final ReentrantLock collectorLock = new ReentrantLock();
/**
* Scheduled collect call should wait for this period if the current
* CollectorState is RESETTING
*/
protected long waitPeriodIfAlreadyCollecting = 0L;
/** Flag to indicate whether reset is called on this collector */
protected AtomicBoolean resetFlag = new AtomicBoolean(false);
/** Indicates that the collector should be reset every resetCount collections. */
protected int resetCount;
/** Reference of a POJO that stores information about the last collection result */
protected CollectionResult collectionResult;
protected static final Pattern namePattern = Pattern.compile("\\{(\\d++)\\}");
protected static final Pattern thisPattern = Pattern.compile("\\{THIS-PROPERTY:([a-zA-Z\\(\\)\\s-]+)}");
protected static final Pattern thisDomainPattern = Pattern.compile("\\{THIS-DOMAIN:([\\d+])}");
protected static final Pattern segmentPattern = Pattern.compile("\\{SEGMENT:([\\d+])}");
protected static final Pattern targetDomainPattern = Pattern.compile("\\{TARGET-DOMAIN:([\\d+])}");
protected static final Pattern targetPattern = Pattern.compile("\\{TARGET-PROPERTY:([a-zA-Z\\(\\)\\s-]+)}");
/** The root tracing namespace where all collected metrics will be traced to */
protected String[] tracingNameSpace;
/** The virtual tracer host */
protected String virtualHost = null;
/** The virtual tracer agent */
protected String virtualAgent = null;
protected String defaultVirtualHost = "APMRouter";
protected String defaultVirtualAgent = "Collectors";
static {
Runtime.getRuntime().addShutdownHook(new Thread(){
@Override
public void run() {
scheduler.shutdownNow();
}
});
}
/**
* Returns collection period for this collector
*/
//@ManagedMetric(category="MetricCatalogService",displayName="AverageCallTimeNs",metricType=MetricType.LONG_GAUGE, description="Frequency (ms) at which the collect will happen")
@ManagedAttribute (description = "Frequency (ms) at which the collect will happen")
public long getCollectionPeriod() {
if(collectionPeriod==-1L) {
collectionPeriod = ConfigurationHelper.getLongSystemThenEnvProperty("org.helios.collector." + getClass().getSimpleName().toLowerCase() + ".period", 15000L);
}
return collectionPeriod;
}
/**
* Set/override collection period for this collector
*/
@ManagedAttribute (description = "Frequency (ms) at which the collect will happen")
public void setCollectionPeriod(long period) {
collectionPeriod = period;
unscheduleCollect();
scheduleCollect();
}
/**
* Indicates whether this collector is started
*/
@ManagedAttribute (description = "Whether this collector is currently active")
public AtomicBoolean getStarted() {
return started;
}
public boolean isLogErrors() {
return logErrors;
}
public void setLogErrors(boolean logErrors) {
this.logErrors = logErrors;
}
@ManagedAttribute (description = "Should collection result be printed out after each collect call")
public boolean isLogCollectionResult() {
return logCollectionResult;
}
public void setLogCollectionResult(boolean logCollectionResult) {
this.logCollectionResult = logCollectionResult;
}
public String getBlackoutStart() {
return blackoutStart;
}
@ManagedOperation(description="Specify start of blackout period ")
@ManagedOperationParameters({
@ManagedOperationParameter(name = "blackoutStart", description = "Start of blackout period - expressed in HH:MM format. HH is in the 24 hour format")})
public void setBlackoutStart(String blackoutStart) {
this.blackoutStart = blackoutStart;
}
public String getBlackoutEnd() {
return blackoutEnd;
}
@ManagedOperation(description="Specify end of blackout period ")
@ManagedOperationParameters({
@ManagedOperationParameter(name = "blackoutEnd", description = "End of blackout period - expressed in HH:MM format. HH is in the 24 hour format")})
public void setBlackoutEnd(String blackoutEnd) {
this.blackoutEnd = blackoutEnd;
}
public CollectorState getState() {
return state;
}
private void setState(CollectorState state) {
this.state = state;
}
@ManagedAttribute (description = "Last time collect started")
public String getLastTimeCollectionStarted() {
return new Date(lastTimeCollectionStarted).toString();
}
@ManagedAttribute (description = "Last time collect completed")
public String getLastTimeCollectionCompleted() {
return new Date(lastTimeCollectionCompleted).toString();
}
@ManagedAttribute (description = "Last time collect completed successfully")
public String getLastTimeCollectionSucceeded() {
return new Date(lastTimeCollectionSucceeded).toString();
}
@ManagedAttribute (description = "Last time collect failed")
public String getLastTimeCollectionFailed() {
return new Date(lastTimeCollectionFailed).toString();
}
@ManagedAttribute (description = "Elapsed time (in ms) for the last collect call")
public long getLastCollectionElapsedTime() {
return lastCollectionElapsedTime;
}
@ManagedAttribute (description = "Total number of collect so far")
public int getTotalCollectionCount() {
return totalCollectionCount;
}
@ManagedAttribute (description = "Total successful collects so far")
public int getTotalSuccessCount() {
return totalSuccessCount;
}
@ManagedAttribute (description = "Total failed collect so far")
public int getTotalFailureCount() {
return totalFailureCount;
}
@ManagedAttribute (description = "Total number of colectors active currently in this JVM")
public int getNumberOfActiveCollectors() {
return numberOfActiveCollectors.get();
}
@ManagedAttribute (description = "Number of consecutive errors produced by this collector so far")
public int getConsecutiveFailureCount() {
return consecutiveFailureCount;
}
@ManagedAttribute (description = "Flag that indicates whether the alternate frequency is in effect due to recent error while collection")
public boolean isFallbackFrequencyActivated() {
return fallbackFrequencyActivated;
}
@ManagedAttribute (description = "Maximum number of attempts that will be made to restart a collector whose initial start has failed")
public int getMaxRestartAttempts() {
return maxRestartAttempts;
}
@ManagedAttribute (description = "The root tracing namespace where all collected metrics will be traced to")
public String[] getTracingNameSpace() {
return (String[])tracingNameSpace.clone();
}
public void setTracingNameSpace(String[] tracingNameSpace) {
this.tracingNameSpace = tracingNameSpace;
}
/**
* Returns the virtual host for this target MBeanServer
* @return the virtual host for this target MBeanServer
*/
@ManagedAttribute
public String getVirtualHost() {
return virtualHost;
}
/**
* Sets the virtual host for the Target MBeanServer
* @param virtualHost
*/
@ManagedAttribute
public void setVirtualHost(String virtualHost){
virtualHost = virtualHost.trim();
if(virtualHost.contains(".")) {
String[] frags = virtualHost.replace(" ", "").split("\\.");
frags = reverseArr(frags);
this.virtualHost = org.helios.apmrouter.jmx.StringHelper.fastConcatAndDelim(".", frags);
}else{
this.virtualHost = virtualHost;
}
}
/**
* Returns the virtual agent for this target MBeanServer
* @return the virtual agent for this target MBeanServer
*/
@ManagedAttribute
public String getVirtualAgent() {
return virtualAgent;
}
/**
* Sets the virtual agent for the Target MBeanServer
* @param virtualAgent
*/
@ManagedAttribute
public void setVirtualAgent(String virtualAgent){
this.virtualAgent = virtualAgent;
}
/* ================== CUSTOM LIFECYCLE METHODS =============================*/
/**
* Initializes basic resources that are critical for any collector to work properly.
* This method cannot be overriden by concrete implementation classes.
*
*/
public final void init() {
setState(CollectorState.INITIALIZING);
try{
if(virtualHost!=null && virtualAgent!=null) {
this.tracer = TracerFactory.getTracer(virtualHost, virtualAgent, getClass().getSimpleName()+"-"+getBeanName(), (int)(getCollectionPeriod()*1.2));
}else{
// virtualAgent and/or virtualHost property is not provided so switching back to default naming convention
this.tracer = TracerFactory.getTracer(defaultVirtualHost, defaultVirtualAgent, getClass().getSimpleName()+"-"+getBeanName(), (int)(getCollectionPeriod()*1.2));
//this.tracer = TracerFactory.getTracer();
}
initCollector();
setState(CollectorState.INITIALIZED);
} catch(Exception ex){
setState(CollectorState.INIT_FAILED);
if(logErrors)
error("An error occured while initializing the collector bean: "+this.getBeanName(),ex);
}
}
private void processBlackoutInfo() {
if(blackoutStart!=null && blackoutEnd!=null){
blackoutInfo = new BlackoutInfo(blackoutStart, blackoutEnd, beanName);
if(!blackoutInfo.isValidRange()){
blackoutInfo = null;
}
}
}
public ObjectName getObjectName() {
return JMXHelper.objectName(System.getProperty(COLLECTORS_DOMAIN_PROPERTY, COLLECTORS_DOMAIN_DEFAULT)+":type="+this.getClass().getName().substring( this.getClass().getName().lastIndexOf( '.' ) + 1 )+",name="+this.getBeanName());
}
/**
* An additional convenience method provided for implementing task that needs to be
* performed for initialization of this collector
*/
public void initCollector(){}
/**
* This method can be overridden by concrete implementation classes
* for any custom pre startup tasks that needs to be done.
*/
public void preStart(){}
/**
* This method is an entry point to kickstart any collectors' lifecycle. It initializes and
* performs startup tasks before this collector is scheduled with HeliosScheduler.
* This method cannot be overriden by concrete implementation classes.
*
*/
@ManagedOperation
public final void doStart() throws Exception {
try {
init();
if(getState() != CollectorState.INITIALIZED){
error("Initialization error for bean: "+this.getBeanName() + ", so no further attempts would be made to start it.");
//executeExceptionScript();
return;
}
setState(CollectorState.STARTING);
preStart();
startCollector();
postStart();
//registerInMBeanServer(this);
scheduleCollect();
//scheduleReset();
setState(CollectorState.STARTED);
numberOfActiveCollectors.incrementAndGet();
if(notificationPublisher!=null)
notificationPublisher.sendNotification(new Notification("org.helios.collector.state",this,notificationSerialNumber.incrementAndGet(),System.currentTimeMillis(),this.getBeanName() +" - " +getState().toString()));
info(banner("Collector ", this.getBeanName(), " Started"));
}catch (Exception ex){
setState(CollectorState.START_FAILED);
if(scheduleHandle!=null && !scheduleHandle.isCancelled())
scheduleHandle.cancel(false);
error("An error occured while starting the collector bean: "+this.getBeanName());
scheduleRestart();
throw ex;
//executeExceptionScript();
}
}
protected void scheduleRestart(){
if(!(maxRestartAttempts == -1) && restartAttempts >= maxRestartAttempts){
error(banner("Restart attempts for bean: "+this.getBeanName() + " is exhausted, so no further attempts will be made."));
return;
}
restartAttempts++;
if(restartAttemptDelayFrequency > 0L){
scheduler.schedule(new Runnable(){
public void run() {
try {
start();
} catch (Exception e) {}
}
}, restartAttemptDelayFrequency,TimeUnit.MILLISECONDS);
info("Another attempt to start bean: "+this.getBeanName() + " will be made in "+ restartAttemptDelayFrequency + " milliseconds.");
}
}
/**
* This method can be overridden by concrete implementation classes
* for any custom post startup tasks that needs to be done.
*/
public void postStart(){}
/**
* To be implemented by concrete classes for any custom startup tasks
*/
public void startCollector() {}
/**
* Callback method for HeliosScheduler to trigger the start of a collection.
*/
public CollectionResult call() throws CollectorException {
collect();
return collectionResult;
}
/**
* This method can be overridden by concrete implementation classes
* for any custom pre collect tasks that needs to be done.
*/
public void preCollect() {}
/**
* This method ties up the functionality and sequencing of pre, post and collectCallback methods. It cannot be
* overridden by concrete collector classes
*/
public final void collect(){
lastTimeCollectionStarted=System.currentTimeMillis();
//Check whether blackout period is active for this collector
debug("Collect called for bean: " + this.getBeanName());
processBlackoutInfo();
if(blackoutInfo!=null && blackoutInfo.isBlackoutActive()){
info("*** Skipping collection as blackout period is active...");
return;
}
//Check whether this collector is running in a dormant mode
if(fallbackFrequencyActivated && iterationsToSkip!=-1){
if(actualSkipped < iterationsToSkip ){
actualSkipped++;
debug("*** Skipping iteration " + actualSkipped + " for collector " +this.getBeanName()+" as it is in fallbackFrequencyActivated mode");
return;
}else{
actualSkipped=0;
}
}
CollectorState currState = getState();
boolean errors=false;
boolean gotLock=false;
if(collectorLock.isLocked()){
try{
gotLock = collectorLock.tryLock(waitPeriodIfAlreadyCollecting, TimeUnit.MILLISECONDS);
if(!gotLock){
if(currState == CollectorState.COLLECTING){
debug("The previous collect process is alreading running so skipping current collect call for bean: "+this.getBeanName());
return;
}else{
error("Unable to obtain a lock on collector bean: "+this.getBeanName());
return;
}
}
}catch(InterruptedException ex){}
}
if(resetFlag.get()==true || (resetCount>0 && totalCollectionCount!=0 && totalCollectionCount%resetCount==0)){
try{
this.reset();
resetFlag.getAndSet(false);
}catch(Exception ex){
if(logErrors)
error("An exception occured while resetting the collector bean: "+this.getBeanName(),ex);
}
}
if(getState() == CollectorState.STARTED){
final String threadName = Thread.currentThread().getName();
try {
Thread.currentThread().setName("Collector[" + this.getBeanName() + "]");
debug("[", threadName, "] Starting collect for bean: ", this.getBeanName());
long start = System.currentTimeMillis();
setState(CollectorState.COLLECTING);
//numberOfCollectorsRunning.incrementAndGet();
preCollect();
collectionResult = collectCallback();
if(collectionResult.getResultForLastCollection() == CollectionResult.Result.FAILURE){
throw new Exception(collectionResult.getAnyException());
}
lastTimeCollectionSucceeded=System.currentTimeMillis();
totalSuccessCount++;
if(fallbackFrequencyActivated){
/* This collector is running on a fallback frequency. Since this collect call was
* successful, switch the collect frequency back to normal schedule now. */
fallbackFrequencyActivated=false;
consecutiveFailureCount=0;
actualSkipped=0;
info("*** Frequency for collector: " + this.getBeanName() +" is switched back to normal now.");
}
//debug("Completed collect for bean: ", this.getBeanName());
info("[", threadName, "] Completed collect for bean [", this.getBeanName(), "] in [", (System.currentTimeMillis()-start), "] ms.");
} catch (Exception ex){
lastTimeCollectionFailed=System.currentTimeMillis();
errors=true;
totalFailureCount++;
consecutiveFailureCount++;
if(consecutiveFailureCount>=failureThreshold && !fallbackFrequencyActivated){
info("*** Slowing down the collect frequency for bean: " + this.getBeanName() +" as it has exceeded the collectFailureThreshold parameter.");
fallbackFrequencyActivated=true;
}
// this.sendNotification(new Notification("org.helios.collector.exception.notification",this,notificationSerialNumber.incrementAndGet(),lastTimeCollectionFailed,this.getBeanName()));
if(logErrors)
error("Collection failed for bean collector: "+this.getBeanName(),ex);
//executeExceptionScript();
}finally {
try {
totalCollectionCount++;
setState(currState);
if(!errors){
postCollect();
lastTimeCollectionCompleted=System.currentTimeMillis();
lastCollectionElapsedTime = lastTimeCollectionCompleted - lastTimeCollectionStarted;
debug("Last Collection Elapsed Time: " + lastCollectionElapsedTime + " milliseconds");
tracer.getDirectTracer().traceGauge(lastCollectionElapsedTime, "ElapsedTime", getTracingNameSpace());
}
if(logCollectionResult)
logCollectionResultDetails(collectionResult);
if(collectorLock.isLocked())
collectorLock.unlock();
//numberOfCollectorsRunning.decrementAndGet();
} finally {
Thread.currentThread().setName(threadName);
}
}
} else {
trace("Not executing collect method as the collector state is not STARTED.");
}
}
/**
* @param collectionResult
*
*/
private void logCollectionResultDetails(CollectionResult collectionResult) {
debug(collectionResult);
}
/**
* Collector specific collection tasks that should be implemented by concrete collector classes
*/
public abstract CollectionResult collectCallback() throws CollectorException;
/**
* This method can be overridden by concrete implementation classes
* for any custom pre collect tasks that needs to be done.
*/
public void postCollect() {}
/**
* This method can be overridden by concrete implementation classes
* for any custom pre stop tasks that needs to be done.
*/
public void preStop() {}
/**
* This method ties up the functionality and sequencing of pre, post and stopCollector methods. It cannot be
* overridden by concrete collector classes
*/
@ManagedOperation
public final void doStop() {
if(getState()==CollectorState.STOPPED || getState()==CollectorState.STOPPING)
return;
setState(CollectorState.STOPPING);
try {
preStop();
unscheduleCollect();
stopCollector();
postStop();
setState(CollectorState.STOPPED);
numberOfActiveCollectors.decrementAndGet();
if(notificationPublisher!=null)
notificationPublisher.sendNotification(new Notification("org.helios.collector.state",this,notificationSerialNumber.incrementAndGet(),System.currentTimeMillis(),this.getBeanName() +" - " +getState().toString()));
info(banner("Collector", this.getBeanName()+" Stopped"));
} catch (Exception ex){
debug("An error occured while stopping collector bean: " + this.getBeanName(),ex);
}
}
/**
* To be implemented by concrete classes for any custom stop tasks
*/
public void stopCollector() {}
/**
* This method can be overridden by concrete implementation classes
* for any custom post stop tasks that needs to be done.
*/
public void postStop() {
}
// /**
// * This method can be overridden by concrete implementation classes
// * for any custom pre reset tasks that needs to be done.
// */
// public void preReset() {}
//
/**
* This method ties up the functionality and sequencing of pre, post and resetCollector methods. It cannot be
* overridden by concrete collector classes
*/
public final void reset(){
CollectorState currState = getState();
setState(CollectorState.RESETTING);
// acquires a re-enterent lock to prevent collectorCallback from
// executing while reset is happening.
collectorLock.lock();
try {
//preReset();
resetCollector();
//postReset();
setState(currState);
info(banner("Reset Completed for Collector", this.getBeanName()));
} catch (Exception ex){
debug("An error occured while resetting the collector bean: " + this.getBeanName(),ex);
} finally {
collectorLock.unlock();
}
}
/**
* An additional convenience method provided for implementing task that needs to be
* performed for resetting this collector
*/
public void resetCollector() {}
/**
* This method ties up the functionality and sequencing of pre, post and destroyCollector methods. It cannot be
* overridden by concrete collector classes
*/
public final void destroy() throws CollectorException{
try {
doStop();
destroyCollector();
info(banner("Collector", this.getBeanName()," Destroyed"));
} catch(Exception ex){
throw new CollectorException("An error occured while destroying collector bean: " + this.getBeanName(),ex);
}
}
/**
* An additional convenience method provided for implementing task that needs to be
* performed for destroying this collector
*/
public void destroyCollector() {}
//
// /**
// * An additional convenience method provided for implementing task that needs to be
// * performed for resetting this collector
// */
// public void resetCollector() {}
//
// /**
// * This method can be overridden by concrete implementation classes
// * for any custom post reset tasks that needs to be done.
// */
// public void postReset() {}
/**
* @return the current state of this collector
*/
@ManagedAttribute
public String currentState() {
if(getState()==CollectorState.STARTED) return "Started";
else if(getState()==CollectorState.COLLECTING) return "Collecting";
else if(getState()==CollectorState.STOPPED) return "Stopped";
else if(getState()==CollectorState.INITIALIZED) return "Initialized";
else if(getState()==CollectorState.INITIALIZING) return "Initializing";
else if(getState()==CollectorState.INIT_FAILED) return "Initialization Failed";
else if(getState()==CollectorState.STARTING) return "Starting";
else if(getState()==CollectorState.START_FAILED) return "Start Failed";
else if(getState()==CollectorState.STOPPING) return "Stopping";
else if(getState()==CollectorState.CONSTRUCTED) return "Constructed";
else if(getState()==CollectorState.RESETTING) return "Resetting";
else return "Unknown";
}
/**
* Schedule this collector with fixed frequency
*/
public void scheduleCollect() {
long collectPeriod = getCollectionPeriod();
final UncaughtExceptionHandler ueh = this;
scheduleHandle = scheduler.scheduleAtFixedRate(new Runnable(){
public void run() { Thread.currentThread().setUncaughtExceptionHandler(ueh); call(); }
}, 0,collectPeriod, TimeUnit.MILLISECONDS);
info("Started collection schedule with frequency of ["+ collectPeriod + "] ms. for collector [" + this.getBeanName() + "]");
}
/**
* Unschedule this collector so it doesn't get triggered anymore
*/
public void unscheduleCollect() {
if(scheduleHandle!=null) {
scheduleHandle.cancel(false);
info("Unscheduled collector [" + this.getBeanName() + "]");
}
}
/**
* Applies pattern substitutions to the passed string for target properties from this MBean.
* @param name A value to be formatted.
* @return A formatted name.
*/
protected String formatName(String name) {
if(name.contains("{THIS-PROPERTY")) {
name = bindTokens(objectName, name, thisPattern);
}
if(name.contains("{THIS-DOMAIN")) {
name = bindTokens(objectName, name, thisDomainPattern);
}
if(name.contains("{SEGMENT")) {
name = bindTokens(objectName, name, segmentPattern);
}
return name.replace('/', '-');
}
/**
* Applies pattern substitutions to the passed string for target properties from the target mbean.
* @param name A value to be formatted.
* @return A formatted name.
*/
protected String formatName(String name, ObjectName remoteMBean) {
if(name.contains("{TARGET-PROPERTY")) {
name = bindTokens(remoteMBean, name, targetPattern);
}
if(name.contains("{THIS-DOMAIN")) {
name = bindTokens(objectName, name, targetDomainPattern);
}
return name.replace('/', '-');
}
/**
* Takes the text passed and replaces tokens in accordance with the pattern
* supplied taking the substitution vale from properties in the passed object name.
* @param targetObjectName The substitution values come from this object name.
* @param text The original text that will be substituted.
* @param p The pattern matcher to locate substitution tokens.
* @return The substituted string.
*/
public String bindTokens(ObjectName targetObjectName, String text, Pattern p) {
Matcher matcher = p.matcher(text);
String token = null;
String property = null;
String propertyValue = null;
int pos = -1;
while(matcher.find()) {
token = matcher.group(0);
property = matcher.group(1);
propertyValue = targetObjectName.getKeyProperty(property);
if(token.toUpperCase().contains("DOMAIN")) {
pos = Integer.parseInt(property);
propertyValue = targetObjectName.getDomain().split("\\.")[pos];
} else if(token.toUpperCase().contains("SEGMENT")) {
pos = Integer.parseInt(property);
propertyValue = tracingNameSpace[pos];
} else {
propertyValue = targetObjectName.getKeyProperty(property);
}
text = text.replace(token, propertyValue);
}
return text;
}
@Override
public void uncaughtException(Thread t, Throwable e) {
error("Uncaught Exception in Scheduled Task on Thread [", t, "]", e);
e.printStackTrace(System.err);
}
/**
* Reverses the order of the passed array
* @param arr The array to reverse
* @return The reversed order array
*/
protected String[] reverseArr(String[] arr) {
String[] ret = new String[arr.length];
for(int ri = arr.length-1, i = 0; i < arr.length; i++, ri--) {
ret[ri] = arr[i];
}
return ret;
}
// public void handleNotification(Notification notification, Object handback) {}
}