You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by ec...@apache.org on 2008/01/28 06:42:26 UTC

svn commit: r615733 [2/4] - in /geronimo/server/trunk/plugins/monitoring: agent-jar/ agent-jar/src/main/java/org/apache/geronimo/monitoring/ agent-jar/src/main/java/org/apache/geronimo/monitoring/snapshot/ mconsole-war/src/main/webapp/WEB-INF/view/

Modified: geronimo/server/trunk/plugins/monitoring/agent-jar/src/main/java/org/apache/geronimo/monitoring/snapshot/SnapshotDBHelper.java
URL: http://svn.apache.org/viewvc/geronimo/server/trunk/plugins/monitoring/agent-jar/src/main/java/org/apache/geronimo/monitoring/snapshot/SnapshotDBHelper.java?rev=615733&r1=615732&r2=615733&view=diff
==============================================================================
--- geronimo/server/trunk/plugins/monitoring/agent-jar/src/main/java/org/apache/geronimo/monitoring/snapshot/SnapshotDBHelper.java (original)
+++ geronimo/server/trunk/plugins/monitoring/agent-jar/src/main/java/org/apache/geronimo/monitoring/snapshot/SnapshotDBHelper.java Sun Jan 27 21:42:24 2008
@@ -1,702 +1,702 @@
-/**
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You under the Apache License, Version 2.0
- *  (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package org.apache.geronimo.monitoring.snapshot;
-
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.ResultSet;
-import java.sql.Statement;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.TreeMap;
-
-import javax.sql.DataSource;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-public class SnapshotDBHelper {
-    private static Log log = LogFactory.getLog(SnapshotDBHelper.class);
-    // field attributes for the Statistics table in the DB
-    private static final String SNAPSHOT_TIME = "snapshot_time";
-    private static final String MBEANNAME = "mbeanName";
-    private static final String STATSVALUELIST = "statsValueList";
-    private static final String STATSNAMELIST = "statsNameList";
-    // Connection object used for DB interaction
-    private static Connection conn = null;
-    // Data Sources
-    private static DataSource activeDS = null;
-    private static DataSource archiveDS = null;
-    
-    public SnapshotDBHelper() {
-        
-    }
-    
-    public SnapshotDBHelper(DataSource activeDS, DataSource archiveDS) {
-        SnapshotDBHelper.activeDS = activeDS;
-        SnapshotDBHelper.archiveDS = archiveDS;
-    }
-    
-    /**
-     * @return A map: mbeanName --> ArrayList of statistic attributes for that mbean
-     */
-    public HashMap<String, ArrayList<String>> getAllSnapshotStatAttributes() {
-        openActiveConnection();
-        HashMap<String, ArrayList<String>> retval = new HashMap<String, ArrayList<String>>();
-        try {
-            Statement stmt = conn.createStatement();
-            String query = "SELECT DISTINCT mbeanName, statsNameList FROM MBeans";
-            ResultSet rs = stmt.executeQuery(query);
-            // add each mbean/statsValue combination to retval
-            while(rs.next()) {
-                String mbeanName = rs.getString(MBEANNAME);
-                String statsNameStr = rs.getString(STATSNAMELIST);
-                String[] statsNameList = statsNameStr.split(",");
-                ArrayList<String> mbeanAttributeList = new ArrayList<String>();
-                // copy from String[] to ArrayList<String>
-                for(int i = 0; i < statsNameList.length; i++) {
-                    mbeanAttributeList.add(statsNameList[i]);
-                }
-                retval.put(mbeanName, mbeanAttributeList);
-            }
-        } catch(Exception e) {
-            log.error(e.getMessage(), e);
-        } finally {
-            closeConnection();
-        }
-        return retval;
-    }
-    
-    /**
-     * 
-     * @return The number of snapshots present in the active database
-     */
-    public Long getSnapshotCount() {
-        long retval = 0;
-        try {
-            openActiveConnection();
-            Statement stmt = conn.createStatement();
-            String query = "SELECT COUNT(DISTINCT snapshot_time) FROM Statistics";
-            ResultSet rs = stmt.executeQuery(query);
-            rs.next();
-            retval = rs.getLong(1);
-        } catch(Exception e) {
-            log.error(e.getMessage(), e);
-        } finally {
-            closeConnection();
-        }
-        return new Long(retval);
-    }
-    
-    /**
-     * @param numberOfSnapshots - the number of latest snapshots to look at
-     * @return A hashmap which maps an mbean --> a hashmap with an attribute name and its value . All values will be the max.
-     */
-    public HashMap<String, HashMap<String, Long>> fetchMaxSnapshotData(Integer numberOfSnapshots) {
-        return fetchMaxOrMinSnapshotData(numberOfSnapshots, true);
-    }
-    /**
-     * @param numberOfSnapshots - the number of latest snapshots to look at
-     * @return A hashmap which maps an mbean --> a hashmap with an attribute name and its value . All values will be the min.
-     */    
-    public HashMap<String, HashMap<String, Long>> fetchMinSnapshotData(Integer numberOfSnapshots) {
-        return fetchMaxOrMinSnapshotData(numberOfSnapshots, false);
-    }
-    
-    /**
-     * @param numberOfSnapshots - the number of latest snapshots to look at.
-     * @param isMax - true if the result should be all maximum values. otherwise, false.
-     * @return A hashmap which maps an mbean --> a hashmap with an attribute name and its value . All values will be the min
-     * or max, depending on the isMax parameter.
-     */
-    private HashMap<String, HashMap<String, Long>> fetchMaxOrMinSnapshotData(Integer numberOfSnapshots, boolean isMax) {
-        openActiveConnection();
-        ResultSet snapshotTimeTable = fetchSnapshotTimesFromDB();
-        HashMap<String, HashMap<String, Long>> stats = new HashMap<String, HashMap<String, Long>>();
-        try {
-            // for each snapshot in the table
-            while(snapshotTimeTable.next()) {
-                Long snapshotTime = snapshotTimeTable.getLong(SNAPSHOT_TIME);
-                // retrieve the snapshot information by time
-                ResultSet snapshotData = fetchSnapshotDataFromDB(snapshotTime);
-                // for each statistic, perform a relaxation
-                while(snapshotData.next()) {
-                    String mbean = snapshotData.getString(MBEANNAME);
-                    // get map associated with mbean
-                    HashMap<String, Long> mbeanMap = stats.get(mbean);
-                    if(mbeanMap == null) {
-                        mbeanMap = new HashMap<String, Long>();
-                    }
-                    String[] statsNameList = snapshotData.getString(STATSNAMELIST).split(",");
-                    String[] statsValueList = snapshotData.getString(STATSVALUELIST).split(",");
-                    assert(statsNameList.length == statsValueList.length);
-                    // for each statname/statsvalue combo in an mbean
-                    for(int i = 0 ; i < statsNameList.length; i++) {
-                        String statsName = statsNameList[i];
-                        Long maxStatsValue = mbeanMap.get(statsName);
-                        // give maxStatsValue some value if there isn't one
-                        if(maxStatsValue == null) {
-                            if(isMax) {
-                                maxStatsValue = new Long(0);
-                            } else {
-                                maxStatsValue = Long.MAX_VALUE;
-                            }
-                        }
-                        // relax
-                        if(isMax) {
-                            maxStatsValue = new Long(Math.max(Long.parseLong(statsValueList[i]), maxStatsValue.longValue()));
-                        } else {
-                            maxStatsValue = new Long(Math.min(Long.parseLong(statsValueList[i]), maxStatsValue.longValue()));
-                        }
-                        // save name/value back into mbeanMap
-                        mbeanMap.put(statsName, maxStatsValue);
-                    }
-                    // save mbeanMap back into stats
-                    stats.put(mbean, mbeanMap);
-                }
-                
-                // compute the remaining snapshots left to look at
-                numberOfSnapshots--;
-                // discontinue once we have looked at numberOfSnapshots snapshots
-                if(numberOfSnapshots == 0) {
-                    break;
-                }
-            }
-        } catch(Exception e) {
-            log.error(e.getMessage(), e);
-        } finally {
-            closeConnection();
-        }
-        return stats;
-    }
-    
-    /**
-     * @param ds
-     * @param aggregateStats
-     * @return Returns a boolean if the snapshot statistics were successfully added
-     * to the DB.
-     */
-    public boolean addSnapshotToDB(HashMap<String, HashMap<String, Long>> aggregateStats) {
-        boolean success = true;
-        // get the current time from 1970
-        String currTime = "";
-        currTime += (new Date()).getTime();
-        try {
-            // for each mbean
-            for(Iterator itt = aggregateStats.keySet().iterator(); itt.hasNext(); ) {
-                String mbean = (String)itt.next();
-                // prepare the statsNameList and statsValueList beforehand
-                String statsNameList = "", statsValueList = "";
-                for(Iterator<String> it = aggregateStats.get(mbean).keySet().iterator(); it.hasNext(); ) {
-                    String statsName = it.next();
-                    Long statsValue = aggregateStats.get(mbean).get(statsName);
-                    if(statsNameList.length() == 0 || statsValueList.length() == 0) {
-                        // do not add a comma because this is the first occurrence
-                        statsValueList += statsValue.toString();
-                        statsNameList += statsName;
-                    } else {
-                        // add a comma
-                        statsValueList += "," + statsValue.toString();
-                        statsNameList += "," + statsName;
-                    }
-                }
-                
-                // start talking to DB
-                openActiveConnection();
-                Statement stmt = conn.createStatement();
-                HashMap stats = aggregateStats.get(mbean);
-                //--------Ensure MBeans are in place
-                int mbeanId = getMBeanId(mbean); 
-                if(mbeanId != -1) {
-                    // mbean already exists in the db
-                } else {
-                    // doesn't exist in the db so add it
-                    // add mbean record to the db
-                    stmt.executeUpdate("INSERT INTO MBeans (mbeanName, statsNameList) VALUES ("+ surroundWithQuotes(mbean) + "," + surroundWithQuotes(statsNameList) + ")");
-                    mbeanId = getMBeanId(mbean);
-                }
-                
-                // insert the statistics into Statistics table
-                stmt.executeUpdate( prepareInsertSnapshotStatement(currTime, statsValueList, mbeanId) );
-                closeConnection();
-            }
-        } catch(Exception  e){
-            log.error(e.getMessage(), e);
-            success = false;
-        } finally {
-            closeConnection();
-        }
-        
-        // go through the archiving process
-        try {
-            int retentionDays = Integer.parseInt(SnapshotConfigXMLBuilder.getAttributeValue("retention"));
-            long retentionMillis = (long)(retentionDays) * 86400000; // convert from days to milliseconds
-            archiveSnapshots( Long.parseLong(currTime) - retentionMillis );
-        } catch(Exception e) {
-            log.warn("Cannot archive snapshots because attribute 'retention' is not present in snapshot-config.xml.");
-        }
-        return success;
-    }
-    
-    /**
-     * Moves records from the ActiveDB to the ArchiveDB. The records that are moved
-     * are those whose snapshot_times exceed the retention period
-     * @param cutOffTime - in milliseconds
-     */
-    private void archiveSnapshots(long cutOffTime) {
-        // for each successful update of Snapshots/Statistics table
-        // increment or decrement these counters to ensure that nothing is being 
-        // lost in between. If these counters are non-zero, some records have been
-        // lost.
-        int snapshotsOver = 0;
-        int statisticsOver = 0;
-        try {
-            openActiveConnection();
-            ResultSet overDueSnapshotTimes = getOverDueSnapshotTimes(cutOffTime);
-            ArrayList<Long> overdueTimes = new ArrayList<Long>();
-            // save overdue times into an array list for later usage
-            while(overDueSnapshotTimes.next()) {
-                overdueTimes.add(overDueSnapshotTimes.getLong(SNAPSHOT_TIME));
-            }
-            closeConnection();
-            // for each overdue snapshot time
-            // -transfer all records associated with that snaphot_time to ArchiveDB
-            for(int i = 0; i < overdueTimes.size(); i++) {
-                long snapshotTime = overdueTimes.get(i);
-                openActiveConnection();
-                ResultSet rsSnapshotData = fetchSnapshotDataFromDB(new Long(snapshotTime));
-                HashMap<String, HashMap<String, Long>> snapshotData = new HashMap<String,HashMap<String, Long>>();
-                while(rsSnapshotData.next()) {
-                    // extract values from sql table
-                    String mbeanName = rsSnapshotData.getString(MBEANNAME);
-                    String statsNameList = rsSnapshotData.getString(STATSNAMELIST);
-                    String statsValueList = rsSnapshotData.getString(STATSVALUELIST);
-                    Long snapshot_time = rsSnapshotData.getLong(SNAPSHOT_TIME);
-                    // get a connection to the archive db too
-                    Connection archiveConn = archiveDS.getConnection();
-                    Statement archiveStmt = archiveConn.createStatement();
-                    //--------Ensure MBeans are in place
-                    int mbeanId = getMBeanIdFromArchive(mbeanName); 
-                    if(mbeanId != -1) {
-                        // mbean already exists in the db
-                    } else {
-                        // doesn't exist in the db so add it
-                        // add mbean record to the db
-                        archiveStmt.executeUpdate("INSERT INTO MBeans (mbeanName, statsNameList) VALUES ("+ surroundWithQuotes(mbeanName) + ", " + surroundWithQuotes(statsNameList) + ")");
-                        mbeanId = getMBeanIdFromArchive(mbeanName);
-                    }
-                    // ensure Statistics table has record of mbeanId, snapshotId, statsValue, statsName
-                    String updateStr = prepareInsertSnapshotStatement(snapshot_time + "", statsValueList, mbeanId);
-                    statisticsOver += archiveStmt.executeUpdate( updateStr );
-                    // close connection to archiveDB
-                    archiveConn.close();
-                }
-                closeConnection();
-            }
-            // for each snapshot time, remove all instances that is associated with in 
-            // in the active DB
-            for(int i = 0; i < overdueTimes.size(); i++) {
-                long snapshotTime = overdueTimes.get(i);
-                openActiveConnection();
-                Statement stmt = conn.createStatement();
-                // remove from Statistics table
-                String statisticsUpdate = "DELETE FROM Statistics WHERE snapshot_time=" + snapshotTime;
-                statisticsOver -= stmt.executeUpdate(statisticsUpdate);
-                closeConnection();
-            }
-        } catch(Exception e) {
-            log.error(e.getMessage(), e);
-        } finally {
-            closeConnection();
-        }
-        
-        // ensure that the transferring was good
-        if(snapshotsOver != 0) {
-            log.warn("Transferred snapshots was completed, but some things were lost.");
-        }
-        if(statisticsOver != 0) {
-            log.warn("Transferred statistics was completed, but some things were lost.");
-        }
-    }
-    
-    /**
-     * @param cutOffTime
-     * @return An SQL table contain a column of all the times that did not make the cutOffTime.
-     */
-    private ResultSet getOverDueSnapshotTimes(long cutOffTime) {
-        try {
-            Statement stmt = conn.createStatement();
-            String query = "SELECT DISTINCT snapshot_time FROM Statistics WHERE snapshot_time < " + cutOffTime;
-            return stmt.executeQuery(query);
-        } catch(Exception e) {
-            log.error(e.getMessage(), e);
-        }
-        return null;
-    }
-    
-    /**
-     * @param mbean
-     * @return The mbean id of the mbean from table ArchiveDB.MBean. Returns -1 if record does not exist.
-     */
-    private int getMBeanIdFromArchive(String mbean) throws Exception {
-        int retval = -1;
-        Connection archiveConn = archiveDS.getConnection();
-        Statement stmt = archiveConn.createStatement();
-        ResultSet rs = stmt.executeQuery("SELECT id FROM MBeans WHERE mbeanName=" + surroundWithQuotes(mbean));
-        if(rs.next()) {
-            retval = rs.getInt("id");
-        }
-        stmt.close();
-        archiveConn.close();
-        return retval;
-    }
-    
-    /**
-     * @param mbean
-     * @return The mbean id of the mbean from table ActiveDB.MBean. Returns -1 if record does not exist.
-     */
-    private int getMBeanId(String mbean) throws Exception {
-        int retval = -1;
-        Connection conn = activeDS.getConnection();
-        Statement stmt = conn.createStatement();
-        ResultSet rs = stmt.executeQuery("SELECT id FROM MBeans WHERE mbeanName=" + surroundWithQuotes(mbean));
-        if(rs.next()) {
-            retval = rs.getInt("id");
-        }
-        stmt.close();
-        conn.close();
-        return retval;
-    }
-    
-    /**
-     * @param snapshot_time
-     * @param statsValueList
-     * @param mbeanId
-     * @return Returns an SQL insert statement for one statistic given the correct information.
-     */
-    public String prepareInsertSnapshotStatement(String snapshot_time, String statsValueList, int mbeanId) {
-        String retval = "INSERT INTO Statistics (snapshot_time, statsValueList, mbeanId) VALUES (";
-        retval += snapshot_time;
-        retval += ",";
-        retval += surroundWithQuotes(statsValueList);
-        retval += ",";
-        retval += mbeanId;
-        retval += ")";
-        return retval;
-    }
-
-    /**
-     * @param s
-     * @return A String with ' at the beginning and end
-     */
-    private String surroundWithQuotes(String s) {
-        return "'" + s.trim() + "'";
-    }
-    
-    /**
-     * Fetches the data stored from the snapshot thread and returns
-     * it in a ArrayList with each element being a HashMap of the attribute
-     * mapping to the statistic. Grabs 'numberOfSnapshots' snapshots. Grabs 
-     * one snapshot per 'everyNthsnapshot'
-     * 
-     * @param numberOfSnapshot
-     * @param everyNthSnapshot
-     * @return ArrayList
-     */ 
-    public ArrayList<HashMap<String, HashMap<String, Object>>> fetchData(Integer numberOfSnapshot, 
-                                                                                Integer everyNthSnapshot) {
-        ArrayList<HashMap<String, HashMap<String, Object>>> stats = new ArrayList<HashMap<String, HashMap<String, Object>>>();
-        openActiveConnection();
-        // get all records in the database grouped and ordered by time
-        ResultSet table = fetchSnapshotTimesFromDB();
-        // iterate through the table and finds the times (which uniquely IDs a snapshot)
-        // that are wanted and queries the rest of the DB using the time as the condition
-        // (i.e. the ones that are in the c*n-th snapshot where c <= numberOfSnapshot
-        // and n == everyNthSnapshot)
-        int nthSnapshot = 0;
-        try {
-            while(table.next()) {
-                Long snapshotTime = table.getLong(SNAPSHOT_TIME);
-                // grab 0*nth, 1*nth, 2*nth snapshot up to min(numberOfSnapshot*everyNthSnapshot, size of the table)
-                if(nthSnapshot % everyNthSnapshot == 0) {
-                    HashMap<String, HashMap<String, Object>> snapshotData = packageSnapshotData(snapshotTime);
-                    stats.add( 0, snapshotData );
-                    numberOfSnapshot--;
-                }
-                nthSnapshot++;
-                // no more snapshots needs to be looked at, we have successfully acquired our goal
-                if(numberOfSnapshot == 0) {
-                    break;
-                }
-            }
-        } catch(Exception e) {
-            log.error(e.getMessage(), e);
-        } finally {
-            closeConnection();
-        }
-        return stats;
-    }
-    
-    /**
-     * @param snapshotTime
-     * @return A hashmap in the form <String1, HashMap> where String1 is the mbean name
-     * and HashMap is a map containing a <String2, Object> where String2 is an attribute name
-     * and Object is the value. Additionally, if String is "times" then it maps to a HashMap
-     * containing snapshot_time and snapshot_date information.
-     */
-    private HashMap<String, HashMap<String, Object>> packageSnapshotData(Long snapshotTime) {
-        HashMap<String, HashMap<String, Object>> snapshotPkg = new HashMap<String, HashMap<String, Object>>();
-        openActiveConnection();
-        ResultSet snapshotData = fetchSnapshotDataFromDB(snapshotTime);
-        try {
-            // for each record save it somewhere in the snapshotPkg
-            while(snapshotData.next()) {
-                String currMBean = snapshotData.getString(MBEANNAME);
-                // get the information for the mbean defined by currMBean
-                HashMap<String, Object> mbeanInfo = snapshotPkg.get(currMBean);
-                if(mbeanInfo == null) {
-                    mbeanInfo = new HashMap<String, Object>();
-                }
-                // get statistics from resultset
-                String statsValueStr = snapshotData.getString(STATSVALUELIST);
-                String statsNameStr = snapshotData.getString(STATSNAMELIST);
-                String[] statsValueList = statsValueStr.split(",");
-                String[] statsNameList = statsNameStr.split(",");
-                assert(statsValueList.length == statsNameList.length);
-                // for each statsValue/statsName, save it
-                for(int i = 0 ; i < statsValueList.length; i++) {
-                    long statValue = Long.parseLong(statsValueList[i]);
-                    mbeanInfo.put(statsNameList[i], new Long(statValue));
-                }
-                // save the hashmap into the snapshotpkg
-                snapshotPkg.put(currMBean, mbeanInfo);
-            }
-        } catch(Exception e) {
-            log.error(e.getMessage(), e);
-        } finally {
-            closeConnection();
-        }
-        // add the time and date
-        HashMap<String, Object> timeMap = new HashMap<String, Object>();
-        timeMap.put(SNAPSHOT_TIME, snapshotTime);
-        snapshotPkg.put("times", timeMap);
-        
-        return snapshotPkg;
-    }
-    
-    /**
-     * @param snapshotTime
-     * @return Returns a ResultSet with all statistic information that matches the snapshot_time.
-     */
-    private ResultSet fetchSnapshotDataFromDB(Long snapshotTime) {
-        String query = "SELECT S.statsValueList AS statsValueList, M.statsNameList AS statsNameList, S.snapshot_time AS snapshot_time, M.mbeanName AS mbeanName FROM Statistics S, MBeans M WHERE S.snapshot_time=" + snapshotTime;
-        query += " AND S.mbeanId=M.id";
-        ResultSet retval = null;
-        try {
-            if(conn.isClosed()) {
-                openActiveConnection();
-            }
-            Statement stmt = conn.createStatement();
-            retval = stmt.executeQuery(query);
-        } catch(Exception e) {
-            log.error(e.getMessage(), e);
-        }
-        return retval;
-    }
-
-    /**
-     * @return Returns a ResultSet with one column (snapshot_time) sorted in descending order
-     */
-    private ResultSet fetchSnapshotTimesFromDB() {
-        String query = "SELECT DISTINCT snapshot_time FROM Statistics ORDER BY snapshot_time DESC";
-        ResultSet retval = null;
-        try {
-            if(conn.isClosed()) {
-                openActiveConnection();
-            }
-            Statement stmt = conn.createStatement();
-            retval = stmt.executeQuery(query);
-        } catch(Exception e) {
-            log.error(e.getMessage(), e);
-        }
-        return retval;
-    }
-    
-    /**
-     * Opens the global connection to activeDB
-     */
-    private void openActiveConnection() {
-        try {
-            conn = activeDS.getConnection();
-        } catch(Exception e) {
-            log.error(e.getMessage(), e);
-        }
-    }
-    
-    /**
-     * Opens the global connection to archiveDB
-     */
-    private void openArchiveConnection() {
-        try {
-            conn = archiveDS.getConnection();
-        } catch(Exception e) {
-            log.error(e.getMessage(), e);
-        }
-    }
-    
-    /**
-     * Closes the global connection to a DB
-     */
-    private void closeConnection() {
-        if(conn != null) {
-            try {
-                conn.close();
-            } catch(Exception e) {
-                log.error(e.getMessage(), e);
-            }
-        }
-    }
-    
-    /**
-     * @param mbeanName
-     * @param statsName
-     * @param numberOfSnapshots
-     * @param everyNthSnapshot
-     * @return HashMap which maps from a snapshot_time --> value of the mbean.statsName at that time
-     */
-    public TreeMap<Long, Long> getSpecificStatistics(    String mbeanName, 
-                                                                String statsName, 
-                                                                int numberOfSnapshots, 
-                                                                int everyNthSnapshot, 
-                                                                boolean showArchived) {
-        openActiveConnection();
-        TreeMap<Long, Long> stats = new TreeMap<Long, Long>();
-        int nthSnapshot = 0;
-        // attempt to get as many snapshots from the active db as possible
-        try {
-            Statement stmt = conn.createStatement();
-            int mbeanId = getMBeanId(mbeanName);
-            if(mbeanId == -1) {
-                log.error(mbeanName + " does not exist in the database.");
-            } else {
-                String query = "SELECT DISTINCT snapshot_time, statsValueList, statsNameList FROM Statistics, MBeans M WHERE mbeanId=" + mbeanId + " AND mbeanId=M.id ORDER BY snapshot_time DESC";
-                ResultSet rs = stmt.executeQuery(query);
-                // iterate through the table paying attention to those at everyNthSnapshot-th position
-                while(rs.next()) {
-                    // every nth snapshot I save the information into my returning hashmap
-                    if(nthSnapshot % everyNthSnapshot == 0) {
-                        String[] statsValueList = rs.getString(STATSVALUELIST).split(",");
-                        String[] statsNameList = rs.getString(STATSNAMELIST).split(",");
-                        assert(statsValueList.length == statsNameList.length);
-                        Long statsValue = null;
-                        for(int i = 0 ; i < statsNameList.length; i++) {
-                            if(statsNameList[i].equals(statsName)) {
-                                long value = Long.parseLong(statsValueList[i]);
-                                statsValue = new Long(value);
-                            }
-                        }
-                        // exit function after error
-                        if(statsValue == null) {
-                            log.warn("Statistics name '" + statsName + "' does not exist");
-                            return stats;
-                        } else {
-                            stats.put(rs.getLong(SNAPSHOT_TIME), statsValue);
-                            numberOfSnapshots--;
-                        }
-                    }
-                    // update counter
-                    nthSnapshot++;
-                    // enough data, end this thing
-                    if(numberOfSnapshots == 0) {
-                        break;
-                    }
-                }
-            }
-        } catch(Exception e) {
-            log.error(e.getMessage(), e);
-        } finally {
-            closeConnection();
-        }
-
-        nthSnapshot = 0;
-
-        // attempt to get the remaining snapshots requested from the archive DB
-        // iff the showArchive flag is set
-        if(showArchived && numberOfSnapshots != 0) {
-            try {
-                openArchiveConnection();    // connection to the Archive DB
-                Statement stmt = conn.createStatement();
-                int mbeanId = getMBeanId(mbeanName);
-                if(mbeanId == -1) {
-                    log.error(mbeanName + " does not exist in the database.");
-                } else {
-                    String query = "SELECT DISTINCT snapshot_time, statsValueList, statsNameList FROM Statistics, MBeans M WHERE mbeanId=" + mbeanId + " AND mbeanId=M.id ORDER BY snapshot_time DESC";
-                    ResultSet rs = stmt.executeQuery(query);
-                    // iterate through the table paying attention to those at everyNthSnapshot-th position
-                    while(rs.next()) {
-                        // every nth snapshot I save the information into my returning hashmap
-                        if(nthSnapshot % everyNthSnapshot == 0) {
-                            String[] statsValueList = rs.getString(STATSVALUELIST).split(",");
-                            String[] statsNameList = rs.getString(STATSNAMELIST).split(",");
-                            assert(statsValueList.length == statsNameList.length);
-                            Long statsValue = null;
-                            for(int i = 0 ; i < statsNameList.length; i++) {
-                                if(statsNameList[i].equals(statsName)) {
-                                    long value = Long.parseLong(statsValueList[i]);
-                                    statsValue = new Long(value);
-                                }
-                            }
-                            // exit function after error
-                            if(statsValue == null) {
-                                log.warn("Statistics name '" + statsName + "' does not exist");
-                                return stats;
-                            } else {
-                                stats.put(rs.getLong(SNAPSHOT_TIME), statsValue);
-                                numberOfSnapshots--;
-                            }
-                        }
-                        // update counter
-                        nthSnapshot++;
-                        // enough data, end this thing
-                        if(numberOfSnapshots == 0) {
-                            break;
-                        }
-                    }
-                }
-            } catch(Exception e) {
-                log.error(e.getMessage(), e);
-            } finally {
-                closeConnection();
-            }
-        }
-        return stats;
-    }
-    
-    /**
-     * Sets the necessary data sources for this helper to talk to the db
-     * @param activeDS
-     * @param archiveDS
-     */
-    public void setDataSources(DataSource active, DataSource archive) {
-        activeDS = active;
-        archiveDS = archive;
-    }
-}
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.monitoring.snapshot;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.TreeMap;
+
+import javax.sql.DataSource;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class SnapshotDBHelper {
+    private static Log log = LogFactory.getLog(SnapshotDBHelper.class);
+    // field attributes for the Statistics table in the DB
+    private static final String SNAPSHOT_TIME = "snapshot_time";
+    private static final String MBEANNAME = "mbeanName";
+    private static final String STATSVALUELIST = "statsValueList";
+    private static final String STATSNAMELIST = "statsNameList";
+    // Connection object used for DB interaction
+    private static Connection conn = null;
+    // Data Sources
+    private static DataSource activeDS = null;
+    private static DataSource archiveDS = null;
+    
+    public SnapshotDBHelper() {
+        
+    }
+    
+    public SnapshotDBHelper(DataSource activeDS, DataSource archiveDS) {
+        SnapshotDBHelper.activeDS = activeDS;
+        SnapshotDBHelper.archiveDS = archiveDS;
+    }
+    
+    /**
+     * @return A map: mbeanName --> ArrayList of statistic attributes for that mbean
+     */
+    public HashMap<String, ArrayList<String>> getAllSnapshotStatAttributes() {
+        openActiveConnection();
+        HashMap<String, ArrayList<String>> retval = new HashMap<String, ArrayList<String>>();
+        try {
+            Statement stmt = conn.createStatement();
+            String query = "SELECT DISTINCT mbeanName, statsNameList FROM MBeans";
+            ResultSet rs = stmt.executeQuery(query);
+            // add each mbean/statsValue combination to retval
+            while(rs.next()) {
+                String mbeanName = rs.getString(MBEANNAME);
+                String statsNameStr = rs.getString(STATSNAMELIST);
+                String[] statsNameList = statsNameStr.split(",");
+                ArrayList<String> mbeanAttributeList = new ArrayList<String>();
+                // copy from String[] to ArrayList<String>
+                for(int i = 0; i < statsNameList.length; i++) {
+                    mbeanAttributeList.add(statsNameList[i]);
+                }
+                retval.put(mbeanName, mbeanAttributeList);
+            }
+        } catch(Exception e) {
+            log.error(e.getMessage(), e);
+        } finally {
+            closeConnection();
+        }
+        return retval;
+    }
+    
+    /**
+     * 
+     * @return The number of snapshots present in the active database
+     */
+    public Long getSnapshotCount() {
+        long retval = 0;
+        try {
+            openActiveConnection();
+            Statement stmt = conn.createStatement();
+            String query = "SELECT COUNT(DISTINCT snapshot_time) FROM Statistics";
+            ResultSet rs = stmt.executeQuery(query);
+            rs.next();
+            retval = rs.getLong(1);
+        } catch(Exception e) {
+            log.error(e.getMessage(), e);
+        } finally {
+            closeConnection();
+        }
+        return new Long(retval);
+    }
+    
+    /**
+     * @param numberOfSnapshots - the number of latest snapshots to look at
+     * @return A hashmap which maps an mbean --> a hashmap with an attribute name and its value . All values will be the max.
+     */
+    public HashMap<String, HashMap<String, Long>> fetchMaxSnapshotData(Integer numberOfSnapshots) {
+        return fetchMaxOrMinSnapshotData(numberOfSnapshots, true);
+    }
+    /**
+     * @param numberOfSnapshots - the number of latest snapshots to look at
+     * @return A hashmap which maps an mbean --> a hashmap with an attribute name and its value . All values will be the min.
+     */    
+    public HashMap<String, HashMap<String, Long>> fetchMinSnapshotData(Integer numberOfSnapshots) {
+        return fetchMaxOrMinSnapshotData(numberOfSnapshots, false);
+    }
+    
+    /**
+     * @param numberOfSnapshots - the number of latest snapshots to look at.
+     * @param isMax - true if the result should be all maximum values. otherwise, false.
+     * @return A hashmap which maps an mbean --> a hashmap with an attribute name and its value . All values will be the min
+     * or max, depending on the isMax parameter.
+     */
+    private HashMap<String, HashMap<String, Long>> fetchMaxOrMinSnapshotData(Integer numberOfSnapshots, boolean isMax) {
+        openActiveConnection();
+        ResultSet snapshotTimeTable = fetchSnapshotTimesFromDB();
+        HashMap<String, HashMap<String, Long>> stats = new HashMap<String, HashMap<String, Long>>();
+        try {
+            // for each snapshot in the table
+            while(snapshotTimeTable.next()) {
+                Long snapshotTime = snapshotTimeTable.getLong(SNAPSHOT_TIME);
+                // retrieve the snapshot information by time
+                ResultSet snapshotData = fetchSnapshotDataFromDB(snapshotTime);
+                // for each statistic, perform a relaxation
+                while(snapshotData.next()) {
+                    String mbean = snapshotData.getString(MBEANNAME);
+                    // get map associated with mbean
+                    HashMap<String, Long> mbeanMap = stats.get(mbean);
+                    if(mbeanMap == null) {
+                        mbeanMap = new HashMap<String, Long>();
+                    }
+                    String[] statsNameList = snapshotData.getString(STATSNAMELIST).split(",");
+                    String[] statsValueList = snapshotData.getString(STATSVALUELIST).split(",");
+                    assert(statsNameList.length == statsValueList.length);
+                    // for each statname/statsvalue combo in an mbean
+                    for(int i = 0 ; i < statsNameList.length; i++) {
+                        String statsName = statsNameList[i];
+                        Long maxStatsValue = mbeanMap.get(statsName);
+                        // give maxStatsValue some value if there isn't one
+                        if(maxStatsValue == null) {
+                            if(isMax) {
+                                maxStatsValue = new Long(0);
+                            } else {
+                                maxStatsValue = Long.MAX_VALUE;
+                            }
+                        }
+                        // relax
+                        if(isMax) {
+                            maxStatsValue = new Long(Math.max(Long.parseLong(statsValueList[i]), maxStatsValue.longValue()));
+                        } else {
+                            maxStatsValue = new Long(Math.min(Long.parseLong(statsValueList[i]), maxStatsValue.longValue()));
+                        }
+                        // save name/value back into mbeanMap
+                        mbeanMap.put(statsName, maxStatsValue);
+                    }
+                    // save mbeanMap back into stats
+                    stats.put(mbean, mbeanMap);
+                }
+                
+                // compute the remaining snapshots left to look at
+                numberOfSnapshots--;
+                // discontinue once we have looked at numberOfSnapshots snapshots
+                if(numberOfSnapshots == 0) {
+                    break;
+                }
+            }
+        } catch(Exception e) {
+            log.error(e.getMessage(), e);
+        } finally {
+            closeConnection();
+        }
+        return stats;
+    }
+    
+    /**
+     * @param ds
+     * @param aggregateStats
+     * @return Returns a boolean if the snapshot statistics were successfully added
+     * to the DB.
+     */
+    public boolean addSnapshotToDB(HashMap<String, HashMap<String, Long>> aggregateStats) {
+        boolean success = true;
+        // get the current time from 1970
+        String currTime = "";
+        currTime += (new Date()).getTime();
+        try {
+            // for each mbean
+            for(Iterator itt = aggregateStats.keySet().iterator(); itt.hasNext(); ) {
+                String mbean = (String)itt.next();
+                // prepare the statsNameList and statsValueList beforehand
+                String statsNameList = "", statsValueList = "";
+                for(Iterator<String> it = aggregateStats.get(mbean).keySet().iterator(); it.hasNext(); ) {
+                    String statsName = it.next();
+                    Long statsValue = aggregateStats.get(mbean).get(statsName);
+                    if(statsNameList.length() == 0 || statsValueList.length() == 0) {
+                        // do not add a comma because this is the first occurrence
+                        statsValueList += statsValue.toString();
+                        statsNameList += statsName;
+                    } else {
+                        // add a comma
+                        statsValueList += "," + statsValue.toString();
+                        statsNameList += "," + statsName;
+                    }
+                }
+                
+                // start talking to DB
+                openActiveConnection();
+                Statement stmt = conn.createStatement();
+                HashMap stats = aggregateStats.get(mbean);
+                //--------Ensure MBeans are in place
+                int mbeanId = getMBeanId(mbean); 
+                if(mbeanId != -1) {
+                    // mbean already exists in the db
+                } else {
+                    // doesn't exist in the db so add it
+                    // add mbean record to the db
+                    stmt.executeUpdate("INSERT INTO MBeans (mbeanName, statsNameList) VALUES ("+ surroundWithQuotes(mbean) + "," + surroundWithQuotes(statsNameList) + ")");
+                    mbeanId = getMBeanId(mbean);
+                }
+                
+                // insert the statistics into Statistics table
+                stmt.executeUpdate( prepareInsertSnapshotStatement(currTime, statsValueList, mbeanId) );
+                closeConnection();
+            }
+        } catch(Exception  e){
+            log.error(e.getMessage(), e);
+            success = false;
+        } finally {
+            closeConnection();
+        }
+        
+        // go through the archiving process
+        try {
+            int retentionDays = Integer.parseInt(SnapshotConfigXMLBuilder.getAttributeValue("retention"));
+            long retentionMillis = (long)(retentionDays) * 86400000; // convert from days to milliseconds
+            archiveSnapshots( Long.parseLong(currTime) - retentionMillis );
+        } catch(Exception e) {
+            log.warn("Cannot archive snapshots because attribute 'retention' is not present in snapshot-config.xml.");
+        }
+        return success;
+    }
+    
+    /**
+     * Moves records from the ActiveDB to the ArchiveDB. The records that are moved
+     * are those whose snapshot_times exceed the retention period
+     * @param cutOffTime - in milliseconds
+     */
+    private void archiveSnapshots(long cutOffTime) {
+        // for each successful update of Snapshots/Statistics table
+        // increment or decrement these counters to ensure that nothing is being 
+        // lost in between. If these counters are non-zero, some records have been
+        // lost.
+        int snapshotsOver = 0;
+        int statisticsOver = 0;
+        try {
+            openActiveConnection();
+            ResultSet overDueSnapshotTimes = getOverDueSnapshotTimes(cutOffTime);
+            ArrayList<Long> overdueTimes = new ArrayList<Long>();
+            // save overdue times into an array list for later usage
+            while(overDueSnapshotTimes.next()) {
+                overdueTimes.add(overDueSnapshotTimes.getLong(SNAPSHOT_TIME));
+            }
+            closeConnection();
+            // for each overdue snapshot time
+            // -transfer all records associated with that snaphot_time to ArchiveDB
+            for(int i = 0; i < overdueTimes.size(); i++) {
+                long snapshotTime = overdueTimes.get(i);
+                openActiveConnection();
+                ResultSet rsSnapshotData = fetchSnapshotDataFromDB(new Long(snapshotTime));
+                HashMap<String, HashMap<String, Long>> snapshotData = new HashMap<String,HashMap<String, Long>>();
+                while(rsSnapshotData.next()) {
+                    // extract values from sql table
+                    String mbeanName = rsSnapshotData.getString(MBEANNAME);
+                    String statsNameList = rsSnapshotData.getString(STATSNAMELIST);
+                    String statsValueList = rsSnapshotData.getString(STATSVALUELIST);
+                    Long snapshot_time = rsSnapshotData.getLong(SNAPSHOT_TIME);
+                    // get a connection to the archive db too
+                    Connection archiveConn = archiveDS.getConnection();
+                    Statement archiveStmt = archiveConn.createStatement();
+                    //--------Ensure MBeans are in place
+                    int mbeanId = getMBeanIdFromArchive(mbeanName); 
+                    if(mbeanId != -1) {
+                        // mbean already exists in the db
+                    } else {
+                        // doesn't exist in the db so add it
+                        // add mbean record to the db
+                        archiveStmt.executeUpdate("INSERT INTO MBeans (mbeanName, statsNameList) VALUES ("+ surroundWithQuotes(mbeanName) + ", " + surroundWithQuotes(statsNameList) + ")");
+                        mbeanId = getMBeanIdFromArchive(mbeanName);
+                    }
+                    // ensure Statistics table has record of mbeanId, snapshotId, statsValue, statsName
+                    String updateStr = prepareInsertSnapshotStatement(snapshot_time + "", statsValueList, mbeanId);
+                    statisticsOver += archiveStmt.executeUpdate( updateStr );
+                    // close connection to archiveDB
+                    archiveConn.close();
+                }
+                closeConnection();
+            }
+            // for each snapshot time, remove all instances that is associated with in 
+            // in the active DB
+            for(int i = 0; i < overdueTimes.size(); i++) {
+                long snapshotTime = overdueTimes.get(i);
+                openActiveConnection();
+                Statement stmt = conn.createStatement();
+                // remove from Statistics table
+                String statisticsUpdate = "DELETE FROM Statistics WHERE snapshot_time=" + snapshotTime;
+                statisticsOver -= stmt.executeUpdate(statisticsUpdate);
+                closeConnection();
+            }
+        } catch(Exception e) {
+            log.error(e.getMessage(), e);
+        } finally {
+            closeConnection();
+        }
+        
+        // ensure that the transferring was good
+        if(snapshotsOver != 0) {
+            log.warn("Transferred snapshots was completed, but some things were lost.");
+        }
+        if(statisticsOver != 0) {
+            log.warn("Transferred statistics was completed, but some things were lost.");
+        }
+    }
+    
+    /**
+     * @param cutOffTime
+     * @return An SQL table contain a column of all the times that did not make the cutOffTime.
+     */
+    private ResultSet getOverDueSnapshotTimes(long cutOffTime) {
+        try {
+            Statement stmt = conn.createStatement();
+            String query = "SELECT DISTINCT snapshot_time FROM Statistics WHERE snapshot_time < " + cutOffTime;
+            return stmt.executeQuery(query);
+        } catch(Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return null;
+    }
+    
+    /**
+     * @param mbean
+     * @return The mbean id of the mbean from table ArchiveDB.MBean. Returns -1 if record does not exist.
+     */
+    private int getMBeanIdFromArchive(String mbean) throws Exception {
+        int retval = -1;
+        Connection archiveConn = archiveDS.getConnection();
+        Statement stmt = archiveConn.createStatement();
+        ResultSet rs = stmt.executeQuery("SELECT id FROM MBeans WHERE mbeanName=" + surroundWithQuotes(mbean));
+        if(rs.next()) {
+            retval = rs.getInt("id");
+        }
+        stmt.close();
+        archiveConn.close();
+        return retval;
+    }
+    
+    /**
+     * @param mbean
+     * @return The mbean id of the mbean from table ActiveDB.MBean. Returns -1 if record does not exist.
+     */
+    private int getMBeanId(String mbean) throws Exception {
+        int retval = -1;
+        Connection conn = activeDS.getConnection();
+        Statement stmt = conn.createStatement();
+        ResultSet rs = stmt.executeQuery("SELECT id FROM MBeans WHERE mbeanName=" + surroundWithQuotes(mbean));
+        if(rs.next()) {
+            retval = rs.getInt("id");
+        }
+        stmt.close();
+        conn.close();
+        return retval;
+    }
+    
+    /**
+     * @param snapshot_time
+     * @param statsValueList
+     * @param mbeanId
+     * @return Returns an SQL insert statement for one statistic given the correct information.
+     */
+    public String prepareInsertSnapshotStatement(String snapshot_time, String statsValueList, int mbeanId) {
+        String retval = "INSERT INTO Statistics (snapshot_time, statsValueList, mbeanId) VALUES (";
+        retval += snapshot_time;
+        retval += ",";
+        retval += surroundWithQuotes(statsValueList);
+        retval += ",";
+        retval += mbeanId;
+        retval += ")";
+        return retval;
+    }
+
+    /**
+     * @param s
+     * @return A String with ' at the beginning and end
+     */
+    private String surroundWithQuotes(String s) {
+        return "'" + s.trim() + "'";
+    }
+    
+    /**
+     * Fetches the data stored from the snapshot thread and returns
+     * it in a ArrayList with each element being a HashMap of the attribute
+     * mapping to the statistic. Grabs 'numberOfSnapshots' snapshots. Grabs 
+     * one snapshot per 'everyNthsnapshot'
+     * 
+     * @param numberOfSnapshot
+     * @param everyNthSnapshot
+     * @return ArrayList
+     */ 
+    public ArrayList<HashMap<String, HashMap<String, Object>>> fetchData(Integer numberOfSnapshot, 
+                                                                                Integer everyNthSnapshot) {
+        ArrayList<HashMap<String, HashMap<String, Object>>> stats = new ArrayList<HashMap<String, HashMap<String, Object>>>();
+        openActiveConnection();
+        // get all records in the database grouped and ordered by time
+        ResultSet table = fetchSnapshotTimesFromDB();
+        // iterate through the table and finds the times (which uniquely IDs a snapshot)
+        // that are wanted and queries the rest of the DB using the time as the condition
+        // (i.e. the ones that are in the c*n-th snapshot where c <= numberOfSnapshot
+        // and n == everyNthSnapshot)
+        int nthSnapshot = 0;
+        try {
+            while(table.next()) {
+                Long snapshotTime = table.getLong(SNAPSHOT_TIME);
+                // grab 0*nth, 1*nth, 2*nth snapshot up to min(numberOfSnapshot*everyNthSnapshot, size of the table)
+                if(nthSnapshot % everyNthSnapshot == 0) {
+                    HashMap<String, HashMap<String, Object>> snapshotData = packageSnapshotData(snapshotTime);
+                    stats.add( 0, snapshotData );
+                    numberOfSnapshot--;
+                }
+                nthSnapshot++;
+                // no more snapshots needs to be looked at, we have successfully acquired our goal
+                if(numberOfSnapshot == 0) {
+                    break;
+                }
+            }
+        } catch(Exception e) {
+            log.error(e.getMessage(), e);
+        } finally {
+            closeConnection();
+        }
+        return stats;
+    }
+    
+    /**
+     * @param snapshotTime
+     * @return A hashmap in the form <String1, HashMap> where String1 is the mbean name
+     * and HashMap is a map containing a <String2, Object> where String2 is an attribute name
+     * and Object is the value. Additionally, if String is "times" then it maps to a HashMap
+     * containing snapshot_time and snapshot_date information.
+     */
+    private HashMap<String, HashMap<String, Object>> packageSnapshotData(Long snapshotTime) {
+        HashMap<String, HashMap<String, Object>> snapshotPkg = new HashMap<String, HashMap<String, Object>>();
+        openActiveConnection();
+        ResultSet snapshotData = fetchSnapshotDataFromDB(snapshotTime);
+        try {
+            // for each record save it somewhere in the snapshotPkg
+            while(snapshotData.next()) {
+                String currMBean = snapshotData.getString(MBEANNAME);
+                // get the information for the mbean defined by currMBean
+                HashMap<String, Object> mbeanInfo = snapshotPkg.get(currMBean);
+                if(mbeanInfo == null) {
+                    mbeanInfo = new HashMap<String, Object>();
+                }
+                // get statistics from resultset
+                String statsValueStr = snapshotData.getString(STATSVALUELIST);
+                String statsNameStr = snapshotData.getString(STATSNAMELIST);
+                String[] statsValueList = statsValueStr.split(",");
+                String[] statsNameList = statsNameStr.split(",");
+                assert(statsValueList.length == statsNameList.length);
+                // for each statsValue/statsName, save it
+                for(int i = 0 ; i < statsValueList.length; i++) {
+                    long statValue = Long.parseLong(statsValueList[i]);
+                    mbeanInfo.put(statsNameList[i], new Long(statValue));
+                }
+                // save the hashmap into the snapshotpkg
+                snapshotPkg.put(currMBean, mbeanInfo);
+            }
+        } catch(Exception e) {
+            log.error(e.getMessage(), e);
+        } finally {
+            closeConnection();
+        }
+        // add the time and date
+        HashMap<String, Object> timeMap = new HashMap<String, Object>();
+        timeMap.put(SNAPSHOT_TIME, snapshotTime);
+        snapshotPkg.put("times", timeMap);
+        
+        return snapshotPkg;
+    }
+    
+    /**
+     * @param snapshotTime
+     * @return Returns a ResultSet with all statistic information that matches the snapshot_time.
+     */
+    private ResultSet fetchSnapshotDataFromDB(Long snapshotTime) {
+        String query = "SELECT S.statsValueList AS statsValueList, M.statsNameList AS statsNameList, S.snapshot_time AS snapshot_time, M.mbeanName AS mbeanName FROM Statistics S, MBeans M WHERE S.snapshot_time=" + snapshotTime;
+        query += " AND S.mbeanId=M.id";
+        ResultSet retval = null;
+        try {
+            if(conn.isClosed()) {
+                openActiveConnection();
+            }
+            Statement stmt = conn.createStatement();
+            retval = stmt.executeQuery(query);
+        } catch(Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return retval;
+    }
+
+    /**
+     * @return Returns a ResultSet with one column (snapshot_time) sorted in descending order
+     */
+    private ResultSet fetchSnapshotTimesFromDB() {
+        String query = "SELECT DISTINCT snapshot_time FROM Statistics ORDER BY snapshot_time DESC";
+        ResultSet retval = null;
+        try {
+            if(conn.isClosed()) {
+                openActiveConnection();
+            }
+            Statement stmt = conn.createStatement();
+            retval = stmt.executeQuery(query);
+        } catch(Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return retval;
+    }
+    
+    /**
+     * Opens the global connection to activeDB
+     */
+    private void openActiveConnection() {
+        try {
+            conn = activeDS.getConnection();
+        } catch(Exception e) {
+            log.error(e.getMessage(), e);
+        }
+    }
+    
+    /**
+     * Opens the global connection to archiveDB
+     */
+    private void openArchiveConnection() {
+        try {
+            conn = archiveDS.getConnection();
+        } catch(Exception e) {
+            log.error(e.getMessage(), e);
+        }
+    }
+    
+    /**
+     * Closes the global connection to a DB
+     */
+    private void closeConnection() {
+        if(conn != null) {
+            try {
+                conn.close();
+            } catch(Exception e) {
+                log.error(e.getMessage(), e);
+            }
+        }
+    }
+    
+    /**
+     * @param mbeanName
+     * @param statsName
+     * @param numberOfSnapshots
+     * @param everyNthSnapshot
+     * @return HashMap which maps from a snapshot_time --> value of the mbean.statsName at that time
+     */
+    public TreeMap<Long, Long> getSpecificStatistics(    String mbeanName, 
+                                                                String statsName, 
+                                                                int numberOfSnapshots, 
+                                                                int everyNthSnapshot, 
+                                                                boolean showArchived) {
+        openActiveConnection();
+        TreeMap<Long, Long> stats = new TreeMap<Long, Long>();
+        int nthSnapshot = 0;
+        // attempt to get as many snapshots from the active db as possible
+        try {
+            Statement stmt = conn.createStatement();
+            int mbeanId = getMBeanId(mbeanName);
+            if(mbeanId == -1) {
+                log.error(mbeanName + " does not exist in the database.");
+            } else {
+                String query = "SELECT DISTINCT snapshot_time, statsValueList, statsNameList FROM Statistics, MBeans M WHERE mbeanId=" + mbeanId + " AND mbeanId=M.id ORDER BY snapshot_time DESC";
+                ResultSet rs = stmt.executeQuery(query);
+                // iterate through the table paying attention to those at everyNthSnapshot-th position
+                while(rs.next()) {
+                    // every nth snapshot I save the information into my returning hashmap
+                    if(nthSnapshot % everyNthSnapshot == 0) {
+                        String[] statsValueList = rs.getString(STATSVALUELIST).split(",");
+                        String[] statsNameList = rs.getString(STATSNAMELIST).split(",");
+                        assert(statsValueList.length == statsNameList.length);
+                        Long statsValue = null;
+                        for(int i = 0 ; i < statsNameList.length; i++) {
+                            if(statsNameList[i].equals(statsName)) {
+                                long value = Long.parseLong(statsValueList[i]);
+                                statsValue = new Long(value);
+                            }
+                        }
+                        // exit function after error
+                        if(statsValue == null) {
+                            log.warn("Statistics name '" + statsName + "' does not exist");
+                            return stats;
+                        } else {
+                            stats.put(rs.getLong(SNAPSHOT_TIME), statsValue);
+                            numberOfSnapshots--;
+                        }
+                    }
+                    // update counter
+                    nthSnapshot++;
+                    // enough data, end this thing
+                    if(numberOfSnapshots == 0) {
+                        break;
+                    }
+                }
+            }
+        } catch(Exception e) {
+            log.error(e.getMessage(), e);
+        } finally {
+            closeConnection();
+        }
+
+        nthSnapshot = 0;
+
+        // attempt to get the remaining snapshots requested from the archive DB
+        // iff the showArchive flag is set
+        if(showArchived && numberOfSnapshots != 0) {
+            try {
+                openArchiveConnection();    // connection to the Archive DB
+                Statement stmt = conn.createStatement();
+                int mbeanId = getMBeanId(mbeanName);
+                if(mbeanId == -1) {
+                    log.error(mbeanName + " does not exist in the database.");
+                } else {
+                    String query = "SELECT DISTINCT snapshot_time, statsValueList, statsNameList FROM Statistics, MBeans M WHERE mbeanId=" + mbeanId + " AND mbeanId=M.id ORDER BY snapshot_time DESC";
+                    ResultSet rs = stmt.executeQuery(query);
+                    // iterate through the table paying attention to those at everyNthSnapshot-th position
+                    while(rs.next()) {
+                        // every nth snapshot I save the information into my returning hashmap
+                        if(nthSnapshot % everyNthSnapshot == 0) {
+                            String[] statsValueList = rs.getString(STATSVALUELIST).split(",");
+                            String[] statsNameList = rs.getString(STATSNAMELIST).split(",");
+                            assert(statsValueList.length == statsNameList.length);
+                            Long statsValue = null;
+                            for(int i = 0 ; i < statsNameList.length; i++) {
+                                if(statsNameList[i].equals(statsName)) {
+                                    long value = Long.parseLong(statsValueList[i]);
+                                    statsValue = new Long(value);
+                                }
+                            }
+                            // exit function after error
+                            if(statsValue == null) {
+                                log.warn("Statistics name '" + statsName + "' does not exist");
+                                return stats;
+                            } else {
+                                stats.put(rs.getLong(SNAPSHOT_TIME), statsValue);
+                                numberOfSnapshots--;
+                            }
+                        }
+                        // update counter
+                        nthSnapshot++;
+                        // enough data, end this thing
+                        if(numberOfSnapshots == 0) {
+                            break;
+                        }
+                    }
+                }
+            } catch(Exception e) {
+                log.error(e.getMessage(), e);
+            } finally {
+                closeConnection();
+            }
+        }
+        return stats;
+    }
+    
+    /**
+     * Sets the necessary data sources for this helper to talk to the db
+     * @param activeDS
+     * @param archiveDS
+     */
+    public void setDataSources(DataSource active, DataSource archive) {
+        activeDS = active;
+        archiveDS = archive;
+    }
+}