org.openiot.gsn.utils.GSNMonitor.java Source code

Java tutorial

Introduction

Here is the source code for org.openiot.gsn.utils.GSNMonitor.java

Source

/**
*    Copyright (c) 2011-2014, OpenIoT
*   
*    This file is part of OpenIoT.
*
*    OpenIoT is free software: you can redistribute it and/or modify
*    it under the terms of the GNU Lesser General Public License as published by
*    the Free Software Foundation, version 3 of the License.
*
*    OpenIoT 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 Lesser General Public License for more details.
*
*    You should have received a copy of the GNU Lesser General Public License
*    along with OpenIoT.  If not, see <http://www.gnu.org/licenses/>.
*
*     Contact: OpenIoT mailto: info@openiot.eu
 * @author Sofiane Sarni
*/

package org.openiot.gsn.utils;

import org.openiot.gsn.beans.GSNSessionAddress;
import org.openiot.gsn.beans.VSensorMonitorConfig;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.ConnectException;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.SimpleEmail;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

/*
* GSN Monitor
* Monitors a series of Virtual Sensors (last update time)
* and generates a short report shown on standard output
* You can put this program in a Cron job and run it regularely to check status.
* It can also be used from Nagios
* Syntax for calling:
* java -jar VSMonitor.jar <config_file>
*  e.g.  java -jar VSMonitor.jar conf/monitoring.cfg
* As input, a config file containing list sensors described by: 
* name of sensor,
* timeout (expressing expected update period, for example every 1h),
* gsn session address (e.g. www.server.com:22001/gsn)
* and possibly a username and password, in case session requires authentication.
* Syntax for configuration file
* # Servers without authentication
* sensor1@10d@www.server.com:22001/gsn
* sensor2@20m@www.server2.com:22003/gsn
* # Server with authentication
* sensor3@1h@www.server4.com:22005/gsn@httpusername:httppassword
* */
public class GSNMonitor {

    public static final String CONFIG_SEPARATOR = "@";
    public static final String DEFAULT_GSN_LOG4J_PROPERTIES = "conf/log4j.properties";

    public static final int STATUS_OK = 0;
    public static final int STATUS_WARNING = 1;
    public static final int STATUS_CRITICAL = 2;
    public static final int STATUS_UNKNOWN = 3;

    private static int status = STATUS_OK;

    private static transient final Logger logger = Logger.getLogger(VSMonitor.class);

    public static HashMap<String, VSensorMonitorConfig> monitoredSensors = new HashMap<String, VSensorMonitorConfig>();
    public static HashMap<String, Long> sensorsUpdateDelay = new HashMap<String, Long>();
    public static List<GSNSessionAddress> listOfGSNSessions = new Vector<GSNSessionAddress>();
    public static List<String> listOfMails = new Vector<String>();

    public static StringBuilder errorsBuffer = new StringBuilder();
    public static StringBuilder warningsBuffer = new StringBuilder();
    public static StringBuilder infosBuffer = new StringBuilder();
    public static StringBuilder summary = new StringBuilder();
    public static StringBuilder report = new StringBuilder();

    private static final String GSN_REALM = "GSNRealm";
    private static String gmail_username;
    private static String gmail_password;
    private static final String SMTP_GMAIL_COM = "smtp.gmail.com";
    private static int nHostsDown = 0;
    private static int nSensorsLate = 0;

    /*
    * Reads config file and initializes:
    * e-mail config (gmail_username, gmail_password)
    * lists
    * - monitoredSensors: list of monitored sensors (including expected update delay)
    * - sensorsUpdateDelay: list of sensors update delays (initialized to -1)
    * - listOfGSNSessions: list of GSN sessions
    * */
    public static void initFromFile(String fileName) {
        long timeout;
        String vsensorname;
        String host;
        String path;
        int port;
        boolean needspassword;
        String password;
        String username;
        logger.warn("Trying to initialize VSMonitor from file <" + fileName + ">");
        try {
            BufferedReader in = new BufferedReader(new FileReader(fileName));
            String str;
            while ((str = in.readLine()) != null) {
                // ignore comments starting with #
                if (str.trim().indexOf("#") == 0) {
                    continue;
                }
                if (str.trim().indexOf("@gmail-username") >= 0) {
                    gmail_username = str.trim().split(" ")[1];
                    //System.out.println("GMAIL Username: "+ gmail_username);
                    continue;
                }
                if (str.trim().indexOf("@gmail-password") >= 0) {
                    gmail_password = str.trim().split(" ")[1];
                    //System.out.println("GMAIL password: "+ gmail_password);
                    continue;
                }
                //@gmail-username
                //@gmail-password
                String[] s = str.trim().split(CONFIG_SEPARATOR);
                if (s.length < 3) {
                    logger.warn("Malformed monitoring line in file <" + fileName + "> : " + str);
                    //System.out.println("Malformed monitoring line in file <"+ fileName + "> : "+str);
                } else {
                    //System.out.println(s.length+" Elements found");

                    vsensorname = s[0].trim();
                    timeout = VSensorMonitorConfig.timeOutFromString(s[1].trim());
                    //System.out.println("\""+s[2]+"\"");
                    String[] host_port_path = s[2].split(":");
                    //System.out.println(host_port_path.length);
                    if (host_port_path.length != 2) {
                        logger.warn("Malformed monitoring line in file <" + fileName + "> : " + str);
                        //System.out.println("Malformed monitoring line in file <"+ fileName + "> : "+str);
                        continue;
                    } else {
                        //System.out.println("["+host_port_path[0].trim()+"]["+host_port_path[1].trim()+"]");
                        host = host_port_path[0].trim();
                        int j = host_port_path[1].trim().indexOf("/");
                        String portStr = host_port_path[1].trim().substring(0, j);
                        //System.out.println("Port:"+portStr);
                        path = host_port_path[1].trim().substring(j);
                        //System.out.println("Path:"+"\""+path+"\"");

                        try {
                            port = Integer.parseInt(portStr);
                            //System.out.println(">>"+port);
                        } catch (NumberFormatException e) {
                            logger.warn("Malformed monitoring line in file <" + fileName + "> : " + str);
                            //System.out.println("Malformed monitoring line in file <"+ fileName + "> : "+str);
                            continue;
                        }

                        if (s.length > 3) { // needs password
                            needspassword = true;
                            String[] username_password = s[3].split(":");
                            if (username_password.length > 1) {
                                username = username_password[0].trim();
                                password = username_password[1].trim();
                            } else {
                                logger.warn("Malformed monitoring line in file <" + fileName + "> : " + str);
                                //System.out.println("Malformed monitoring line in file <"+ fileName + "> : "+str);
                                continue;
                            }
                        } else {
                            needspassword = false;
                            username = "";
                            password = "";
                        }
                        // DEBUG INFO
                        //System.out.println("TIMEOUT: "+timeout);
                        //System.out.println("Creating object with : "+vsensorname+" "+host+" "+port+" "+timeout+" "+path+" "+needspassword+" "+username+" "+password);
                        monitoredSensors.put(vsensorname, new VSensorMonitorConfig(vsensorname, host, port, timeout,
                                path, needspassword, username, password));

                        // DEBUG INFO
                        //System.out.println("RESULT: "+ monitoredSensors.get(vsensorname).toString());

                        sensorsUpdateDelay.put(vsensorname, new Long(-1)); // not yes initialized, to be initialized when web server is queried

                        GSNSessionAddress gsnSessionAdress = new GSNSessionAddress(host, path, port, needspassword,
                                username, password); //TODO: insitialize it

                        if (!listOfGSNSessions.contains(gsnSessionAdress)) {
                            listOfGSNSessions.add(gsnSessionAdress);
                        }

                        //System.out.println("VS: "+"\""+vsensorname+"\""+" timeout: " + Long.toString(timeout));
                        //System.out.println("Added: "+ monitoredSensors.get(vsensorname));
                        logger.warn("Added:" + monitoredSensors.get(vsensorname));
                    }
                }
            }
        } catch (IOException e) {
            logger.warn("IO Exception while trying to open file <" + fileName + "> " + e);
        }
    }

    /*
    * Queries a GSN session for status
    * Uses Http Get method to read xml status (usually under /gsn)
    * and initializes global variables :
    * - errorsBuffer
    * - noErrrorsBuffer
    * - nHostsDown
    * */
    public static void readStatus(GSNSessionAddress gsnSessionAddress) throws Exception {

        String httpAddress = gsnSessionAddress.getURL();

        DefaultHttpClient client = new DefaultHttpClient();

        if (gsnSessionAddress.needsPassword()) {
            client.getCredentialsProvider().setCredentials(
                    new AuthScope(gsnSessionAddress.getHost(), gsnSessionAddress.getPort()/*, GSN_REALM*/),
                    new UsernamePasswordCredentials(gsnSessionAddress.getUsername(),
                            gsnSessionAddress.getPassword()));
        }

        logger.warn("Querying server: " + httpAddress);
        HttpGet get = new HttpGet(httpAddress);

        try {
            // execute the GET, getting string directly
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            String responseBody = client.execute(get, responseHandler);

            parseXML(responseBody);

        } catch (HttpResponseException e) {
            errorsBuffer.append("HTTP 401 Authentication Needed for : ").append(httpAddress).append("\n");
        } catch (UnknownHostException e) {
            errorsBuffer.append("Unknown host: ").append(httpAddress).append("\n");
            nHostsDown++;
        } catch (ConnectException e) {
            errorsBuffer.append("Connection refused to host: ").append(httpAddress).append("\n");
            raiseStatusTo(STATUS_CRITICAL);
            nHostsDown++;
        } finally {
            // release any connection resources used by the method
            client.getConnectionManager().shutdown();
        }
    }

    public static int raiseStatusTo(int newStatus) {
        if (status < newStatus)
            status = newStatus;
        return status;
    }

    /*
    * Checks update times
    * */
    public static void checkUpdateTimes() {
        for (int i = 0; i < sensorsUpdateDelay.size(); i++) {
            Long lastUpdated = (Long) sensorsUpdateDelay.values().toArray()[i];
            String sensorName = (String) sensorsUpdateDelay.keySet().toArray()[i];
            if (lastUpdated.longValue() > monitoredSensors.get(sensorName).getTimeout()) {

                raiseStatusTo(STATUS_WARNING);

                warningsBuffer.append(sensorName).append("@").append(monitoredSensors.get(sensorName).getHost())
                        .append(":").append(monitoredSensors.get(sensorName).getPort()).append(" not updated for ")
                        .append(VSensorMonitorConfig.ms2dhms(sensorsUpdateDelay.get(sensorName)))
                        .append(" (expected <")
                        .append(VSensorMonitorConfig.ms2dhms(monitoredSensors.get(sensorName).getTimeout()))
                        .append(")\n");
                nSensorsLate++;
            } else {
                infosBuffer.append(sensorName).append("@").append(monitoredSensors.get(sensorName).getHost())
                        .append(":").append(monitoredSensors.get(sensorName).getPort()).append(" (on time)\n");
            }
        }
    }

    /*
    * Sends an e-mail to recipients specified in command line
    * (through global listOfMails)
    * with the global summary as subject
    * and global errorsBuffer as body
    * */
    private static void sendMail() throws EmailException {

        SimpleEmail email = new SimpleEmail();
        //email.setDebug(true);
        email.setHostName(SMTP_GMAIL_COM);
        email.setAuthentication(gmail_username, gmail_password);
        //System.out.println(gmail_username +" "+ gmail_password);
        email.getMailSession().getProperties().put("mail.smtp.starttls.enable", "true");
        email.getMailSession().getProperties().put("mail.smtp.auth", "true");
        email.getMailSession().getProperties().put("mail.debug", "true");
        email.getMailSession().getProperties().put("mail.smtp.port", "465");
        email.getMailSession().getProperties().put("mail.smtp.socketFactory.port", "465");
        email.getMailSession().getProperties().put("mail.smtp.socketFactory.class",
                "javax.net.ssl.SSLSocketFactory");
        email.getMailSession().getProperties().put("mail.smtp.socketFactory.fallback", "false");
        email.getMailSession().getProperties().put("mail.smtp.starttls.enable", "true");

        for (String s : listOfMails) {
            email.addTo(s);
        }
        email.setFrom(gmail_username + "@gmail.com", gmail_username);

        email.setSubject("[GSN Alert] " + summary.toString());
        email.setMsg(report.toString());
        email.send();

    }

    /*
    * parses the XML string
    * and initializes sensorsUpdateDelay
    * with update delays for relevant sensors
    * */
    public static void parseXML(String s) {
        try {

            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            InputSource inputSource = new InputSource();
            inputSource.setCharacterStream(new StringReader(s));

            Document document = documentBuilder.parse(inputSource);
            NodeList nodes = document.getElementsByTagName("virtual-sensor");

            for (int i = 0; i < nodes.getLength(); i++) {
                Element element = (Element) nodes.item(i);

                String sensor_name = element.getAttribute("name");

                if (!sensorsUpdateDelay.containsKey(sensor_name))
                    continue; // skip sensors that are not monitored

                logger.warn("Sensor: " + sensor_name);

                NodeList listOfField = element.getElementsByTagName("field");
                for (int j = 0; j < listOfField.getLength(); j++) {
                    Element line = (Element) listOfField.item(j);

                    if (line.getAttribute("name").indexOf("timed") >= 0) {
                        String last_updated_as_string = line.getTextContent();

                        try {
                            Long last_updated_as_Long = GregorianCalendar.getInstance().getTimeInMillis()
                                    - VSensorMonitorConfig.datetime2timestamp(last_updated_as_string);
                            logger.warn(new StringBuilder(last_updated_as_string).append(" => ")
                                    .append(VSensorMonitorConfig.ms2dhms(last_updated_as_Long)).toString());

                            sensorsUpdateDelay.put(sensor_name, last_updated_as_Long);
                        } catch (ParseException e) {
                            errorsBuffer.append("Last update time for sensor ").append(sensor_name)
                                    .append(" cannot be read. Error while parsing > ")
                                    .append(last_updated_as_string).append(" <\n");
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.warn("Exception while parsing XML\n");
            e.printStackTrace();
        }
    }

    /*
    * Prints the stack trace of the exception to a string.
    * */
    public static String getStackTrace(Throwable t) {
        StringWriter stringWriter = new StringWriter();
        PrintWriter printWriter = new PrintWriter(stringWriter, true);
        t.printStackTrace(printWriter);
        printWriter.flush();
        stringWriter.flush();
        return stringWriter.toString();
    }

    public static void main(String[] args) {

        PropertyConfigurator.configure(DEFAULT_GSN_LOG4J_PROPERTIES);
        String configFileName;

        if (args.length >= 2) {
            configFileName = args[0];
            System.out.println("Using config file: " + configFileName);
            for (int i = 1; i < args.length; i++) {
                System.out.println("Adding e-mail: " + args[i]);
                listOfMails.add(args[i]);
            }
        } else {
            System.out.println("Usage java -jar VSMonitor.jar <config_file> <list_of_mails>");
            System.out.println("e.g.  java -jar VSMonitor.jar conf/monitoring.cfg user@gmail.com admin@gmail.com");
            return;
        }

        initFromFile(configFileName);

        // for each monitored GSN server
        Iterator iter = listOfGSNSessions.iterator();
        while (iter.hasNext()) {

            try {
                readStatus((GSNSessionAddress) iter.next());
            } catch (Exception e) {
                logger.error("Exception: " + e.getMessage());
                logger.error("StackTrace:\n" + getStackTrace(e));
            }
        }

        checkUpdateTimes();

        // Generate Report
        report.append("\n[ERROR]\n" + errorsBuffer).append("\n[WARNING]\n" + warningsBuffer)
                .append("\n[INFO]\n" + infosBuffer);

        if ((nSensorsLate > 0) || (nHostsDown > 0)) {
            summary.append("WARNING: ");
            if (nHostsDown > 0)
                summary.append(nHostsDown + " host(s) down. ");
            if (nSensorsLate > 0)
                summary.append(nSensorsLate + " sensor(s) not updated. ");

            // Send e-mail only if there are errors
            try {
                sendMail();
            } catch (EmailException e) {
                logger.error("Cannot send e-mail. " + e.getMessage());
                logger.error("StackTrace:\n" + getStackTrace(e));
            }
        }

        // Showing report
        System.out.println(summary);
        System.out.println(report);

        System.exit(status);
    }
}