com.quigley.zabbixj.agent.active.ActiveThread.java Source code

Java tutorial

Introduction

Here is the source code for com.quigley.zabbixj.agent.active.ActiveThread.java

Source

/*
 * Zabbix/J - A Java agent for the Zabbix monitoring system.
 * Copyright (C) 2006-2010 Michael F. Quigley Jr.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 */

package com.quigley.zabbixj.agent.active;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.quigley.zabbixj.ZabbixException;
import com.quigley.zabbixj.metrics.MetricsContainer;

public class ActiveThread extends Thread {
    public ActiveThread(MetricsContainer metricsContainer, String hostName, InetAddress serverAddress,
            int serverPort, int refreshInterval) {
        running = true;
        checks = new HashMap<Integer, List<String>>();
        lastChecked = new HashMap<Integer, Long>();

        this.metricsContainer = metricsContainer;
        this.hostName = hostName;
        this.serverAddress = serverAddress;
        this.serverPort = serverPort;
        this.refreshInterval = refreshInterval;
    }

    public void run() {
        if (log.isDebugEnabled()) {
            log.debug("ActiveThread Starting.");
        }

        try {
            if (log.isDebugEnabled()) {
                log.debug("Starting initial refresh of active checks.");
            }

            requestActiveChecks();

            if (log.isDebugEnabled()) {
                log.debug("Initial refresh of active checks completed.");
            }

        } catch (Exception e) {
            log.error("Initial refresh failed.", e);
        }

        while (running) {
            try {
                Thread.sleep(1000);

            } catch (InterruptedException ie) {
                return;
            }

            long clock = System.currentTimeMillis() / 1000;

            if ((clock - lastRefresh) >= refreshInterval) {
                try {
                    requestActiveChecks();

                } catch (Exception e) {
                    log.error("Unable to refresh.", e);
                }
            }

            for (int delay : checks.keySet()) {
                long delayLastChecked = lastChecked.get(delay);
                if (clock - delayLastChecked >= delay) {
                    try {
                        sendMetrics(delay, checks.get(delay));

                    } catch (Exception e) {
                        log.error("Unable to send metrics.", e);
                    }
                }
            }
        }
    }

    private void requestActiveChecks() throws Exception {
        if (log.isDebugEnabled()) {
            log.debug("Requesting a list of active checks from the server.");
        }

        Socket socket = new Socket(serverAddress, serverPort);
        InputStream input = socket.getInputStream();
        OutputStream output = socket.getOutputStream();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        JSONObject request = new JSONObject();
        request.put("request", "active checks");
        request.put("host", hostName);

        byte[] buffer = getRequest(request);

        output.write(buffer);
        output.flush();

        buffer = new byte[10240];
        int read = 0;
        while ((read = input.read(buffer, 0, 10240)) != -1) {
            baos.write(buffer, 0, read);
        }

        socket.close();

        JSONObject response = getResponse(baos.toByteArray());
        if (response.getString("response").equals("success")) {
            refreshFromActiveChecksResponse(response);

        } else {
            log.warn("Server reported a failure when requesting active checks:" + response.getString("info"));
        }

        lastRefresh = System.currentTimeMillis() / 1000;
    }

    private void refreshFromActiveChecksResponse(JSONObject response) throws JSONException {
        ActiveChecksResponseIndex index = getActiveChecksResponseIndex(response);
        insertNewChecks(index);
        pruneChangedChecks(index);
        pruneUnusedDelays(index);
    }

    private ActiveChecksResponseIndex getActiveChecksResponseIndex(JSONObject response) throws JSONException {
        ActiveChecksResponseIndex index = new ActiveChecksResponseIndex();

        JSONArray data = response.getJSONArray("data");
        for (int i = 0; i < data.length(); i++) {
            JSONObject check = data.getJSONObject(i);
            String key = check.getString("key");
            int delay = check.getInt("delay");
            index.add(key, delay);
        }

        return index;
    }

    private void insertNewChecks(ActiveChecksResponseIndex index) {
        long clock = System.currentTimeMillis() / 1000;

        for (String key : index.getIndex().keySet()) {
            int delay = index.getIndex().get(key);
            if (!checks.containsKey(delay)) {
                if (log.isDebugEnabled()) {
                    log.debug("Inserting new check list for delay '" + delay + "'.");
                }
                checks.put(delay, new ArrayList<String>());
            }
            List<String> keysForDelay = checks.get(delay);
            if (!keysForDelay.contains(key)) {
                if (log.isDebugEnabled()) {
                    log.debug("Adding new key '" + key + "' to check list for delay '" + delay + "'.");
                }
                keysForDelay.add(key);
            }
            if (!lastChecked.containsKey(delay)) {
                lastChecked.put(delay, clock);
            }
        }
    }

    private void pruneChangedChecks(ActiveChecksResponseIndex index) {
        for (int delay : index.getDelays()) {
            List<String> keysForDelay = checks.get(delay);
            for (String key : new ArrayList<String>(keysForDelay)) {
                if (index.getIndex().containsKey(key)) {
                    int currentDelay = index.getIndex().get(key);
                    if (currentDelay != delay) {
                        if (log.isDebugEnabled()) {
                            log.debug("Removing '" + key + "' from delay '" + delay + "' list.");
                        }

                        keysForDelay.remove(key);
                    }
                } else {
                    if (log.isDebugEnabled()) {
                        log.debug("Removing '" + key + "' from delay '" + delay + "' list.");
                    }

                    keysForDelay.remove(key);
                }
            }
            checks.put(delay, keysForDelay);
        }
    }

    private void pruneUnusedDelays(ActiveChecksResponseIndex index) {
        for (int delay : new ArrayList<Integer>(lastChecked.keySet())) {
            if (!index.getDelays().contains(delay)) {
                if (log.isDebugEnabled()) {
                    log.debug("Removing unused delay '" + delay + "' from last checked list.");
                }

                lastChecked.remove(delay);
            }
        }
        for (int delay : new ArrayList<Integer>(checks.keySet())) {
            if (!index.getDelays().contains(delay)) {
                if (log.isDebugEnabled()) {
                    log.debug("Removing unused delay '" + delay + "' from checks.");
                }

                checks.remove(delay);
            }
        }
    }

    private void sendMetrics(int delay, List<String> keyList) throws Exception {
        if (log.isDebugEnabled()) {
            String message = "Sending metrics for delay '" + delay + "' with keys: ";
            for (int i = 0; i < keyList.size(); i++) {
                if (i > 0) {
                    message += ", ";
                }
                message += keyList.get(i);
            }
            log.debug(message);
        }

        long clock = System.currentTimeMillis() / 1000;

        JSONObject metrics = new JSONObject();
        metrics.put("request", "agent data");

        JSONArray data = new JSONArray();
        for (String keyName : keyList) {
            JSONObject key = new JSONObject();
            key.put("host", hostName);
            key.put("key", keyName);
            try {
                Object value = metricsContainer.getMetric(keyName);
                key.put("value", value.toString());

            } catch (Exception e) {
                key.put("value", "ZBX_NOTSUPPORTED");
            }
            key.put("clock", "" + clock);

            data.put(key);
        }
        metrics.put("data", data);
        metrics.put("clock", "" + clock);

        Socket socket = new Socket(serverAddress, serverPort);
        InputStream input = socket.getInputStream();
        OutputStream output = socket.getOutputStream();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        output.write(getRequest(metrics));
        output.flush();

        byte[] buffer = new byte[10240];
        int read = 0;
        while ((read = input.read(buffer, 0, 10240)) != -1) {
            baos.write(buffer, 0, read);
        }

        socket.close();

        JSONObject response = getResponse(baos.toByteArray());
        if (response.getString("response").equals("success")) {
            if (log.isDebugEnabled()) {
                log.debug("The server reported success '" + response.getString("info") + "'.");
            }
        } else {
            log.error("Failure!");
        }

        lastChecked.put(delay, clock);
    }

    private byte[] getRequest(JSONObject jsonObject) throws Exception {
        byte[] requestBytes = jsonObject.toString().getBytes();

        String header = "ZBXD\1";
        byte[] headerBytes = header.getBytes();

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(bos);
        dos.writeLong(requestBytes.length);
        dos.flush();
        dos.close();
        bos.close();
        byte[] requestLengthBytes = bos.toByteArray();

        byte[] allBytes = new byte[headerBytes.length + requestLengthBytes.length + requestBytes.length];

        int index = 0;
        for (int i = 0; i < headerBytes.length; i++) {
            allBytes[index++] = headerBytes[i];
        }
        for (int i = 0; i < requestLengthBytes.length; i++) {
            allBytes[index++] = requestLengthBytes[7 - i]; // Reverse the byte order.
        }
        for (int i = 0; i < requestBytes.length; i++) {
            allBytes[index++] = requestBytes[i];
        }

        return allBytes;
    }

    private JSONObject getResponse(byte[] responseBytes) throws Exception {
        byte[] sizeBuffer = new byte[8];
        int index = 0;
        for (int i = 12; i > 4; i--) {
            sizeBuffer[index++] = responseBytes[i];
        }
        ByteArrayInputStream bais = new ByteArrayInputStream(sizeBuffer);
        DataInputStream dis = new DataInputStream(bais);
        long size = dis.readLong();
        dis.close();
        bais.close();

        byte[] jsonBuffer = new byte[responseBytes.length - 13];
        if (jsonBuffer.length != size) {
            throw new ZabbixException("Reported and actual buffer sizes differ!");
        }

        index = 0;
        for (int i = 13; i < responseBytes.length; i++) {
            jsonBuffer[index++] = responseBytes[i];
        }

        JSONObject response = new JSONObject(new String(jsonBuffer));

        return response;
    }

    public void shutdown() {
        running = false;
    }

    private boolean running;
    private Map<Integer, List<String>> checks;
    private Map<Integer, Long> lastChecked;
    private long lastRefresh;

    private MetricsContainer metricsContainer;
    private String hostName;
    private InetAddress serverAddress;
    private int serverPort;
    private int refreshInterval;

    private class ActiveChecksResponseIndex {
        public ActiveChecksResponseIndex() {
            index = new HashMap<String, Integer>();
            delays = new ArrayList<Integer>();
        }

        public Map<String, Integer> getIndex() {
            return index;
        }

        public List<Integer> getDelays() {
            return delays;
        }

        public void add(String key, int delay) {
            index.put(key, delay);
            if (!delays.contains(delay)) {
                delays.add(delay);
            }
        }

        private Map<String, Integer> index;
        private List<Integer> delays;
    }

    private static Logger log = LoggerFactory.getLogger(ActiveThread.class);
}