com.appdynamics.extensions.couchbase.CouchBaseWrapper.java Source code

Java tutorial

Introduction

Here is the source code for com.appdynamics.extensions.couchbase.CouchBaseWrapper.java

Source

/** 
 * Copyright 2013 AppDynamics 
 * 
 * Licensed under the Apache License, Version 2.0 (the License);
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.appdynamics.extensions.couchbase;

import static com.appdynamics.extensions.couchbase.CouchBaseMonitor.*;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.lang.math.NumberUtils;
import org.apache.log4j.Logger;

import com.appdynamics.extensions.http.Response;
import com.appdynamics.extensions.http.SimpleHttpClient;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;

public class CouchBaseWrapper {
    private static final Logger logger = Logger.getLogger("com.singularity.extensions.CouchBaseWrapper");
    public static final String CLUSTER_STATS_KEY = "Cluster Stats";
    private static final String CLUSTER_NODE_URI = "/pools/default";
    private static final String BUCKET_URI = "/pools/default/buckets";
    private static final String BUCKET_STATS_URI = BUCKET_URI + "/%s/stats";
    private static final String REPLICATIONS_KEY = "replications";
    private static final String BUCKET_EP_METRIC_PREFIX = "ep_";
    private static final String PATH_SEPARATOR = "/";

    /**
     * Gathers Cluster and Node stats. Cluster stats as Map of CLUSTER_STATS_KEY
     * and Map of MetricName, MetricValue and NodeStats as Map of NodeName, Map
     * of MetricName, MetricValue
     * 
     * @param httpClient
     * @return
     */
    public Map<String, Map<String, Double>> gatherClusterNodeMetrics(SimpleHttpClient httpClient) {
        JsonElement clusterNodeResponse = getResponse(httpClient, CLUSTER_NODE_URI);
        Map<String, Map<String, Double>> clusterNodeMetrics = new HashMap<String, Map<String, Double>>();
        if (clusterNodeResponse != null && clusterNodeResponse.isJsonObject()) {
            JsonObject clusterNodeJsonObject = clusterNodeResponse.getAsJsonObject();
            JsonObject storageTotals = clusterNodeJsonObject.getAsJsonObject("storageTotals");
            clusterNodeMetrics = populateClusterMetrics(clusterNodeMetrics, storageTotals);

            JsonArray nodes = (JsonArray) clusterNodeJsonObject.get("nodes");
            clusterNodeMetrics.putAll(populateNodeMetrics(nodes));
        }
        return clusterNodeMetrics;
    }

    /**
     * Gathers Bucket stats as Map of BucketName and Map of MetricName,
     * MetricValue
     * 
     * @param httpClient
     * @return Map<String, Map<String, Double>>
     */
    public Map<String, Map<String, Double>> gatherBucketMetrics(SimpleHttpClient httpClient) {
        JsonElement bucketResponse = getResponse(httpClient, BUCKET_URI);
        Map<String, Map<String, Double>> bucketMetrics = new HashMap<String, Map<String, Double>>();
        if (bucketResponse != null && bucketResponse.isJsonArray()) {
            JsonArray bucketStats = bucketResponse.getAsJsonArray();
            bucketMetrics = populateBucketMetrics(bucketStats);
        }
        return bucketMetrics;
    }

    /**
     * Gathers Bucket Replication stats from /pools/default/buckets/<bucket_name>/stats
     * 
     * Response contains all other stats but this only filters any stats that starts with 'replications', 
     * e.g. replications/03cd3332434401c64594f47eeeabbb79/beer-sample/gamesim-sample/wtavg_meta_latency
     * 
     * @param buckets
     * @param httpClient
     * @return Map<String, Map<String, Double>>
     */
    public Map<String, Map<String, Double>> gatherOtherBucketMetrics(Set<String> buckets,
            SimpleHttpClient httpClient) {
        Map<String, Map<String, Double>> otherBucketMetrics = new HashMap<String, Map<String, Double>>();

        if (buckets != null) {
            for (String bucket : buckets) {
                JsonElement bucketResponse = getResponse(httpClient, String.format(BUCKET_STATS_URI, bucket));

                if (bucketResponse != null && bucketResponse.isJsonObject()) {
                    JsonObject bucketStats = bucketResponse.getAsJsonObject();
                    JsonObject op = bucketStats.getAsJsonObject("op");
                    JsonObject samples = op.getAsJsonObject("samples");
                    populateOtherBucketMetrics(bucket, samples.entrySet().iterator(), otherBucketMetrics);
                }
            }
        }

        return otherBucketMetrics;
    }

    private void populateOtherBucketMetrics(String bucketName, Iterator<Entry<String, JsonElement>> iterator,
            Map<String, Map<String, Double>> otherBucketMetrics) {

        while (iterator.hasNext()) {
            Entry<String, JsonElement> entry = iterator.next();
            String metricName = (String) entry.getKey();
            JsonArray stats = (JsonArray) entry.getValue();

            // sample format: replications/03cd3332434401c64594f47eeeabbb79/beer-sample/gamesim-sample/wtavg_meta_latency
            if (metricName.startsWith(REPLICATIONS_KEY)) {
                populateBucketReplicationMetrics(bucketName, metricName, stats, otherBucketMetrics);

            } else if (metricName.startsWith(BUCKET_EP_METRIC_PREFIX)) {

                if (!otherBucketMetrics.containsKey(bucketName)) {
                    otherBucketMetrics.put(bucketName, new HashMap<String, Double>());
                }

                populateOtherBucketMetricsMapHelper(metricName, stats, otherBucketMetrics.get(bucketName));
            }
        }
    }

    private void populateBucketReplicationMetrics(String bucketName, String metricName, JsonArray stats,
            Map<String, Map<String, Double>> otherBucketMetrics) {
        String[] metricNameParts = metricName.split(PATH_SEPARATOR);

        if (metricNameParts != null && metricNameParts.length > 2) {
            // e.g. gamesim-sample
            String destinationBucket = metricNameParts[metricNameParts.length - 2];

            // e.g. wtavg_meta_latency
            String replicationMetricName = metricNameParts[metricNameParts.length - 1];

            // e.g. beer-sample|replications|gamesim-sample
            String metricPrefix = String.format("%s%s%s%s%s", bucketName, METRIC_SEPARATOR, REPLICATIONS_KEY,
                    METRIC_SEPARATOR, destinationBucket);

            if (!otherBucketMetrics.containsKey(metricPrefix)) {
                otherBucketMetrics.put(metricPrefix, new HashMap<String, Double>());
            }

            populateOtherBucketMetricsMapHelper(replicationMetricName, stats, otherBucketMetrics.get(metricPrefix));
        }
    }

    private void populateOtherBucketMetricsMapHelper(String metricName, JsonArray replicationStats,
            Map<String, Double> bucketMetrics) {
        if (replicationStats != null) {
            Iterator<JsonElement> stats = replicationStats.iterator();
            while (stats.hasNext()) {
                try {
                    Double value = stats.next().getAsDouble();
                    bucketMetrics.put(metricName, value);
                    break;
                } catch (ClassCastException ex) {
                    if (logger.isDebugEnabled()) {
                        logger.debug(
                                String.format("Incorrect value type retrieved for %s, ignoring...", metricName),
                                ex);
                    }
                } catch (IllegalStateException ex) {
                    if (logger.isDebugEnabled()) {
                        logger.debug(
                                String.format("Incorrect value type retrieved for %s, ignoring...", metricName),
                                ex);
                    }
                }
            }
        }

    }

    /**
     * Returns JsonElement after parsing HttpResponse from given uri
     * 
     * @param httpClient
     * @param uri
     * @return
     */
    private JsonElement getResponse(SimpleHttpClient httpClient, String uri) {
        String response = getResponseString(httpClient, uri);
        JsonElement jsonElement = null;
        try {
            jsonElement = new JsonParser().parse(response);
        } catch (JsonParseException e) {
            logger.error("Response from " + uri + "is not a json");
        }
        return jsonElement;
    }

    /**
     * Returns HttpResponse as string from given url
     * 
     * @param httpClient
     * @param path
     * @return
     */
    private String getResponseString(SimpleHttpClient httpClient, String path) {
        Response response = null;
        String responseString = "";
        try {
            response = httpClient.target().path(path).get();
            responseString = response.string();
        } catch (Exception e) {
            logger.error("Exception in getting response from " + path, e);
            throw new RuntimeException(e);
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
            } catch (Exception ex) {
                // Ignore
            }
        }
        return responseString;
    }

    /**
     * Populates the cluster metrics hashmap
     * 
     * @param nodeMetrics
     * @param clusterStats
     * @return
     */
    private Map<String, Map<String, Double>> populateClusterMetrics(Map<String, Map<String, Double>> nodeMetrics,
            JsonObject clusterStats) {
        Map<String, Double> clusterMetrics = new HashMap<String, Double>();
        if (clusterStats != null) {
            JsonObject ramStats = clusterStats.getAsJsonObject("ram");
            Iterator<Entry<String, JsonElement>> iterator = ramStats.entrySet().iterator();
            populateMetricsMapHelper(iterator, clusterMetrics, "ram_");

            JsonObject hddStats = clusterStats.getAsJsonObject("hdd");
            iterator = hddStats.entrySet().iterator();
            populateMetricsMapHelper(iterator, clusterMetrics, "hdd_");
        }
        nodeMetrics.put(CLUSTER_STATS_KEY, clusterMetrics);
        return nodeMetrics;
    }

    /**
     * Populates the node metrics hashmap
     * 
     * @param nodes
     * @return
     */
    private Map<String, Map<String, Double>> populateNodeMetrics(JsonArray nodes) {
        Map<String, Map<String, Double>> nodeMetrics = new HashMap<String, Map<String, Double>>();
        for (JsonElement node : nodes) {
            JsonObject nodeObject = node.getAsJsonObject();
            Map<String, Double> metrics = new HashMap<String, Double>();
            nodeMetrics.put(nodeObject.get("hostname").getAsString(), metrics);

            JsonObject interestingStats = nodeObject.getAsJsonObject("interestingStats");
            Iterator<Entry<String, JsonElement>> iterator = interestingStats.entrySet().iterator();
            populateMetricsMapHelper(iterator, metrics, "");

            JsonObject systemStats = nodeObject.getAsJsonObject("systemStats");
            iterator = systemStats.entrySet().iterator();
            populateMetricsMapHelper(iterator, metrics, "");

            iterator = nodeObject.entrySet().iterator();
            populateMetricsMapHelper(iterator, metrics, "");
        }
        return nodeMetrics;
    }

    /**
     * Populates the bucket metrics hashmap
     * 
     * @param buckets
     * @return
     */
    private Map<String, Map<String, Double>> populateBucketMetrics(JsonArray buckets) {
        Map<String, Map<String, Double>> bucketMetrics = new HashMap<String, Map<String, Double>>();
        for (JsonElement bucket : buckets) {
            JsonObject bucketObject = bucket.getAsJsonObject();
            Map<String, Double> metrics = new HashMap<String, Double>();
            bucketMetrics.put(bucketObject.get("name").getAsString(), metrics);

            JsonObject interestingStats = bucketObject.getAsJsonObject("quota");
            Iterator<Entry<String, JsonElement>> iterator = interestingStats.entrySet().iterator();
            populateMetricsMapHelper(iterator, metrics, "");

            JsonObject systemStats = bucketObject.getAsJsonObject("basicStats");
            iterator = systemStats.entrySet().iterator();
            populateMetricsMapHelper(iterator, metrics, "");
        }
        return bucketMetrics;
    }

    /**
     * Populates an empty map with values retrieved from the entry set of a Json
     * Object
     * 
     * @param iterator
     *            An entry set iterator for the json object
     * @param metrics
     *            Initially empty map that is populated based on the values
     *            retrieved from entry set
     * @param prefix
     *            Optional prefix for the metric name to distinguish duplicate
     *            metric names
     */
    private void populateMetricsMapHelper(Iterator<Entry<String, JsonElement>> iterator,
            Map<String, Double> metrics, String prefix) {
        while (iterator.hasNext()) {
            Entry<String, JsonElement> entry = iterator.next();
            String metricName = (String) entry.getKey();
            JsonElement value = (JsonElement) entry.getValue();
            if (value instanceof JsonPrimitive && NumberUtils.isNumber(value.getAsString())) {
                Double val = value.getAsDouble();
                metrics.put(prefix + metricName, val);
            }
        }
    }
}