org.hyperic.hq.product.JDBCQueryCache.java Source code

Java tutorial

Introduction

Here is the source code for org.hyperic.hq.product.JDBCQueryCache.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.product;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.util.jdbc.DBUtil;

/**
 * JDBCQueryCache is a simple caching mechanism to be used with
 * JDBCMeasurementPlugins.
 * 
 * example:
 * 
 * mysql> select Value, Default from sys_table;
 * +-----------------------------------+----------+---------+
 * | Variable_name                     | Value    | Default |
 * +-----------------------------------+----------+---------+
 * | DB_Max_Memory                     | 8192     | 1024    |
 * | DB_Max_Connections                | 400      | 10      |
 * ...
 * ...
 * 
 * <code>
 * String query = "select Value, Default from sys_table";
 * JDBCQueryCache cache = new JDBCQueryCache(query, "Variable_name", 5000);
 * Double val = Double.valueOf(cache.get("DB_Max_Memory", "Value").toString());
 * val = Double.valueOf(cache.get("DB_Max_Connections", "Default").toString());
 * </code>
 */
public class JDBCQueryCache {
    private static final String _logCtx = JDBCQueryCache.class.getName();
    private final Log _log = LogFactory.getLog(_logCtx);
    private final Map _cache = new HashMap();
    private final String _query, _queryKey;
    private long _last = -1l;
    private final long _cacheTimeout;

    public JDBCQueryCache(String query, String queryKey, long cacheTimeout) {
        _query = query;
        _queryKey = queryKey;
        _cacheTimeout = cacheTimeout;
    }

    /**
     * Explicitly clears any cached value
     */
    public void clearCache() {
        _cache.clear();
    }

    /**
     * Explicitly sets the expire time of the cache to expireTime.  Cache will
     * not repopulate until System.currentTimeMillis() <= expireTime.
     */
    public void setExpireTime(long expireTime) {
        _last = expireTime - _cacheTimeout;
    }

    /**
     * @return Object representation of the *only* row and column value
     *  or null if it does not exist
     * @throws JDBCQueryCacheException if there are 0 or > 1 rows in the cache.
     */
    public Object getOnlyRow(Connection conn, String column) throws SQLException, JDBCQueryCacheException {
        final long now = System.currentTimeMillis();
        if (_cache.isEmpty() || (now - _cacheTimeout) > _last) {
            repopulateCache(conn);
        }
        final Set keys = _cache.keySet();
        if (keys.size() > 1) {
            throw new JDBCQueryCacheException("cache contains more than one row");
        } else if (keys.size() <= 0) {
            throw new JDBCQueryCacheException("cache does not contain any results");
        }
        List list = null;
        for (final Iterator it = _cache.entrySet().iterator(); it.hasNext();) {
            final Map.Entry entry = (Map.Entry) it.next();
            list = (List) entry.getValue();
            break;
        }
        if (list == null) {
            return null;
        }
        for (final Iterator it = list.iterator(); it.hasNext();) {
            final NameValuePair pair = (NameValuePair) it.next();
            if (pair.getName().equals(column)) {
                return pair.getValue();
            }
        }
        throw new JDBCQueryCacheException("column " + column + " not found.");
    }

    /**
     * @return Object representation of the row key/column value or null if it 
     * does not exist
     */
    public Object get(Connection conn, String key, String column) throws JDBCQueryCacheException, SQLException {
        long now = System.currentTimeMillis();
        if (_cache.isEmpty() || (now - _cacheTimeout) > _last) {
            repopulateCache(conn);
        }
        List list = (List) _cache.get(key);
        if (list == null) {
            return null;
        }
        for (Iterator it = list.iterator(); it.hasNext();) {
            NameValuePair pair = (NameValuePair) it.next();
            if (pair.getName().equalsIgnoreCase(column)) {
                return pair.getValue();
            }
        }
        throw new JDBCQueryCacheException("key " + key + ", column " + column + " not found.");
    }

    private void repopulateCache(Connection conn) throws SQLException, JDBCQueryCacheException {
        Statement stmt = null;
        ResultSet rs = null;
        if (_log.isDebugEnabled()) {
            final String msg = "re-populating JDBCQueryCache for " + _query + " with queryKey of " + _queryKey;
            _log.debug(msg);
        }
        try {
            _cache.clear();
            stmt = conn.createStatement();
            rs = stmt.executeQuery(_query);
            final List columns = getQueryColumns(rs);
            final boolean debug = _log.isDebugEnabled();
            while (rs.next()) {
                int i = 1;
                final List vals = new ArrayList();
                String key = null;
                for (final Iterator it = columns.iterator(); it.hasNext(); i++) {
                    final String column = (String) it.next();
                    if (column.equalsIgnoreCase(_queryKey)) {
                        key = rs.getString(i);
                    } else {
                        String tmp = rs.getString(i);
                        tmp = (rs.wasNull()) ? null : tmp;
                        if (debug) {
                            _log.debug("adding nameValuePair=" + column + "/" + tmp);
                        }
                        vals.add(new NameValuePair(column, tmp));
                    }
                }
                if (key == null) {
                    throw new JDBCQueryCacheException(
                            "queryKey, " + _queryKey + " was not represented in the query");
                }
                if (debug)
                    _log.debug("adding key=" + key);
                _cache.put(key, vals);
            }
        } catch (SQLException e) {
            throw e;
        } finally {
            DBUtil.closeJDBCObjects(_logCtx, null, stmt, rs);
            // even if a failure occurs we want to set the _last update time.
            // this will alleviate the situation where there are a bunch
            // of subsequent failures.  These failures could potentially hold
            // up the ScheduleThread
            _last = System.currentTimeMillis();
        }
    }

    private List getQueryColumns(ResultSet rs) throws SQLException {
        ResultSetMetaData rsmd = rs.getMetaData();
        int size = rsmd.getColumnCount();
        List rtn = new ArrayList(size);
        for (int i = 1; i <= size; i++) {
            rtn.add(rsmd.getColumnLabel(i));
        }
        return rtn;
    }

    private class NameValuePair {
        private final String _name;
        private final Object _value;

        public NameValuePair(String name, Object value) {
            _name = name;
            _value = value;
        }

        public String getName() {
            return _name;
        }

        public Object getValue() {
            return _value;
        }
    }
}