be.fgov.kszbcss.rhq.websphere.support.measurement.PMIMeasurementHandler.java Source code

Java tutorial

Introduction

Here is the source code for be.fgov.kszbcss.rhq.websphere.support.measurement.PMIMeasurementHandler.java

Source

/*
 * RHQ WebSphere Plug-in
 * Copyright (C) 2012-2014 Crossroads Bank for Social Security
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, version 2, as
 * published by the Free Software Foundation, and/or the GNU Lesser
 * General Public License, version 2.1, also 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 and the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License
 * and the GNU Lesser General Public License along with this program;
 * if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package be.fgov.kszbcss.rhq.websphere.support.measurement;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.domain.measurement.MeasurementDataNumeric;
import org.rhq.core.domain.measurement.MeasurementReport;
import org.rhq.core.domain.measurement.MeasurementScheduleRequest;

import be.fgov.kszbcss.rhq.websphere.process.WebSphereServer;

import com.ibm.websphere.pmi.PmiDataInfo;
import com.ibm.websphere.pmi.PmiModuleConfig;
import com.ibm.websphere.pmi.stat.StatDescriptor;
import com.ibm.websphere.pmi.stat.WSAverageStatistic;
import com.ibm.websphere.pmi.stat.WSCountStatistic;
import com.ibm.websphere.pmi.stat.WSRangeStatistic;
import com.ibm.websphere.pmi.stat.WSStatistic;
import com.ibm.websphere.pmi.stat.WSStats;

public class PMIMeasurementHandler implements MeasurementGroupHandler {
    private static long STAT_ENABLE_ATTEMPT_INTERVAL = 12 * 3600 * 1000;

    private static final Log log = LogFactory.getLog(PMIMeasurementHandler.class);

    private final PMIModuleSelector moduleSelector;
    private StatDescriptor descriptor;
    private final Map<String, WSAverageStatistic> lastStats = new HashMap<String, WSAverageStatistic>();

    /**
     * Records failed attempts to enable a statistic. The key is the dataId of the statistic, and
     * the value is the timestamp of the last attempt to enable the statistic.
     */
    private final Map<Integer, Long> failedAttemptsToEnableStat = new HashMap<Integer, Long>();

    public PMIMeasurementHandler(String... path) {
        this(new StaticPMIModuleSelector(path));
    }

    public PMIMeasurementHandler(PMIModuleSelector moduleSelector) {
        this.moduleSelector = moduleSelector;
    }

    private void purgeFailedAttemptsToEnableStat() {
        long currentTime = System.currentTimeMillis();
        for (Iterator<Long> it = failedAttemptsToEnableStat.values().iterator(); it.hasNext();) {
            // Purge every 12h
            if (currentTime > it.next() + STAT_ENABLE_ATTEMPT_INTERVAL) {
                it.remove();
            }
        }
    }

    private StatDescriptor loadDescriptor() {
        try {
            return new StatDescriptor(moduleSelector.getPath());
        } catch (Exception ex) {
            log.error("Failed to determine PMI module path", ex);
            return null;
        }
    }

    public void getValues(WebSphereServer server, MeasurementReport report,
            Map<String, MeasurementScheduleRequest> requests) {
        purgeFailedAttemptsToEnableStat();

        StatDescriptor descriptor;
        WSStats stats;
        boolean isFreshDescriptor;
        synchronized (this) {
            descriptor = this.descriptor;
            if (descriptor == null) {
                log.debug("Initial load of descriptor");
                descriptor = loadDescriptor();
                if (descriptor == null) {
                    return;
                }
                this.descriptor = descriptor;
                isFreshDescriptor = true;
            } else {
                isFreshDescriptor = false;
            }
        }
        while (true) {
            if (log.isDebugEnabled()) {
                log.debug("Attempting to get stats for " + descriptor);
            }
            try {
                stats = server.getWSStats(descriptor);
            } catch (Exception ex) {
                log.error("Failed to get statistics object", ex);
                return;
            }
            if (stats == null) {
                if (isFreshDescriptor) {
                    log.debug("Stats not available");
                    return;
                } else {
                    synchronized (this) {
                        log.debug("Refreshing descriptor");
                        descriptor = loadDescriptor();
                        if (Arrays.equals(this.descriptor.getPath(), descriptor.getPath())) {
                            log.debug("Descriptor didn't change; abandon.");
                            return;
                        } else {
                            log.debug("Descriptor changed; retrying...");
                            this.descriptor = descriptor;
                            isFreshDescriptor = true;
                        }
                    }
                }
            } else {
                break;
            }
        }

        PmiModuleConfig pmiModuleConfig;
        try {
            pmiModuleConfig = server.getPmiModuleConfig(stats);
        } catch (Exception ex) {
            log.error("Failed to get PMI module configuration", ex);
            return;
        }
        Set<Integer> statisticsToEnable = null;

        for (Map.Entry<String, MeasurementScheduleRequest> entry : requests.entrySet()) {
            MeasurementScheduleRequest request = entry.getValue();
            String name = request.getName();
            String statisticName = entry.getKey();
            if (log.isDebugEnabled()) {
                log.debug("Starting to get value for " + name);
            }
            int dataId = pmiModuleConfig.getDataId(statisticName);
            if (dataId == -1) {
                if (log.isDebugEnabled()) {
                    log.debug("Could not find statistic with name " + statisticName + " as is (stats type "
                            + stats.getStatsType()
                            + ") in the PMI module configuration; attempting to find a matching prefixed name among the existing statistics");
                }
                // WebSphere 8.5 prefixes some stat names with a given string within a given type;
                // most of the time, this prefix will be the shortened type name (i.e. QueueStats),
                // but sometimes it isn't, which makes generalization impossible. The prefixed format being
                // a stable <Prefix>.<StatisticName>, we will seek for a ".statisticName" in all
                // available PmiDataInfo in the considered pmiConfigModule.
                for (PmiDataInfo pdi : pmiModuleConfig.listAllData()) {
                    if (pdi.getName().endsWith("." + statisticName)) {
                        dataId = pdi.getId();
                        break;
                    }
                }
            }
            if (dataId == -1) {
                log.error("Could not find statistic with name " + statisticName + " (stats type "
                        + stats.getStatsType() + ") in the PMI module configuration");
                continue;
            }
            // For some WSStats objects, the statistic names don't match the names used by PMI.
            // Therefore we translate all names to data IDs. This also makes it easier to
            // automatically enable the statistics if necessary.
            WSStatistic statistic = stats.getStatistic(dataId);
            if (statistic == null) {
                if (failedAttemptsToEnableStat.containsKey(dataId)) {
                    if (log.isDebugEnabled()) {
                        Date nextAttempt = new Date(
                                failedAttemptsToEnableStat.get(dataId) + STAT_ENABLE_ATTEMPT_INTERVAL);
                        log.debug("Statistic with name " + statisticName + " (ID " + dataId
                                + ") not available, but a previous attempt to enable the statistic failed; will retry at "
                                + SimpleDateFormat.getInstance().format(nextAttempt));
                    }
                } else {
                    log.info("Statistic with name " + statisticName + " (ID " + dataId
                            + ") not available; will attempt to enable it");
                    if (statisticsToEnable == null) {
                        statisticsToEnable = new HashSet<Integer>();
                    }
                    statisticsToEnable.add(dataId);
                }
                continue;
            }
            if (log.isDebugEnabled()) {
                log.debug("Loaded Statistic with name " + statisticName + " (ID " + dataId + ") and type "
                        + statistic.getClass().getName());
            }
            double value;
            if (statistic instanceof WSCountStatistic) {
                value = ((WSCountStatistic) statistic).getCount();
            } else if (statistic instanceof WSRangeStatistic) {
                value = getValue(statisticName, (WSRangeStatistic) statistic);
            } else if (statistic instanceof WSAverageStatistic) {
                WSAverageStatistic currentStatistic = (WSAverageStatistic) statistic;
                WSAverageStatistic prevStatistic = lastStats.get(statisticName);
                lastStats.put(statisticName, currentStatistic);
                if (log.isDebugEnabled()) {
                    if (prevStatistic == null) {
                        log.debug("Previous value: <not available>");
                    } else {
                        log.debug("Previous value: " + dumpStatistic(prevStatistic));
                    }
                    log.debug("Current value: " + dumpStatistic(currentStatistic));
                }
                // We need to detect a statistic reset in a reliable way. Checking delta(count) > 0 is not sufficient.
                // In fact, delta(count) < 0 implies that the statistic has been reset, but it can happen that
                // after a statistic reset, delta(count) > 0. In this case, delta(total) may be negative, so that
                // we end up with a negative measurement. Therefore we also compare the startTime values to check
                // that the two statistics have the same baseline.
                if (prevStatistic == null || currentStatistic.getStartTime() != prevStatistic.getStartTime()) {
                    continue;
                } else {
                    long countDelta = currentStatistic.getCount() - prevStatistic.getCount();
                    if (countDelta > 0) {
                        value = ((double) (currentStatistic.getTotal() - prevStatistic.getTotal()))
                                / ((double) countDelta);
                    } else {
                        continue;
                    }
                }
            } else {
                log.error("Unknown or unsupported statistic type " + statistic.getClass().getName());
                continue;
            }
            if (log.isDebugEnabled()) {
                log.debug("Adding measurement for " + name + "; value=" + value);
            }
            report.addData(new MeasurementDataNumeric(request, value));
        }
        if (statisticsToEnable != null) {
            statisticsToEnable.removeAll(server.enableStatistics(descriptor, statisticsToEnable));
            if (!statisticsToEnable.isEmpty()) {
                Long currentTime = System.currentTimeMillis();
                for (Integer dataId : statisticsToEnable) {
                    failedAttemptsToEnableStat.put(dataId, currentTime);
                }
                if (log.isDebugEnabled()) {
                    log.debug("failedAttemptsToEnable = " + failedAttemptsToEnableStat);
                }
            }
        }
    }

    private static String dumpStatistic(WSAverageStatistic statistic) {
        return "total=" + statistic.getTotal() + ", min=" + statistic.getMin() + ", max=" + statistic.getMax()
                + ", startTime=" + statistic.getStartTime() + ", count=" + statistic.getCount();
    }

    protected double getValue(String name, WSRangeStatistic statistic) {
        return statistic.getCurrent();
    }

    @Override
    public String toString() {
        return getClass().getName() + "[" + moduleSelector + "]";
    }
}