org.hyperic.hq.plugin.postgresql.PostgreSQLServerDetector.java Source code

Java tutorial

Introduction

Here is the source code for org.hyperic.hq.plugin.postgresql.PostgreSQLServerDetector.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-2008], 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.plugin.postgresql;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.hq.product.AutoServerDetector;
import org.hyperic.hq.product.PluginException;
import org.hyperic.hq.product.ServerDetector;
import org.hyperic.hq.product.ServerResource;
import org.hyperic.hq.product.ServiceResource;
import org.hyperic.util.config.ConfigResponse;
import org.hyperic.util.jdbc.DBUtil;

public class PostgreSQLServerDetector extends ServerDetector implements AutoServerDetector {

    private Log log = LogFactory.getLog(PostgreSQLServerDetector.class);
    private static final String PTQL_QUERY = "State.Name.re=post(master|gres),State.Name.Pne=$1,Args.0.re=.*post(master|gres)(.exe)?$";
    protected static final String DB_QUERY = "SELECT datname FROM pg_database WHERE datistemplate IS FALSE AND datallowconn IS TRUE";
    private static final String TABLE_QUERY = "SELECT relname, schemaname FROM pg_stat_user_tables";
    private static final String INDEX_QUERY = "SELECT indexrelname, schemaname FROM pg_stat_user_indexes";

    public List getServerResources(ConfigResponse platformConfig) throws PluginException {
        List servers = new ArrayList();

        long[] pids = getPids(PTQL_QUERY);
        log.debug("[getServerProcessList] pids.length=" + pids.length);

        for (int i = 0; i < pids.length; i++) {
            String exe = getProcExe(pids[i]);
            List args = Arrays.asList(getProcArgs(pids[i]));
            log.debug("[getServerProcessList] pid=" + pids[i] + " exec=" + exe + " args=" + args);
            if (exe != null) {
                String version = getVersion(exe);
                String expectedVersion = getTypeProperty("version");
                String pgData = getArgument("-D", args);
                try {
                    pgData = new File(pgData).getCanonicalPath();
                } catch (IOException ex) {
                    log.debug(ex, ex);
                }
                boolean correctVersion = Pattern.compile(expectedVersion).matcher(version).find();
                log.debug("[getServerProcessList] version=" + version + " correctVersion=" + correctVersion);

                //check for vPostgres and HQ
                final boolean isHQ = exe.indexOf("hqdb") != -1;
                if (correctVersion) {
                    File vflicense = new File(new File(exe).getParent(), "vflicense");
                    if (getTypeInfo().getName().startsWith("vPostgres")) {
                        correctVersion = vflicense.exists();
                    } else if (getTypeInfo().getName().startsWith("HQ")) {
                        correctVersion = isHQ;
                    } else {
                        correctVersion = !(vflicense.exists() || isHQ);
                    }
                }

                if (correctVersion) {
                    ServerResource server = createServerResource(exe);
                    server.setMeasurementConfig();
                    if (!isHQ) {
                        server.setControlConfig();
                    }
                    server.setIdentifier(server.getIdentifier() + "$" + pgData);
                    ConfigResponse cprop = new ConfigResponse();
                    cprop.setValue("version", version);
                    setCustomProperties(server, cprop);
                    ConfigResponse productConfig = prepareConfig(pgData, args);
                    populateListeningPorts(pids[i], productConfig, true);
                    setProductConfig(server, productConfig);
                    String basename = getPlatformName() + " " + getTypeInfo().getName();
                    server.setName(prepareName(
                            basename + " " + (isHQ ? PostgreSQL.HQ_SERVER_NAME : PostgreSQL.SERVER_NAME),
                            server.getProductConfig(), null));
                    servers.add(server);
                } else {
                    log.debug("[getServerProcessList] pid='" + pids[i] + "' Is not a '" + getTypeInfo().getName()
                            + "'");
                }
            }
        }
        return servers;
    }

    @Override
    protected List discoverServices(ConfigResponse serverConfig) throws PluginException {
        log.debug("[discoverServices] config=" + serverConfig);
        boolean isHQ = getTypeInfo().getName().startsWith("HQ");
        ArrayList services = new ArrayList();
        String user = serverConfig.getValue(PostgreSQL.PROP_USER);
        String pass = serverConfig.getValue(PostgreSQL.PROP_PASS);
        String url = PostgreSQL.prepareUrl(serverConfig.toProperties(), null);

        try {
            Class.forName(ResourceMeasurement.JDBC_DRIVER);
        } catch (ClassNotFoundException e) {
            throw new PluginException("Unable to load JDBC Driver: " + e.getMessage());
        }

        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;

        List<String> dataBases = new ArrayList<String>();
        try {
            conn = DriverManager.getConnection(url, user, pass);
            stmt = conn.createStatement();
            rs = stmt.executeQuery(DB_QUERY);
            while (rs != null && rs.next()) {
                dataBases.add(rs.getString(1));
            }
        } catch (SQLException e) {
            throw new PluginException("Error querying for DataBases: " + e.getMessage(), e);
        } finally {
            DBUtil.closeJDBCObjects(this.log, conn, stmt, rs);
        }

        Pattern table_reg = null;
        boolean table_all = serverConfig.getValue(PostgreSQL.PROP_TABLE_REG).equalsIgnoreCase("ALL");
        boolean table_off = serverConfig.getValue(PostgreSQL.PROP_TABLE_REG).equalsIgnoreCase("OFF");
        if (!table_all && !table_off) {
            table_reg = Pattern.compile(serverConfig.getValue(PostgreSQL.PROP_TABLE_REG));
        }

        Pattern index_reg = null;
        boolean index_all = serverConfig.getValue(PostgreSQL.PROP_INDEX_REG).equalsIgnoreCase("ALL");
        boolean index_off = serverConfig.getValue(PostgreSQL.PROP_INDEX_REG).equalsIgnoreCase("OFF");
        if (!index_all && !index_off) {
            index_reg = Pattern.compile(serverConfig.getValue(PostgreSQL.PROP_INDEX_REG));
        }

        log.debug("[discoverServices] databases: " + dataBases);
        for (int i = 0; i < dataBases.size(); i++) {
            String dataBase = dataBases.get(i);
            ServiceResource db = new ServiceResource();
            db.setType(this, "DataBase");

            ConfigResponse dbPC = new ConfigResponse();
            dbPC.setValue(PostgreSQL.PROP_DB, dataBase);

            db.setProductConfig(dbPC);
            db.setMeasurementConfig();
            db.setControlConfig();

            db.setServiceName(prepareName(isHQ ? PostgreSQL.HQ_DB_NAME : PostgreSQL.DB_NAME, serverConfig, dbPC));

            services.add(db);

            try {
                conn = DriverManager.getConnection(PostgreSQL.prepareUrl(serverConfig.toProperties(), dataBase),
                        user, pass);
                stmt = conn.createStatement();
                rs = stmt.executeQuery(TABLE_QUERY);

                while (rs != null && rs.next()) {
                    String tablename = rs.getString(1);
                    String schemaname = rs.getString(2);
                    if (isValidName(tablename, table_all, table_off, table_reg)) {
                        ServiceResource service = new ServiceResource();
                        service.setType(this, "Table");

                        ConfigResponse tablePC = new ConfigResponse();
                        tablePC.setValue(PostgreSQL.PROP_DB, dataBase);
                        tablePC.setValue(PostgreSQL.PROP_TABLE, tablename);
                        tablePC.setValue(PostgreSQL.PROP_SCHEMA, schemaname);

                        service.setProductConfig(tablePC);
                        service.setMeasurementConfig();
                        service.setControlConfig();

                        service.setServiceName(prepareName(isHQ ? PostgreSQL.HQ_TABLE_NAME : PostgreSQL.TABLE_NAME,
                                serverConfig, tablePC));

                        services.add(service);
                    }
                }
                DBUtil.closeJDBCObjects(this.log, null, stmt, rs);

                stmt = conn.createStatement();
                rs = stmt.executeQuery(INDEX_QUERY);
                while (rs != null && rs.next()) {
                    String indexname = rs.getString(1);
                    String schemaname = rs.getString(2);

                    if (isValidName(indexname, index_all, index_off, index_reg)) {
                        ServiceResource service = new ServiceResource();
                        service.setType(this, "Index");

                        ConfigResponse indexPC = new ConfigResponse();
                        indexPC.setValue(PostgreSQL.PROP_DB, dataBase);
                        indexPC.setValue(PostgreSQL.PROP_INDEX, indexname);
                        indexPC.setValue(PostgreSQL.PROP_SCHEMA, schemaname);

                        service.setProductConfig(indexPC);
                        service.setMeasurementConfig();
                        service.setControlConfig();

                        service.setServiceName(prepareName(isHQ ? PostgreSQL.INDEX_NAME : PostgreSQL.INDEX_NAME,
                                serverConfig, indexPC));

                        services.add(service);
                    }
                }
            } catch (SQLException e) {
                throw new PluginException("Error querying for services: " + e.getMessage(), e);
            } finally {
                DBUtil.closeJDBCObjects(this.log, conn, stmt, rs);
            }
        }
        return services;
    }

    private String prepareName(String pattern, ConfigResponse serverConf, ConfigResponse serviceConf) {
        List<ConfigResponse> props = new ArrayList<ConfigResponse>();
        String res = pattern;
        props.add(serverConf);
        if (serviceConf != null) {
            props.add(serviceConf);
        }

        for (int i = 0; i < props.size(); i++) {
            ConfigResponse cfg = props.get(i);
            for (Iterator<String> it = cfg.getKeys().iterator(); it.hasNext();) {
                String key = it.next();
                String val = cfg.getValue(key);
                if (val == null) {
                    val = "";
                }
                res = res.replace("${" + key + "}", val);
            }
        }
        return res.trim();
    }

    private String getVersion(String exec) {
        String command[] = { exec, "--version" };
        log.debug("[getVersionString] command= '" + Arrays.asList(command) + "'");
        String version = "";
        try {
            Process cmd = Runtime.getRuntime().exec(command);
            cmd.getOutputStream().close();
            cmd.waitFor();
            String out = inputStreamAsString(cmd.getInputStream());
            String err = inputStreamAsString(cmd.getErrorStream());
            if (log.isDebugEnabled()) {
                if (cmd.exitValue() != 0) {
                    log.error("[getVersionString] exit=" + cmd.exitValue());
                    log.error("[getVersionString] out=" + out);
                    log.error("[getVersionString] err=" + err);
                } else {
                    log.debug("[getVersionString] out=" + out);
                }
            }
            version = out;
        } catch (InterruptedException ex) {
            log.debug("[getVersionString] Error:" + ex.getMessage(), ex);
        } catch (IOException ex) {
            log.debug("[getVersionString] Error:" + ex.getMessage(), ex);
        }
        return version;
    }

    private String inputStreamAsString(InputStream stream) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(stream));
        StringBuilder sb = new StringBuilder();
        try {
            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line).append("\n");
            }
        } finally {
            br.close();
        }
        return sb.toString().trim();
    }

    private String getArgument(String opt, List<String> args) {
        String res = "";
        int i = args.indexOf(opt);
        if (i > -1) {
            res = args.get(i + 1);
        }
        return res.trim();
    }

    private String getConfiguration(String regex, String config) {
        String res = "";
        Matcher m = Pattern.compile(regex, Pattern.MULTILINE + Pattern.CASE_INSENSITIVE).matcher(config);
        if (m.find()) {
            res = m.group(1).trim();
            res = res.replaceAll("=", "");
            res = res.replaceAll("'", "");
            res = res.replaceAll("\"", "");
        }
        return res.trim();
    }

    private String loadConfiguration(String pgData) {
        String configuration = "";
        File configFile = new File(pgData, "postgresql.conf");
        if (configFile.exists() && configFile.canRead()) {
            FileInputStream in = null;
            try {
                in = new FileInputStream(configFile);
                configuration = inputStreamAsString(in);
            } catch (IOException ex) {
                log.error("Error reading file '" + configFile + "': " + ex.getMessage(), ex);
            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException ex) {
                        log.error("Error closing file '" + configFile + "'");
                    }
                }
            }
        }
        return configuration;
    }

    private ConfigResponse prepareConfig(String pgData, List<String> args) {
        ConfigResponse cf = new ConfigResponse();
        cf.setValue(PostgreSQL.PROP_DATA, pgData);
        String configuration = loadConfiguration(pgData);

        String addr = getConfiguration("^ *listen_addresses([^#]*)", configuration);
        if (addr.length() > 0) {
            if (addr.equals("*")) {
                addr = "localhost";
            }
            cf.setValue(PostgreSQL.PROP_HOST, addr);
        } else {
            addr = getArgument("-h", args);
            if (addr.length() > 0) {
                cf.setValue(PostgreSQL.PROP_HOST, addr);
            }
        }

        String port = getConfiguration("^ *port([^#]*)", configuration);
        if (port.length() > 0) {
            cf.setValue(PostgreSQL.PROP_PORT, port);
        } else {
            port = getArgument("-p", args);
            if (port.length() > 0) {
                cf.setValue(PostgreSQL.PROP_PORT, port);
            }
        }
        return cf;
    }

    private boolean isValidName(String name, boolean all, boolean off, Pattern reg) {
        boolean res;
        if (all) {
            res = true;
        } else if (off) {
            res = false;
        } else {
            res = reg.matcher(name).matches();
        }
        return res;
    }

    private void populateListeningPorts(long pid, ConfigResponse productConfig, boolean b) {
        try {
            Class du = Class.forName("org.hyperic.hq.product.DetectionUtil");
            Method plp = du.getMethod("populateListeningPorts", long.class, ConfigResponse.class, boolean.class);
            plp.invoke(null, pid, productConfig, b);
        } catch (ClassNotFoundException ex) {
            log.debug("[populateListeningPorts] Class 'DetectionUtil' not found", ex);
        } catch (NoSuchMethodException ex) {
            log.debug("[populateListeningPorts] Method 'populateListeningPorts' not found", ex);
        } catch (Exception ex) {
            log.debug("[populateListeningPorts] Problem with Method 'populateListeningPorts'", ex);
        }
    }
}