/**
* 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.metric.catalog;
import org.helios.apmrouter.metric.IMetric;
import org.helios.apmrouter.metric.MetricType;
import java.util.ArrayList;
import java.util.List;
import static org.helios.apmrouter.util.Methods.nvl;
/**
* <p>Title: ICEMetricCatalog</p>
* <p>Description: Maintains a map of all created {@link IDelegateMetric}s </p>
* <p>Company: Helios Development Group LLC</p>
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* <p><code>org.helios.apmrouter.metric.ICEMetricCatalog</code></p>
*/
public class ICEMetricCatalog implements IMetricCatalog {
/** The singleton instance */
private static volatile ICEMetricCatalog instance = null;
/** The singleton instance ctor lock */
private static final Object lock = new Object();
/** The system property that defines the metric factory name*/
public static final String METRIC_FACTORY_PROP = "apmrouter.metric.factory.name";
/** The default metric factory name*/
public static final String DEFAULT_METRIC_FACTORY = "org.helios.apmrouter.metric.catalog.heap.StringKeyedHeapMetricCatalog";
/** The delegate concrete catalog implementation, loaded using the class name defined by the system property <b><code>METRIC_FACTORY_PROP</code></b>. */
private volatile IMetricCatalog actualCatalog;
/**
* Acquires the singleton instance
* @return the singleton ICEMetricCatalog
*/
public static ICEMetricCatalog getInstance() {
if(instance==null) {
synchronized(lock) {
if(instance==null) {
instance = new ICEMetricCatalog();
}
}
}
return instance;
}
@SuppressWarnings("unchecked")
private ICEMetricCatalog() {
String className = System.getProperty(METRIC_FACTORY_PROP, DEFAULT_METRIC_FACTORY);
IMetricCatalog tmp = null;
try {
Class<IMetricCatalog> clazz = (Class<IMetricCatalog>) Class.forName(className);
tmp = clazz.newInstance();
} catch (Exception e) {
try {
Class<IMetricCatalog> clazz = (Class<IMetricCatalog>) Class.forName(DEFAULT_METRIC_FACTORY);
tmp = clazz.newInstance();
} catch (Exception ex) {
throw new RuntimeException("Failed to load configured class name [" + className + "] and default class name [" + DEFAULT_METRIC_FACTORY + "]", ex);
}
}
actualCatalog = tmp;
}
/**
* Resets the metric catalog delegate. This is a testing hook.
* <p><b>DO NOT CALL THIS METHOD UNLESS YOU KNOW WHAT YOU'RE DOING.</b>
* @param newClassName
*/
@SuppressWarnings("unused")
private static synchronized void reset(String newClassName) {
if(instance==null || instance.actualCatalog==null) return;
System.setProperty(METRIC_FACTORY_PROP, nvl(newClassName, "ICEMetricCatalog Reset ClassName"));
IMetricCatalog toBeDisposed = instance.actualCatalog;
instance.actualCatalog = null;
instance = null;
toBeDisposed.dispose();
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.metric.catalog.IMetricCatalog#size()
*/
@Override
public int size() {
return actualCatalog.size();
}
/**
* Returns the delegate metric id with the passed token
* @param metricIdToken The token to resolve
* @return The resolved metric ID or null if not found
*/
@Override
public IDelegateMetric get(long metricIdToken) {
return actualCatalog.get(metricIdToken);
}
/**
* Sets the serialization token for the passed metric identifier
* @param host The host name
* @param agent The agent name
* @param name The metric name
* @param type The metric type
* @param namespace The namespace segments
* @return the assigned token
*/
@Override
public long setToken(String host, String agent, CharSequence name, MetricType type, CharSequence... namespace) {
return actualCatalog.setToken(host, agent, name, type, namespace);
}
/**
* Sets the serialization token for the passed metric identifier
* @param token The token to set on the metric
* @param host The host name
* @param agent The agent name
* @param name The metric name
* @param type The metric type
* @param namespace The namespace segments
* @return the assigned token
*/
@Override
public long setToken(long token, String host, String agent, CharSequence name, MetricType type, CharSequence... namespace) {
return actualCatalog.setToken(token, host, agent, name, type, namespace);
}
/**
* Retrieves the named IDelegateMetric, creating it if it not in the catalog
* @param host The host name
* @param agent The agent name
* @param name The metric name
* @param type The metric type
* @param namespace The namespace segments
* @return the IDelegateMetric
*/
@Override
public IDelegateMetric get(String host, String agent, CharSequence name, MetricType type, CharSequence... namespace) {
return actualCatalog.get(host, agent, name, type, namespace);
}
/**
* Retrieves the named IDelegateMetric, creating it if it not in the catalog
* @param host The host name
* @param agent The agent name
* @param name The metric name
* @param type The metric type
* @param namespace The namespace segments
* @return the IDelegateMetric
*/
@Override
public IDelegateMetric get(String host, String agent, CharSequence name, int type, CharSequence... namespace) {
return actualCatalog.get(host, agent, name, type, namespace);
}
/**
* Retrieves the named IDelegateMetric, creating it if it not in the catalog
* @param host The host name
* @param agent The agent name
* @param name The metric name
* @param type The metric type
* @param namespace The namespace segments
* @return the IDelegateMetric
*/
@Override
public IDelegateMetric get(String host, String agent, CharSequence name, String type, CharSequence... namespace) {
return actualCatalog.get(host, agent, name, type, namespace);
}
/**
* Returns the delta for the passed value and metricId key
* @param value The long value to get the delta for
* @param host The host name
* @param agent The agent name
* @param name The metric name
* @param namespace The namespace
* @return the delta value or null if this was the first call for the metric
*/
@Override
public Long getDelta(long value, String host, String agent, CharSequence name, CharSequence... namespace) {
return instance.actualCatalog.getDelta(value, host, agent, name, namespace);
}
/**
* Returns the class name of the current catalog in use
* @return the class name of the current catalog in use
*/
public static String getCatalogClassName() {
return (instance!=null && instance.actualCatalog!=null) ? instance.actualCatalog.getClass().getName() : null;
}
/**
* Returns a delegate metric for the passed FQN and type
* @param fqn The metric FQN
* @param type The metric type
* @return the delegate metric
*/
@Override
public IDelegateMetric build(String fqn, MetricType type) {
return build(fqn, type, actualCatalog);
}
/**
* Returns a delegate metric for the passed FQN and type
* @param fqn The metric FQN
* @param type The metric type
* @param catalog The catalog to look the metric up in
* @return the delegate metric
*/
public static IDelegateMetric build(String fqn, MetricType type, IMetricCatalog catalog) {
nvl(fqn, "FQN");
nvl(type, "MetricType");
String host=null, agent=null, metricName=null;
List<CharSequence> namespace = new ArrayList<CharSequence>();
int sequence = 0;
int lastIndex = 0;
int index = fqn.indexOf(IMetric.NSDELIM, 0);
while(index!=-1) {
if(sequence==0) host = fqn.substring(lastIndex, index);
else if(sequence==1) agent = fqn.substring(lastIndex+1, index);
else namespace.add(fqn.substring(lastIndex+1, index));
sequence++;
lastIndex = index;
index = fqn.indexOf(IMetric.NSDELIM, lastIndex+1);
}
index = fqn.indexOf(IMetric.NADELIM);
if(sequence==1) {
agent = fqn.substring(lastIndex+1, index);
} else {
namespace.add(fqn.substring(lastIndex+1, index));
}
metricName = fqn.substring(index+1);
return (catalog==null ? ICEMetricCatalog.getInstance() : catalog).get(host, agent, metricName, type, namespace.toArray(new CharSequence[namespace.size()]));
}
// public static void main(String[] args) {
// log("FQN Parse Test");
// log(build("myhost/myagent:cpu1", MetricType.LONG));
// log(build("myhost/myagent/foo/bar/baz/cpu:cpu1", MetricType.LONG));
//
// }
//
//
// public static void log(Object msg) {
// System.out.println(msg);
// }
/**
* {@inheritDoc}
* @see org.helios.apmrouter.metric.catalog.IMetricCatalog#dispose()
* <p><b>No Op</b>
*/
@Override
public void dispose() {
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.metric.catalog.IMetricCatalog#setToken(org.helios.apmrouter.metric.IMetric)
*/
@Override
public long setToken(IMetric metric) {
return actualCatalog.setToken(metric);
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.metric.catalog.IMetricCatalog#setToken(java.lang.CharSequence, long)
*/
@Override
public void setToken(CharSequence metricFqn, long token) {
actualCatalog.setToken(metricFqn, token);
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.metric.catalog.IMetricCatalog#setToken(org.helios.apmrouter.metric.catalog.IDelegateMetric, long)
*/
@Override
public long setToken(IDelegateMetric metricId, long token) {
return actualCatalog.setToken(metricId, token);
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.metric.catalog.IMetricCatalog#resetTokens()
*/
public void resetTokens() {
actualCatalog.resetTokens();
}
}
// =============================
// Some other parsers I tested.
// The one above was the fastest
// =============================
//public static String builds(String fqn, MetricType type) {
// nvl(fqn, "FQN");
// nvl(type, "MetricType");
// String host=null, agent=null, metricName=null;
// List<CharSequence> namespace = new ArrayList<CharSequence>();
// int sequence = 0;
// int lastIndex = 0;
// int index = fqn.indexOf(IMetric.NSDELIM, 0);
// while(index!=-1) {
// if(sequence==0) host = fqn.substring(lastIndex, index);
// else if(sequence==1) agent = fqn.substring(lastIndex+1, index);
// else namespace.add(fqn.substring(lastIndex+1, index));
// switch(sequence) {
// case 0:
// host = fqn.substring(lastIndex, index);
// sequence++;
// break;
// case 1:
// agent = fqn.substring(lastIndex+1, index);
// sequence++;
// break;
// default:
// namespace.add(fqn.substring(lastIndex+1, index));
// break;
// }
// lastIndex = index;
// index = fqn.indexOf(IMetric.NSDELIM, lastIndex+1);
// }
//
// index = fqn.indexOf(IMetric.NADELIM);
// if(sequence==1) {
// agent = fqn.substring(lastIndex+1, index);
// } else {
// namespace.add(fqn.substring(lastIndex+1, index));
// }
//
// metricName = fqn.substring(index+1);
//
// return log(host, agent, metricName, namespace.toArray(new CharSequence[namespace.size()])); //ICEMetricCatalog.getInstance().get(host, agent, metricName, type, namespace.toArray(new CharSequence[namespace.size()]));
//}
//
///** FQN split pattern */
//public static final Pattern FQN_PATTERN = Pattern.compile("/|:");
//
//public static String buildr(String fqn, MetricType type) {
// nvl(fqn, "FQN");
// nvl(type, "MetricType");
// String[] frags = FQN_PATTERN.split(fqn);
// String host=frags[0], agent=frags[1], metricName=frags[frags.length-1];
// if(frags.length>3) {
// List<CharSequence> namespace = new ArrayList<CharSequence>();
// for(int i = 2; i < frags.length-1; i++) {
// namespace.add(frags[i]);
// }
//
// return log(host, agent, metricName, namespace.toArray(new CharSequence[namespace.size()])); //ICEMetricCatalog.getInstance().get(host, agent, metricName, type, namespace.toArray(new CharSequence[namespace.size()]));
// }
// log(host, agent, metricName);
// return log(host, agent, metricName); //ICEMetricCatalog.getInstance().get(host, agent, metricName, type);
//}
//
//public static void main(String[] args) {
// log("FQN Parse Test");
//
// int LOOPS = 2000000;
// for(int i = 0; i < LOOPS; i++) {
// if(buildp("myhost/myagent/foo/bar/baz/cpu:cpu1", MetricType.LONG)==null) throw new RuntimeException();
// }
// SystemClock.startTimer();
// for(int i = 0; i < LOOPS; i++) {
// if(buildp("myhost/myagent/foo/bar/baz/cpu:cpu1", MetricType.LONG)==null) throw new RuntimeException();
// }
// ElapsedTime et = SystemClock.endTimer();
// log("Parser Time:" + et);
// for(int i = 0; i < LOOPS; i++) {
// if(builds("myhost/myagent/foo/bar/baz/cpu:cpu1", MetricType.LONG)==null) throw new RuntimeException();
// }
//
// SystemClock.startTimer();
// for(int i = 0; i < LOOPS; i++) {
// if(builds("myhost/myagent/foo/bar/baz/cpu:cpu1", MetricType.LONG)==null) throw new RuntimeException();
// }
// et = SystemClock.endTimer();
// log("Switcher Time:" + et);
// for(int i = 0; i < LOOPS; i++) {
// if(buildr("myhost/myagent/foo/bar/baz/cpu:cpu1", MetricType.LONG)==null) throw new RuntimeException();
// }
// SystemClock.startTimer();
// for(int i = 0; i < LOOPS; i++) {
// if(buildr("myhost/myagent/foo/bar/baz/cpu:cpu1", MetricType.LONG)==null) throw new RuntimeException();
// }
// et = SystemClock.endTimer();
// log("Splitter Time:" + et);
//
//// log(buildp("myhost/myagent:cpu1", MetricType.LONG));
//// log(buildp("myhost/myagent/foo/bar/baz/cpu:cpu1", MetricType.LONG));
//}
//
//public static void log(Object msg) {
// System.out.println(msg);
//}
//public static String log(String host, String agent, String name, CharSequence...ns) {
// StringBuilder b = new StringBuilder("[(").append(host).append("-").append(agent).append(")");
// if(ns.length>0) {
// for(int i = 0; i < ns.length; i++) {
// b.append("-").append(ns[i]);
// }
// }
// b.append("]:").append(name);
// return b.toString();
//}
//