org.hyperic.hq.common.server.session.ServerConfigManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.hyperic.hq.common.server.session.ServerConfigManagerImpl.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.common.server.session;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.SessionFactory;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.mapping.Table;
import org.hyperic.hibernate.dialect.HQDialect;
import org.hyperic.hq.authz.server.session.AuthzSubject;
import org.hyperic.hq.authz.shared.AuthzSubjectManager;
import org.hyperic.hq.common.ApplicationException;
import org.hyperic.hq.common.ConfigProperty;
import org.hyperic.hq.common.SystemException;
import org.hyperic.hq.common.shared.HQConstants;
import org.hyperic.hq.common.shared.ServerConfigManager;
import org.hyperic.hq.context.Bootstrap;
import org.hyperic.hq.measurement.shared.MeasTabManagerUtil;
import org.hyperic.util.ConfigPropertyException;
import org.hyperic.util.StringUtil;
import org.hyperic.util.jdbc.DBUtil;
import org.hyperic.util.timer.StopWatch;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * This class is responsible for setting/getting the server configuration
 */
@Service("serverConfigManager")
public class ServerConfigManagerImpl implements ServerConfigManager {

    private static final String SQL_VACUUM = "VACUUM ANALYZE {0}";

    private static final int DEFAULT_COST = 15;

    private final DBUtil dbUtil;

    private final AuthzSubjectManager authzSubjectManager;

    private static final String[] APPDEF_TABLES = { "EAM_PLATFORM", "EAM_SERVER", "EAM_SERVICE",
            "EAM_CONFIG_RESPONSE", "EAM_AGENT", "EAM_IP", "EAM_RESOURCE", "EAM_CPROP_KEY", "EAM_AUDIT",
            "EAM_AIQ_SERVER", "EAM_AIQ_PLATFORM", "EAM_RESOURCE_EDGE", "EAM_RES_GRP_RES_MAP" };

    private static final String[] DATA_TABLES = { "EAM_MEASUREMENT_DATA_1D", "EAM_MEASUREMENT_DATA_6H",
            "EAM_MEASUREMENT_DATA_1H", "HQ_METRIC_DATA_COMPAT", "EAM_METRIC_PROB", "EAM_REQUEST_STAT",
            "EAM_ALERT_ACTION_LOG", "EAM_ALERT_CONDITION_LOG", "EAM_ALERT", "EAM_EVENT_LOG", "EAM_CPROP",
            "EAM_MEASUREMENT", "EAM_SRN", "HQ_AVAIL_DATA_RLE" };

    public static final String LOG_CTX = "org.hyperic.hq.common.server.session.ServerConfigManagerImpl";
    protected final Log log = LogFactory.getLog(LOG_CTX);
    private final ConfigPropertyDAO configPropertyDAO;
    private final ServerConfigAuditFactory serverConfigAuditFactory;
    private final ServerConfigCache serverConfigCache;

    @Autowired
    public ServerConfigManagerImpl(DBUtil dbUtil, AuthzSubjectManager authzSubjectManager,
            ConfigPropertyDAO configPropertyDAO, ServerConfigAuditFactory serverConfigAuditFactory,
            ServerConfigCache serverConfigCache) {
        this.dbUtil = dbUtil;
        this.authzSubjectManager = authzSubjectManager;
        this.configPropertyDAO = configPropertyDAO;
        this.serverConfigAuditFactory = serverConfigAuditFactory;
        this.serverConfigCache = serverConfigCache;
    }

    private void createChangeAudit(AuthzSubject subject, String key, String oldVal, String newVal) {
        if (key.equals(HQConstants.BaseURL)) {
            serverConfigAuditFactory.updateBaseURL(subject, newVal, oldVal);
        } else if (key.equals(HQConstants.EmailSender)) {
            serverConfigAuditFactory.updateFromEmail(subject, newVal, oldVal);
        } else if (key.equals(HQConstants.DataMaintenance)) {
            int oldHours = (int) (Long.parseLong(oldVal) / 60 / 60 / 1000);
            int newHours = (int) (Long.parseLong(newVal) / 60 / 60 / 1000);
            serverConfigAuditFactory.updateDBMaint(subject, newHours, oldHours);
        } else if (key.equals(HQConstants.DataPurgeRaw)) {
            int oldDays = (int) (Long.parseLong(oldVal) / 24 / 60 / 60 / 1000);
            int newDays = (int) (Long.parseLong(newVal) / 24 / 60 / 60 / 1000);
            serverConfigAuditFactory.updateDeleteDetailed(subject, newDays, oldDays);
        } else if (key.equals(HQConstants.AlertPurge)) {
            int oldPurge = (int) (Long.parseLong(oldVal) / 24 / 60 / 60 / 1000);
            int newPurge = (int) (Long.parseLong(newVal) / 24 / 60 / 60 / 1000);
            serverConfigAuditFactory.updateAlertPurgeInterval(subject, newPurge, oldPurge);
        } else if (key.equals(HQConstants.EventLogPurge)) {
            int oldPurge = (int) (Long.parseLong(oldVal) / 24 / 60 / 60 / 1000);
            int newPurge = (int) (Long.parseLong(newVal) / 24 / 60 / 60 / 1000);
            serverConfigAuditFactory.updateEventPurgeInterval(subject, newPurge, oldPurge);
        } else if (key.equals(HQConstants.AlertsEnabled)) {
            boolean oldEnabled = oldVal.equals("true");
            boolean newEnabled = newVal.equals("true");
            serverConfigAuditFactory.updateAlertsEnabled(subject, newEnabled, oldEnabled);
        } else if (key.equals(HQConstants.AlertNotificationsEnabled)) {
            boolean oldEnabled = oldVal.equals("true");
            boolean newEnabled = newVal.equals("true");
            serverConfigAuditFactory.updateAlertNotificationsEnabled(subject, newEnabled, oldEnabled);
        } else if (key.equals(HQConstants.HIERARCHICAL_ALERTING_ENABLED)) {
            boolean oldEnabled = oldVal.equals("true");
            boolean newEnabled = newVal.equals("true");
            serverConfigAuditFactory.updateHierarchicalAlertingEnabled(subject, newEnabled, oldEnabled);
        }
    }

    private void createChangeAudits(AuthzSubject subject, Collection<ConfigProperty> allProps,
            Properties newProps) {
        Properties oldProps = new Properties();

        for (ConfigProperty prop : allProps) {

            String val = prop.getValue();

            if (val == null) {
                val = prop.getDefaultValue();
            }

            if (val == null) {
                val = "";
            }

            oldProps.put(prop.getKey(), val);
        }

        for (Map.Entry<Object, Object> newEnt : newProps.entrySet()) {
            String newKey = (String) newEnt.getKey();
            String newVal = (String) newEnt.getValue();
            String oldVal = (String) oldProps.get(newKey);

            if (oldVal == null || !oldVal.equals(newVal)) {
                if (oldVal == null) {
                    oldVal = "";
                }
                createChangeAudit(subject, newKey, oldVal, newVal);
            }
        }
    }

    /**
     * Set the server configuration
     * 
     * @throws ConfigPropertyException - if the props object is missing a key
     *         that's currently in the database
     * 
     */
    @Transactional
    public void setConfig(AuthzSubject subject, Properties newProps)
            throws ApplicationException, ConfigPropertyException {
        setConfig(subject, null, newProps);
    }

    @Transactional
    public void deleteConfig(AuthzSubject subject, Set<String> toDelete) {
        Collection<ConfigProperty> allProps = serverConfigCache.getProps(null);
        for (ConfigProperty configProp : allProps) {

            // check if the props object has a key matching
            String key = configProp.getKey();
            if (toDelete.contains(key)) {
                configPropertyDAO.remove(configProp);
                serverConfigCache.remove(key);
            }
        }
    }

    /**
     * Set the server Configuration
     * @param prefix The config prefix to use when setting properties. The
     *        prefix is used for namespace protection and property scoping.
     * @param newProps The Properties to set.
     * @throws ConfigPropertyException - if the props object is missing a key
     *         that's currently in the database
     * 
     */
    @Transactional
    public void setConfig(AuthzSubject subject, String prefix, Properties newProps)
            throws ApplicationException, ConfigPropertyException {

        Properties tempProps = new Properties();
        tempProps.putAll(newProps);

        // get all properties
        Collection<ConfigProperty> allProps = serverConfigCache.getProps(prefix);

        createChangeAudits(subject, allProps, newProps);
        for (ConfigProperty configProp : allProps) {

            // check if the props object has a key matching
            String key = configProp.getKey();
            if (newProps.containsKey(key)) {
                tempProps.remove(key);
                String propValue = (String) newProps.get(key);
                // delete null values from prefixed properties
                if (prefix != null && (propValue == null || propValue.equals("NULL"))) {
                    configPropertyDAO.remove(configProp);
                    serverConfigCache.remove(key);
                } else {
                    // non-prefixed properties never get deleted.
                    configProp.setValue(propValue);
                    serverConfigCache.put(key, propValue);
                    // Fix Bug 1285064/HQ-4793: Save the property in DB 
                    configPropertyDAO.save(configProp);
                    if (HQConstants.HQGUID.equals(key)) {
                        // Came from read only method so need to flush the session
                        configPropertyDAO.flushSession();
                        if (log.isDebugEnabled())
                            log.debug("Saving HQ-GUID in DB [" + propValue + "]");
                    }
                }
            } else if (prefix == null) {
                // Bomb out if props are missing for non-prefixed properties
                throw new ConfigPropertyException("Updated configuration missing required key: " + key);
            }
        }

        // create properties that are still left in tempProps
        if (tempProps.size() > 0) {
            Enumeration propsToAdd = tempProps.propertyNames();
            while (propsToAdd.hasMoreElements()) {
                String key = (String) propsToAdd.nextElement();
                String propValue = tempProps.getProperty(key);
                // create the new property
                configPropertyDAO.create(prefix, key, propValue, propValue);
                serverConfigCache.put(key, propValue);
            }
        }
    }

    /**
     * Run an analyze command on all non metric tables. The metric tables are
     * handled seperately using analyzeHqMetricTables() so that only the tables
     * that have been modified are analyzed.
     * 
     * @return The time taken in milliseconds to run the command.
     * 
     */
    @Transactional
    public long analyzeNonMetricTables() {

        HQDialect dialect = (HQDialect) ((SessionFactoryImplementor) Bootstrap.getBean(SessionFactory.class))
                .getDialect();
        long duration = 0;

        Connection conn = null;
        try {
            conn = dbUtil.getConnection();

            for (Iterator i = Bootstrap.getBean(LocalSessionFactoryBean.class).getConfiguration()
                    .getTableMappings(); i.hasNext();) {
                Table t = (Table) i.next();

                if (t.getName().toUpperCase().startsWith("EAM_MEASUREMENT_DATA")
                        || t.getName().toUpperCase().startsWith("HQ_METRIC_DATA")) {
                    continue;
                }

                String sql = dialect.getOptimizeStmt(t.getName(), 0);
                duration += doCommand(conn, sql, null);
            }
        } catch (SQLException e) {
            log.error("Error analyzing table", e);
        } finally {
            DBUtil.closeConnection(LOG_CTX, conn);
        }

        return duration;
    }

    /**
     * Run an analyze command on both the current measurement data slice and the
     * previous data slice if specified.
     * 
     * @param analyzePrevMetricDataTable tells method to analyze previous metric
     *        data table as well as the current.
     * @return The time taken in milliseconds to run the command.
     * 
     */
    @Transactional
    public long analyzeHqMetricTables(boolean analyzePrevMetricDataTable) {
        long systime = System.currentTimeMillis();
        String currMetricDataTable = MeasTabManagerUtil.getMeasTabname(systime);
        long prevtime = MeasTabManagerUtil.getPrevMeasTabTime(systime);
        String prevMetricDataTable = MeasTabManagerUtil.getMeasTabname(prevtime);

        long duration = 0;
        HQDialect dialect = (HQDialect) ((SessionFactoryImplementor) Bootstrap.getBean(SessionFactory.class))
                .getDialect();

        Connection conn = null;
        try {
            String sql;
            conn = dbUtil.getConnection();
            sql = dialect.getOptimizeStmt(currMetricDataTable, DEFAULT_COST);
            duration += doCommand(conn, sql, null);
            if (analyzePrevMetricDataTable) {
                sql = dialect.getOptimizeStmt(prevMetricDataTable, DEFAULT_COST);
                duration += doCommand(conn, sql, null);
            }
        } catch (SQLException e) {
            log.error("Error analyzing metric tables", e);
            throw new SystemException(e);
        } finally {
            DBUtil.closeConnection(LOG_CTX, conn);
        }
        return duration;
    }

    private long doCommand(Connection conn, String sql, String table) throws SQLException {
        Statement stmt = null;
        StopWatch watch = new StopWatch();

        if (table == null) {
            table = "";
        }

        sql = StringUtil.replace(sql, "{0}", table);

        if (log.isDebugEnabled()) {
            log.debug("Execute command: " + sql);
        }

        try {
            stmt = conn.createStatement();
            stmt.execute(sql);
            return watch.getElapsed();
        } finally {
            DBUtil.closeStatement(LOG_CTX, stmt);
        }
    }

    /**
     * Generate new GUID and set it in properties and DB
     * This method is called only once
     * 
     * @param props server config properties
     * @return HQ-QUID
     * @author tgoldman
     */
    @Transactional
    private synchronized String generateNewGUID(Properties props) {
        // Fix bug 1285064 / HQ-4793 
        String hqGUID;

        // Generate new GUID and set it to cache and DB
        if ((hqGUID = GUIDGenerator.createGUID()) == null) {
            hqGUID = "unknown";
        } else {
            props.setProperty(HQConstants.HQGUID, hqGUID);
            try {
                setConfig(authzSubjectManager.getOverlordPojo(), props);
            } catch (Exception e) {
                throw new SystemException(e);
            }
        }
        log.info("Generating a new HQ-GUID [" + hqGUID + "]");

        return hqGUID;
    }

    /**
     * Save the HQ-QUID value in server configuration table
     * 
     * @param guidValue HQ-GUID
     */
    @Transactional
    private synchronized void saveGUID(String guidValue, ConfigProperty configProp) {
        // Fix Bug 1285064 / HQ-4793: Save the property in DB

        configProp.setValue(guidValue);
        configPropertyDAO.save(configProp);
        configPropertyDAO.flushSession();

        if (log.isDebugEnabled())
            log.debug("Saving the GUID in DB [" + guidValue + "]");
    }

    /**
     * Get all the {@link ConfigProperty}s
     * 
     */
    @Transactional(readOnly = true)
    public Collection<ConfigProperty> getConfigProperties() {

        return configPropertyDAO.findAll();
    }

    /**
     * Gets the GUID for this HQ server instance. The GUID is persistent for the
     * duration of an HQ install and is created upon the first call of this
     * method. If for some reason it can't be determined, 'unknown' will be
     * returned.
     * 
     * 
     */
    @Transactional(readOnly = true)
    public String getGUID() {
        Properties p;
        String res;

        try {
            p = getConfig();
        } catch (Exception e) {
            throw new SystemException(e);
        }

        res = p.getProperty(HQConstants.HQGUID);
        if (res == null || res.trim().length() == 0) {
            // Fix bug 1285064 / HQ-4793: generate new GUID and save it to cache and DB
            res = generateNewGUID(p);
        }

        if (log.isDebugEnabled())
            log.debug("HQ-GUID value is [" + res + "]");

        return res;
    }

    @Transactional(readOnly = true)
    public Properties getConfig() throws ConfigPropertyException {
        return serverConfigCache.getConfig();
    }

    @Transactional(readOnly = true)
    public Properties getConfig(String prefix) throws ConfigPropertyException {
        return serverConfigCache.getConfig(prefix);
    }

    @Transactional(readOnly = true)
    public String getPropertyValue(String name) {
        return serverConfigCache.getProperty(name);
    }

    /**
     * 
     * @return major part of the server version - x.x or x.x.x. 
     * If pattern fails to match - returns the full server version.
     */
    @Transactional(readOnly = true)
    public String getServerMajorVersion() {
        String serverVersion = serverConfigCache.getProperty(HQConstants.ServerVersion);
        return extractMajorVersion(serverVersion);
    }

    private String extractMajorVersion(String version) {
        String majorVersion = version;
        Pattern regex = Pattern.compile("^(\\d+)(.\\d+.\\d+)?");
        Matcher regexMatcher = regex.matcher(version);
        if (regexMatcher.find() && (regexMatcher.groupCount() > 0)) {
            majorVersion = regexMatcher.group(1); // just the first digits before .
                                                  // group(0) is the whole expression
        }
        return majorVersion;
    }

}