org.hyperic.hq.product.Collector.java Source code

Java tutorial

Introduction

Here is the source code for org.hyperic.hq.product.Collector.java

Source

/*
 * 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.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.util.PluginLoader;
import org.hyperic.util.config.ConfigResponse;
import org.hyperic.util.config.ConfigSchema;

public abstract class Collector implements Runnable {

    public static final String PROP_HOSTNAME = "hostname";

    public static final String PROP_PORT = "port";

    public static final String PROP_PROTOCOL = "protocol";

    public static final String PROP_PATH = "path";

    public static final String PROP_SSL = "ssl";

    public static final String PROP_SSL_PROTOCOL = "sslprotocol";

    public static final String PROP_USERNAME = "user";

    public static final String PROP_PASSWORD = "pass";

    public static final String PROP_REALM = "realm";

    public static final String PROP_FOLLOW = "follow";

    public static final String PROP_METHOD = "method";

    public static final String PROP_SSLPORT = PROP_SSL + PROP_PORT;

    public static final String METHOD_HEAD = "HEAD";

    public static final String METHOD_GET = "GET";

    public static final String PROTOCOL_HTTP = "http";

    public static final String PROTOCOL_HTTPS = "https";

    public static final String PROTOCOL_FTP = "ftp";

    public static final String PROTOCOL_SOCKET = "socket";

    public static final String DEFAULT_HOSTNAME = "localhost";

    public static final String DEFAULT_FTP_PORT = "21";

    public static final String DEFAULT_HTTP_PORT = "80";

    public static final String DEFAULT_HTTPS_PORT = "443";

    public static final String PROP_TIMEOUT = "timeout";

    public static final String ATTR_RESPONSE_TIME = "ResponseTime";

    public static final String ATTR_RESPONSE_CODE = "ResponseCode";

    public static final String LISTEN_PORTS = "listen.ports";

    public static final String GUID = "GUID";

    public static final String MAC = "Mac";

    static Log log = LogFactory.getLog(Collector.class.getName());

    static Map containers = new HashMap();

    private GenericPlugin plugin;
    private Properties props;

    private boolean isRunning = false;
    private int timeout = -1;
    private long startTime, endTime;
    //use a ref to Metric: ScheduleThread.unscheduleMetric unsets interval
    private Metric intervalMetric;
    private long lastCollection = -1;
    private static Map compatAliases = new HashMap();

    static {
        //maintain compat w/ old templates
        String[][] aliases = {
                //NetworkServer IP
                { "RequestTime", ATTR_RESPONSE_TIME },
                //Script, Nagios Plugin
                { "ExecTime", ATTR_RESPONSE_TIME }, { "ReturnCode", ATTR_RESPONSE_CODE },
                { "Arg", ExecutableProcess.PROP_FILE }, { "prefix", ExecutableProcess.PROP_EXEC },
                { "Params", ExecutableProcess.PROP_ARGS },
                //other
                { "availability", Metric.ATTR_AVAIL }, };
        for (int i = 0; i < aliases.length; i++) {
            compatAliases.put(aliases[i][0], aliases[i][1]);
        }
    }

    CollectorResult result = new CollectorResult();

    protected void init() throws PluginException {
        setSource(getPlugin().getName());
    }

    public abstract void collect();

    /**
     * Initialize a Collector instance for use outside of MeasurementPlugin.
     * Collectors are generally used for metric collection, but can also be
     * used in some cases for inventory property discovery and/or control.
     * @param plugin A ServerDetector or ControlPlugin
     * @param config Resource configuration properties
     * @throws PluginException
     */
    public void init(GenericPlugin plugin, ConfigResponse config) throws PluginException {

        this.plugin = plugin;
        setProperties(config.toProperties());
        init();
    }

    /**
     * Initialize and collect values for use outside of MeasurementPlugin.
     * This method is useful for inventory property discovery.
     * @param plugin A ServerDetector or ControlPlugin
     * @param config
     * @return Resource configuration properties
     * @throws PluginException
     */
    public Map getValues(GenericPlugin plugin, ConfigResponse config) throws PluginException {

        init(plugin, config);
        collect();
        return getResult().values;
    }

    public int getTimeout() {
        if (this.timeout == -1) {
            this.timeout = getIntegerProperty(getPropTimeout(), getDefaultTimeout());
        }
        return this.timeout;
    }

    public int getTimeoutMillis() {
        return getTimeout() * 1000;
    }

    protected int getDefaultTimeout() {
        return 30; //30 seconds
    }

    protected String getPropTimeout() {
        return PROP_TIMEOUT;
    }

    protected int getIntegerProperty(String key, int defVal) {
        String val = getProperties().getProperty(key);
        if (val == null) {
            if (defVal == -1) {
                String msg = "Missing " + key + " property";
                throw new IllegalArgumentException(msg);
            }
            return defVal;
        }
        try {
            return Integer.parseInt(val);
        } catch (NumberFormatException e) {
            String msg = "Invalid value (" + val + ")" + " for " + key;
            throw new IllegalArgumentException(msg);
        }
    }

    protected String getCollectorProperty(String key, String defVal) {
        return this.props.getProperty(key, defVal);
    }

    static Object getCompatValue(Map map, String key) {
        Object val = map.get(key);
        if (val == null) {
            Object alias = compatAliases.get(key);
            if (alias == null) {
                alias = key.toLowerCase();
            }
            if (alias != null) {
                val = map.get(alias);
            }
        }
        return val;
    }

    protected String getCollectorProperty(String key) {
        return (String) getCompatValue(this.props, key);
    }

    protected void setProperties(Properties props) {
        this.props = props;
    }

    protected Properties getProperties() {
        return this.props;
    }

    protected GenericPlugin getPlugin() {
        return this.plugin;
    }

    protected void setSource(String value) {
        this.result.source = value;
    }

    protected String getSource() {
        return this.result.source;
    }

    protected void setLogLevel(int value) {
        this.result.level = value;
    }

    protected int getLogLevel() {
        return this.result.level;
    }

    protected void setMessage(String value) {
        this.result.message = value;
    }

    protected String getMessage() {
        return this.result.message;
    }

    private String composeMessage(String msg, Throwable t) {
        return msg + ": " + t.getMessage();
    }

    protected void setMessage(String msg, Throwable t) {
        setMessage(composeMessage(msg, t));
    }

    protected void setErrorMessage(String msg) {
        setLogLevel(LogTrackPlugin.LOGLEVEL_ERROR);
        setMessage(msg);
    }

    protected void setWarningMessage(String msg) {
        setLogLevel(LogTrackPlugin.LOGLEVEL_WARN);
        setMessage(msg);
    }

    protected void setInfoMessage(String msg) {
        setLogLevel(LogTrackPlugin.LOGLEVEL_INFO);
        setMessage(msg);
    }

    protected void setDebugMessage(String msg) {
        setLogLevel(LogTrackPlugin.LOGLEVEL_DEBUG);
        setMessage(msg);
    }

    protected void setErrorMessage(String msg, Throwable t) {
        setErrorMessage(composeMessage(msg, t));
    }

    protected void setWarningMessage(String msg, Throwable t) {
        setWarningMessage(composeMessage(msg, t));
    }

    protected void setInfoMessage(String msg, Throwable t) {
        setInfoMessage(composeMessage(msg, t));
    }

    protected void setDebugMessage(String msg, Throwable t) {
        setDebugMessage(composeMessage(msg, t));
    }

    protected void setValue(String key, String val) {
        this.result.setValue(key, val);
    }

    protected void addValues(Map values) {
        this.result.addValues(values);
    }

    protected CollectorResult getResult() {
        return this.result;
    }

    protected void setValue(String key, double val) {
        this.result.setValue(key, val);
    }

    protected void setAvailability(double val) {
        setValue(Metric.ATTR_AVAIL, val);
    }

    protected void setAvailability(boolean val) {
        setAvailability(val ? Metric.AVAIL_UP : Metric.AVAIL_DOWN);
    }

    protected void setResponseCode(int code) {
        setValue(ATTR_RESPONSE_CODE, code);
    }

    protected void setResponseTime(double value) {
        setValue(ATTR_RESPONSE_TIME, value);
    }

    protected void startTime() {
        this.startTime = System.currentTimeMillis();
    }

    protected void endTime() {
        this.endTime = System.currentTimeMillis();
    }

    String mapToString(Map map) {
        Map props = new HashMap();
        for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
            Map.Entry entry = (Map.Entry) it.next();
            String key = (String) entry.getKey();
            String val = (String) entry.getValue();
            if (ConfigSchema.isSecret(key)) {
                val = Metric.mask(val);
            }
            props.put(key, val);
        }
        return props.toString();
    }

    public String toString() {
        return mapToString(this.props);
    }

    static class PluginContainer {
        String name;

        Map collectors = Collections.synchronizedMap(new HashMap());
        Map results = Collections.synchronizedMap(new HashMap());

        static PluginContainer get(GenericPlugin plugin) {
            String name = plugin.getName();
            synchronized (containers) {
                PluginContainer container = (PluginContainer) containers.get(name);
                if (container == null) {
                    container = new PluginContainer();
                    container.name = name;
                    containers.put(name, container);
                }
                return container;
            }
        }

        static void setResult(Collector collector) {
            CollectorResult result = new CollectorResult(collector);
            if (log.isDebugEnabled()) {
                log.debug("name=" + collector.plugin.getName() + ", " + "thread=" + Thread.currentThread().getName()
                        + ", result=" + result);
            }
            get(collector.plugin).results.put(collector.props, result);
        }
    }

    public MetricValue getValue(Metric metric, CollectorResult result) {
        return result.getMetricValue(metric.getAttributeName());
    }

    private long getInterval() {
        if (this.intervalMetric == null) {
            return -1;
        } else {
            return this.intervalMetric.getInterval();
        }
    }

    private void setInterval(Metric metric) {
        this.intervalMetric = metric;
    }

    //interval is used to make collection to happen 1 minute before
    //the Availability metric is scheduled to be collected
    protected void setInterval(MeasurementPlugin plugin, Metric metric) {
        if (!isPoolable()) {
            return; //XXX apply only to netservices and exec for now.
        }
        long itv = metric.getInterval();
        boolean isAvail = metric.getAttributeName().equals(Metric.ATTR_AVAIL);

        if ((isAvail || (getInterval() == -1)) && (itv > 0) && (getInterval() != itv)) {
            setInterval(metric);
            if (log.isDebugEnabled()) {
                log.debug("Set itv=" + (getInterval() / MINUTE) + "min for " + plugin.getName() + " collector: "
                        + this);
            }
        }
    }

    public static MetricValue getValue(MeasurementPlugin plugin, Metric metric)
            throws PluginException, MetricNotFoundException, MetricUnreachableException {

        CollectorResult result;
        Collector collector;
        Properties props = plugin.getCollectorProperties(metric);

        PluginContainer container = PluginContainer.get(plugin);

        collector = (Collector) container.collectors.get(props);
        result = (CollectorResult) container.results.get(props);

        if (result != null) {
            boolean isAvail = metric.getAttributeName().equals(Metric.ATTR_AVAIL);
            result.collected = true;
            if (!result.reported && (result.level != -1)) {
                plugin.getManager().reportEvent(metric, result.getTimeStamp(), result.getLevel(),
                        result.getSource(), result.getMessage());
                result.reported = true;
            }

            MetricValue value;

            boolean setClassLoader = PluginLoader.setClassLoader(collector);

            try {
                value = collector.getValue(metric, result);
                if (isAvail) {
                    collector.lastCollection = value.getTimestamp();
                }
            } finally {
                collector.setInterval(plugin, metric); //sync
                if (setClassLoader) {
                    PluginLoader.resetClassLoader(collector);
                }
            }

            if (value == null) {
                throw new MetricNotFoundException(metric.toString());
            }

            return value;
        }

        if (collector == null) {
            collector = plugin.getNewCollector();
            collector.plugin = plugin;

            boolean setClassLoader = PluginLoader.setClassLoader(collector);

            try {
                collector.setProperties(props);
                collector.init();
                collector.setInterval(plugin, metric);
            } finally {
                if (setClassLoader) {
                    PluginLoader.resetClassLoader(collector);
                }
            }

            if (log.isDebugEnabled()) {
                log.debug("Adding " + plugin.getName() + " collector: " + collector);
            }

            container.collectors.put(props, collector);
        }

        //just added collector to the thread,
        //next time will pickup metrics.
        return MetricValue.FUTURE;
    }

    public boolean equals(Object obj) {
        if (!(obj instanceof Collector)) {
            return false;
        }

        return ((Collector) obj).props.equals(this.props);
    }

    public int hashCode() {
        return this.props.hashCode();
    }

    public boolean isPoolable() {
        return false;
    }

    public void run() {
        this.isRunning = true;
        this.result.values.clear();
        this.result.level = -1;
        this.startTime = this.endTime = -1;

        boolean setClassLoader = PluginLoader.setClassLoader(this);

        try {
            collect();
        } catch (Throwable t) {
            log.error("Error running " + this.plugin.getName() + " collector: " + t, t);
            setErrorMessage("Error: " + t.getMessage(), t);
            setAvailability(false);
        } finally {
            if (setClassLoader) {
                PluginLoader.resetClassLoader(this);
            }
        }

        if (this.endTime != -1) {
            this.result.timestamp = this.endTime;
            setResponseTime(this.endTime - this.startTime);
        } else {
            this.result.timestamp = System.currentTimeMillis();
        }

        PluginContainer.setResult(this);
        this.isRunning = false;
    }

    protected void parseResults(String message) {
        boolean hasResultValue = false;
        StringTokenizer st = new StringTokenizer(message, "\r\n,");

        while (st.hasMoreTokens()) {
            String attr = st.nextToken();

            int ix = attr.indexOf('=');

            if (ix == -1) {
                if (!hasResultValue) {
                    StringTokenizer st2 = new StringTokenizer(attr, " \t");
                    while (st2.hasMoreTokens()) {
                        String s = st2.nextToken();

                        if (Character.isDigit(s.charAt(0)) && Character.isDigit(s.charAt(s.length() - 1))) {
                            setValue("ResultValue", s);
                            hasResultValue = true; //use the first number
                        }
                    }
                }
                continue;
            }
            String key = attr.substring(0, ix);
            String val = attr.substring(key.length() + 1);

            if (key.equals("Message")) {
                setMessage(val);
                continue;
            }

            setValue(key, val);
        }
    }

    private static final long SECOND = 1 * 1000;
    private static final long MINUTE = 60 * SECOND;
    private static final long HOUR = 60 * MINUTE;
    private static final long DAY = 24 * HOUR;

    public static final String REMOVABLE = "removable";
    public static final String ALLOW_REMOVE = "allow_remove";

    private static String lastRun(long now, long time) {
        long delta = now - time;

        if ((delta / MINUTE) < 1) {
            long seconds = delta / SECOND;
            return seconds + " seconds ago";
        }

        if ((delta / HOUR) < 1) {
            long minutes = delta / MINUTE;
            return minutes + " minutes ago";
        }

        if ((delta / DAY) < 1) {
            long hours = delta / HOUR;
            return hours + " hours ago";
        }

        return "on " + new Date(time);
    }

    private static Collection<Collector> getCollectorsToExecute(PluginContainer container) {
        final boolean debug = log.isDebugEnabled();
        final Collection<Collector> rtn = new ArrayList<Collector>();
        if (debug)
            log.debug("Running " + container.name + " collectors");
        List pluginCollectors;
        //copy so we don't block PluginCollector.get()
        synchronized (container.collectors) {
            Collection values = container.collectors.values();
            pluginCollectors = new ArrayList(values.size());
            pluginCollectors.addAll(values);
        }
        for (int i = 0; i < pluginCollectors.size(); i++) {
            Collector collector = (Collector) pluginCollectors.get(i);
            long interval = collector.getInterval();
            long lastCollection = collector.lastCollection;
            if (collector.isRunning) {
                if (debug)
                    log.debug(collector + " is running: deferring");
                continue;
            }
            CollectorResult result = (CollectorResult) container.results.get(collector.props);
            if ((result != null) && (result.values.size() != 0)) {
                long now = System.currentTimeMillis();
                boolean shouldSkip = true;
                if ((interval != -1) && (lastCollection != -1)) {
                    if ((now - lastCollection) >= interval - MINUTE) {
                        //ScheduleThread should be picking this up
                        //within the next minute, so collect now.
                        shouldSkip = false;
                    } else if ((now - result.timestamp) >= interval) {
                        //not in sync w/ ScheduleThread
                        shouldSkip = false;
                    }
                } else {
                    shouldSkip = !result.collected;
                }
                String msg = null;
                if (debug) {
                    String itv, coll;
                    if (interval == -1) {
                        itv = "unknown";
                    } else {
                        itv = (interval / MINUTE) + "min";
                    }
                    if (lastCollection == -1) {
                        coll = "n/a";
                    } else {
                        coll = lastRun(now, lastCollection);
                    }
                    msg = collector + " ran " + lastRun(now, result.timestamp) + "," + " consumed " + coll + ", "
                            + itv + " itv: ";
                }
                if (shouldSkip) {
                    if (debug)
                        log.debug(msg + "deferring.");
                    continue;
                } else {
                    if (debug)
                        log.debug(msg + "collecting.");
                }
            }
            rtn.add(collector);
        }
        return rtn;
    }

    public static Collection<Collector> getCollectorsToExecute() {
        Collection<Collector> rtn = new ArrayList<Collector>();
        List pluginContainers;
        //copy so we don't block PluginCollector.get()
        synchronized (containers) {
            Collection values = containers.values();
            pluginContainers = new ArrayList(values.size());
            pluginContainers.addAll(values);
        }
        for (int i = 0; i < pluginContainers.size(); i++) {
            PluginContainer container = (PluginContainer) pluginContainers.get(i);
            rtn.addAll(getCollectorsToExecute(container));
        }
        return rtn;
    }

    public static void main(String[] args) throws Exception {
        Collector collector = new ExecutableProcess();
        collector.setProperties(System.getProperties());
        collector.init();
        collector.run();
        System.out.println(collector);
    }
}