/*
* NOTE: This copyright does *not* cover user programs that use HQ
* program services by normal system calls through the application
* program interfaces provided as part of the Hyperic Plug-in Development
* Kit or the Hyperic Client Development Kit - this is merely considered
* normal use of the program, and does *not* fall under the heading of
* "derived work".
*
* Copyright (C) [2004, 2005, 2006], Hyperic, Inc.
* This file is part of HQ.
*
* HQ is free software; you can redistribute it and/or modify
* it under the terms version 2 of the GNU General Public License as
* published by the Free Software Foundation. This program 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
package org.hyperic.hq.product;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.hyperic.util.PluginLoader;
import org.hyperic.util.config.ConfigResponse;
import org.hyperic.util.config.ConfigSchema;
public class MeasurementPluginManager extends PluginManager implements MeasurementValueGetter {
private Map metricCache = Collections.synchronizedMap(new HashMap());
private boolean debugRateMetrics;
private LogTrackPluginManager ltpm;
public MeasurementPluginManager() {
super();
}
public MeasurementPluginManager(Properties props) {
super(props);
//only doing this because trace() still logs even
//at debug level???
this.debugRateMetrics =
"true".equals("debugRateMetrics");
}
private void registerProxy(String name, GenericPlugin plugin)
throws PluginException {
registerPlugin(name, plugin);
setPluginInfo(name, new PluginInfo(name, plugin.getPluginVersion()));
}
public void init(PluginManager parent) throws PluginException {
super.init(parent);
this.ltpm =
((ProductPluginManager)parent).getLogTrackPluginManager();
//registry "proxy" plugins. these plugins handle metrics
//based on the Metric domain name, rather than calling into
//the plugin for resource type associated with the metric.
registerProxy(ExecutableProcess.DOMAIN,
new ExecutableMeasurementPlugin());
registerProxy(SNMPMeasurementPlugin.DOMAIN,
new SNMPMeasurementPlugin());
registerProxy(SigarMeasurementPlugin.DOMAIN,
new SigarMeasurementPlugin());
registerProxy(SigarMeasurementPlugin.PTQL_DOMAIN,
new SigarMeasurementPlugin());
registerProxy(Win32MeasurementPlugin.DOMAIN,
new Win32MeasurementPlugin());
}
public String getName() {
return ProductPlugin.TYPE_MEASUREMENT;
}
//from ProductProperties.getProperties()
private boolean isPluginTypeSupported(String type) {
return "true".equals(getProperty("plugin." + type + ".supported"));
}
public ConfigSchema getConfigSchema(String plugin,
TypeInfo info,
ConfigResponse config)
throws PluginNotFoundException {
ConfigSchema schema = super.getConfigSchema(plugin, info, config);
//attach log/config track enable options to measurement config schema
//if the given type supports it.
ProductPluginManager ppm = (ProductPluginManager)getParent();
if (isPluginTypeSupported(ProductPlugin.TYPE_LOG_TRACK)) {
mergeConfigSchema(ppm.getLogTrackPluginManager(),
schema, info, config);
}
if (isPluginTypeSupported(ProductPlugin.TYPE_CONFIG_TRACK)) {
mergeConfigSchema(ppm.getConfigTrackPluginManager(),
schema, info, config);
}
return schema;
}
public MetricValue getValue(String template)
throws PluginException, PluginNotFoundException,
MetricNotFoundException, MetricUnreachableException
{
//split plugin:template...
int ix = template.indexOf(":");
String plugin = template.substring(0, ix);
String metric = template.substring(ix+1, template.length());
return getValue(plugin, metric);
}
public MetricValue getValue(String name, String metric)
throws PluginException, PluginNotFoundException,
MetricNotFoundException, MetricUnreachableException
{
try {
return getValue(name, Metric.parse(metric));
} catch (PluginNotFoundException e) {
if (e.getMessage() == null) {
String msg =
getName() + " plugin name=" + name + " not found";
throw new PluginNotFoundException(msg);
}
else {
throw e;
}
}
}
private MetricValue getPluginValue(String name, Metric metric)
throws PluginException,
MetricNotFoundException,
MetricUnreachableException {
//dont use getPlugin() here to prevent
//PluginNotFoundException where one may have a
//foo-plugin.xml that uses all proxy metrics
//we don't have to require foo-plugin.xml to
//be deployed on the agent
MeasurementPlugin plugin =
(MeasurementPlugin)this.plugins.get(name);
String domain = metric.getDomainName();
//check if there is a proxy registered for the domain
MeasurementPlugin invoker =
(MeasurementPlugin)this.plugins.get(domain);
if (invoker == null) {
if (plugin == null) {
//last chance, might be found as a service extension
plugin = (MeasurementPlugin)getPlugin(name);
}
invoker = plugin; //no proxy
}
PluginLoader.setClassLoader(invoker);
try {
return invoker.getValue(metric);
} finally {
PluginLoader.resetClassLoader(invoker);
}
}
public MetricValue getValue(String name, Metric metric)
throws PluginException, PluginNotFoundException,
MetricNotFoundException, MetricUnreachableException
{
String metricString = metric.toString();
try {
int idx;
if ((idx = metricString.indexOf(MeasurementInfo.RATE_KEY)) != -1) {
// Rate based metric
MetricValue oldVal, newVal, rateVal;
double interval;
// Re-write the Metric to not include the rate.
String newDsn = metricString.substring(0, idx);
String rate =
metricString.substring(idx +
MeasurementInfo.RATE_KEY.length() +
1);
idx = rate.indexOf(':');
if (idx != -1) {
//connection properties were appended after the __RATE__
newDsn += rate.substring(idx);
rate = rate.substring(0, idx);
}
if (rate.equals(MeasurementInfo.NO_RATE)) {
// Don't collect rates for this metric.
throw new MetricNotFoundException("Rate calculation " +
"disabled for this " +
"metric");
} else if (rate.equals(MeasurementInfo.SEC_RATE)) {
interval = 1000;
} else if (rate.equals(MeasurementInfo.MIN_RATE)) {
interval = 1000 * 60;
} else if (rate.equals(MeasurementInfo.HOUR_RATE)) {
interval = 1000 * 60 * 60;
} else {
// Invalid rate interval
throw new MetricNotFoundException("Invalid rate of " +
rate + " for Metric=" +
newDsn);
}
newVal = getPluginValue(name, Metric.parse(newDsn));
oldVal = (MetricValue)metricCache.get(metricString);
if (oldVal == null) {
metricCache.put(metricString, newVal);
if (this.debugRateMetrics) {
String msg =
"First time collecting rate " +
"based metric '" + newDsn + "'";
log.trace(msg);
}
return new MetricValue(Double.NaN);
}
// XXX: Maybe we should wait for two metric collections to
// avoid spikes in the first rate calculation.
metricCache.put(metricString, newVal);
double oldValue, newValue;
double oldTime, newTime;
oldValue = oldVal.getValue();
newValue = newVal.getValue();
// Check if new value is < old value in case a counter
// rolls over. We ignore those measurements
if (newValue < oldValue) {
if (this.debugRateMetrics) {
String msg =
"Rate based metric '" + newDsn +
"'counter rolled over";
log.trace(msg);
}
return new MetricValue(Double.NaN);
}
oldTime = oldVal.getTimestamp();
newTime = newVal.getTimestamp();
rateVal = new MetricValue((newValue - oldValue) /
((newTime - oldTime)/interval),
System.currentTimeMillis());
return rateVal;
} else {
// Else, normal metric
return getPluginValue(name, metric);
}
} catch(NoClassDefFoundError e) {
throw new PluginException(classNotFoundMessage(e), e);
}
}
private String translate(MeasurementPlugin plugin,
String template,
ConfigResponse config) {
PluginLoader.setClassLoader(plugin);
try {
return plugin.translate(template, config);
} finally {
PluginLoader.resetClassLoader(plugin);
}
}
public String translate(String template, ConfigResponse config)
throws PluginNotFoundException
{
int ix = template.indexOf(":");
String type = template.substring(0, ix);
MeasurementPlugin plugin;
//if the domain has a proxy registered,
//run the proxy plugin's translate method.
String name = template.substring(type.length()+1);
ix = name.indexOf(":");
if (ix != -1) {
name = name.substring(0, ix);
plugin = (MeasurementPlugin)this.plugins.get(name);
if (plugin != null) {
template = translate(plugin, template, config);
}
}
plugin = (MeasurementPlugin)getPlugin(type);
template = translate(plugin, template, config);
return template;
}
public MeasurementInfo[] getMeasurements(TypeInfo info)
throws PluginNotFoundException {
String name = info.getName();
MeasurementPlugin plugin = (MeasurementPlugin)getPlugin(name);
return plugin.getMeasurements(info);
}
public String getHelp(TypeInfo info, Map props)
throws PluginNotFoundException {
String name = info.getName();
MeasurementPlugin plugin = (MeasurementPlugin)getPlugin(name);
ConfigSchema schema =
getConfigSchema(plugin.getName(), info,
new ConfigResponse());
String help = plugin.getHelp(info, props);
String trackHelp =
TrackEventPluginManager.getGenericHelp(this.ltpm, schema, info);
if (trackHelp != null) {
trackHelp =
plugin.replaceHelpProperties(trackHelp, info, props);
}
if (help == null) {
return trackHelp;
}
else if (trackHelp != null) {
return help + "<hr>" + trackHelp;
}
else {
return help;
}
}
public void reportEvent(Metric metric,
long time, int level,
String source, String message) {
LogTrackPlugin plugin = null;
if (metric.getId() != null) {
plugin = ltpm.getLogTrackPlugin(metric.getId());
}
if (plugin != null) {
if (message == null) {
message = "No Message";
}
plugin.reportEvent(time, level, source, message);
}
}
}