/*
* NOTE: This copyright does *not* cover user programs that use HQ
* program services by normal system calls through the application
* program interfaces provided as part of the Hyperic Plug-in Development
* Kit or the Hyperic Client Development Kit - this is merely considered
* normal use of the program, and does *not* fall under the heading of
* "derived work".
*
* Copyright (C) [2004-2008], Hyperic, Inc.
* This file is part of HQ.
*
* HQ is free software; you can redistribute it and/or modify
* it under the terms version 2 of the GNU General Public License as
* published by the Free Software Foundation. This program is distributed
* in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
package org.hyperic.hq.plugin.oracle;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Properties;
import org.hyperic.hq.measurement.MeasurementConstants;
import org.hyperic.hq.product.JDBCMeasurementPlugin;
import org.hyperic.hq.product.Metric;
import org.hyperic.hq.product.MetricNotFoundException;
import org.hyperic.hq.product.MetricUnreachableException;
import org.hyperic.hq.product.PluginException;
import org.hyperic.hq.product.TypeInfo;
import org.hyperic.util.StringUtil;
import org.hyperic.util.config.ConfigResponse;
import org.hyperic.util.config.ConfigSchema;
import org.hyperic.util.config.SchemaBuilder;
import org.hyperic.util.jdbc.DBUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class OracleMeasurementPlugin
extends JDBCMeasurementPlugin {
private static String logCtx = OracleMeasurementPlugin.class.getName();
private static Log _log = LogFactory.getLog(logCtx);
public static final String PROP_URL = "jdbcUrl";
public static final String PROP_USER = "jdbcUser";
public static final String PROP_PASSWORD = "jdbcPassword";
public static final String PROP_LISTENERS = "listeners";
static final String JDBC_DRIVER = "oracle.jdbc.driver.OracleDriver";
static final String PROP_USERNAME = "user";
static final String PROP_TABLESPACE = "tablespace";
static final String PROP_SEGMENT = "segment";
private static final String READ_TIMEOUT_KEY = "oracle.jdbc.ReadTimeout";
private static final Integer READ_TIMEOUT_VALUE = new Integer(60000);
private static HashMap ora8Queries = null; // Oracle 8i only
private static HashMap ora9Queries = null; // Oracle 9i only
private static HashMap ora10Queries = null; // Oracle 10g only
private static HashMap genericQueries = null; // Any
private static HashMap tempTspaceQueries = null; // temp tablespace queries
private static final String TABLESPACE_QUERY =
"SELECT * FROM DBA_TABLESPACES WHERE TABLESPACE_NAME=";
private static final String SEGMENT_QUERY = "select SEGMENT_NAME" +
" FROM USER_SEGMENTS WHERE SEGMENT_NAME=";
protected void getDriver()
throws ClassNotFoundException {
Class.forName(JDBC_DRIVER);
}
protected Connection getConnection(String url,
String user,
String password)
throws SQLException {
Properties info = getJDBCConnectionProperties(user, password);
info.put(READ_TIMEOUT_KEY, READ_TIMEOUT_VALUE);
return DriverManager.getConnection(url, info);
}
protected String getDefaultURL() {
return getPluginProperty("DEFAULT_URL");
}
public ConfigSchema getConfigSchema(TypeInfo info, ConfigResponse config)
{
// Override JDBCMeasurementPlugin.
return new ConfigSchema();
}
protected void initQueries() {
if (genericQueries != null) {
return;
}
/**
* The following mess defines the mapping tables between the Metric
* attibute name and the SQL query that is used to look up the value.
* This can be confusing for many reasons:
*
* 1. All Oracle servers/services (regarless of version) will use these
* 2. Many of them are no longer used, but only kept here so to save
* someone from having to add them again at a later time. For
* definitive list of which metrics are collected, see the xml
* definition file.
*
* Oracle 10g exposes most of these through v$sysmetric which avoids
* this manual process. Needed for backwards compat though.
*/
ora8Queries = new HashMap();
ora9Queries = new HashMap();
ora10Queries = new HashMap();
genericQueries = new HashMap();
tempTspaceQueries = new HashMap();
String baseQuery = "SELECT value FROM V$SYSSTAT WHERE name = ";
genericQueries.put("ConsistentChanges", baseQuery +
"'consistent changes'");
genericQueries.put("ConsistentGets", baseQuery +
"'consistent gets'");
genericQueries.put("BlockChanges", baseQuery +
"'db block changes'");
genericQueries.put("BlockGets", baseQuery +
"'db block gets'");
genericQueries.put("BufferInspected", baseQuery +
"'free buffer inspected'");
genericQueries.put("BufferRequested", baseQuery +
"'free buffer requested'");
genericQueries.put("BytesSent", baseQuery +
"'bytes sent via SQL*Net to client'");
genericQueries.put("BytesReceived", baseQuery +
"'bytes received via SQL*Net from client'");
genericQueries.put("ClientRoundtrips", baseQuery +
"'SQL*Net roundtrips to/from client'");
genericQueries.put("CPUUsage", baseQuery +
"'CPU used when call started'");
genericQueries.put("CPUUsageRecursive", baseQuery +
"'recursive cpu usage'");
genericQueries.put("CPUUsageParse", baseQuery +
"'parse time cpu'");
genericQueries.put("LogonsCumulative", baseQuery +
"'logons cumulative'");
genericQueries.put("LogonsCurrent", baseQuery +
"'logons current'");
genericQueries.put("LogonsCurrentUserActive",
"SELECT COUNT(*) FROM V$SESSION " +
"WHERE UPPER(USERNAME) = UPPER('%user%') AND " +
"STATUS = 'ACTIVE'");
genericQueries.put("LogonsCurrentUserInactive",
"SELECT COUNT(*) FROM V$SESSION " +
"WHERE UPPER(USERNAME) = UPPER('%user%') AND " +
"STATUS = 'INACTIVE'");
genericQueries.put("OpenedCursorsCumulative", baseQuery +
"'opened cursors cumulative'");
genericQueries.put("OpenedCursorsCurrent", baseQuery +
"'opened cursors current'");
genericQueries.put("OpenedCursorsCurrentUser",
"SELECT COUNT(*) FROM V$OPEN_CURSOR " +
"WHERE UPPER(USER_NAME) = UPPER('%user%')");
genericQueries.put("ParseCount", baseQuery +
"'parse count (total)'");
genericQueries.put("HardParseCount", baseQuery +
"'parse count (hard)'");
genericQueries.put("ExecuteCount", baseQuery +
"'execute count'");
genericQueries.put("PhysicalReads", baseQuery +
"'physical reads'");
genericQueries.put("LogicalReads", baseQuery +
"'session logical reads'");
genericQueries.put("PhysicalWrites", baseQuery +
"'physical writes'");
genericQueries.put("RedoEntries", baseQuery +
"'redo entries'");
genericQueries.put("RedoLogSpace", baseQuery +
"'redo log space requests'");
genericQueries.put("RedoSize", baseQuery +
"'redo size'");
genericQueries.put("RedoLogSize",
"SELECT SUM(bytes) from V$LOG");
genericQueries.put("RedoSyncWrites", baseQuery +
"'redo synch writes'");
genericQueries.put("SessionUGAMemory",
"SELECT SUM(s.value) FROM V$STATNAME sn, " +
"V$SESSTAT s WHERE sn.statistic#=s.statistic# AND " +
"sn.name='session uga memory' GROUP BY sn.name");
genericQueries.put("SessionPGAMemory",
"SELECT SUM(s.value) FROM V$STATNAME sn, " +
"V$SESSTAT s WHERE sn.statistic#=s.statistic# AND " +
"sn.name='session pga memory' GROUP BY sn.name");
genericQueries.put("TableFetchContinuedRow", baseQuery +
"'table fetch continued row'");
genericQueries.put("TableFetchRowId", baseQuery +
"'table fetch by rowid'");
genericQueries.put("TableScanBlocks", baseQuery +
"'table scan blocks gotten'");
genericQueries.put("TableScanRows", baseQuery +
"'table scan rows gotten'");
genericQueries.put("TableScansLong", baseQuery +
"'table scans (long tables)'");
genericQueries.put("TableScansShort", baseQuery +
"'table scans (short tables)'");
genericQueries.put("UserCalls", baseQuery +
"'user calls'");
genericQueries.put("UserCommits", baseQuery +
"'user commits'");
genericQueries.put("UserRollbacks", baseQuery +
"'user rollbacks'");
genericQueries.put("CallsPerTx",
"SELECT t1.value/t2.value FROM V$SYSSTAT t1, " +
"V$SYSSTAT t2 WHERE t1.name = 'user calls' AND " +
"t2.name = 'user commits'");
genericQueries.put("BlockChangesPerTx",
"SELECT t1.value/t2.value FROM V$SYSSTAT t1, " +
"V$SYSSTAT t2 WHERE " +
"t1.name = 'db block changes' AND " +
"t2.name = 'user calls'");
genericQueries.put("BlockVisitsPerTx",
"SELECT (t1.value+t2.value)/t3.value FROM " +
"V$SYSSTAT t1, V$SYSSTAT t2, " +
"V$SYSSTAT t3 WHERE " +
"t1.name = 'db block gets' AND " +
"t2.name = 'consistent gets' AND " +
"t3.name = 'user commits'");
genericQueries.put("CacheHitRatio",
"SELECT (t1.value+t2.value-t3.value) / " +
"(t4.value + t5.value) FROM V$SYSSTAT t1, " +
"V$SYSSTAT t2, V$SYSSTAT t3, V$SYSSTAT t4, " +
"V$SYSSTAT t5 WHERE " +
"t1.name = 'consistent gets' AND " +
"t2.name = 'db block gets' AND " +
"t3.name = 'physical reads' AND " +
"t4.name = 'consistent gets' AND " +
"t5.name = 'db block gets'");
genericQueries.put("DataDictionaryCacheHitRatio",
"SELECT (1 - (sum(GETMISSES) / sum(GETS))) hit_ratio from V$ROWCACHE");
genericQueries.put("LibraryCacheHitRatio",
"SELECT (sum(PINS) - sum(RELOADS)) / sum(PINS) hit_ratio from V$LIBRARYCACHE");
genericQueries.put("ChangedBlockRatio",
"SELECT t1.value/(t2.value + t3.value) FROM " +
"V$SYSSTAT t1, V$SYSSTAT t2, " +
"V$SYSSTAT t3 WHERE " +
"t1.name = 'db block changes' AND " +
"t2.name = 'db block gets' AND " +
"t3.name = 'consistent gets'");
genericQueries.put("ConsistentChangeRatio",
"SELECT t1.value/t2.value FROM V$SYSSTAT t1, " +
"V$SYSSTAT t2 WHERE " +
"t1.name = 'consistent changes' AND " +
"t2.name = 'consistent gets'");
genericQueries.put("ContinuedRowRatio",
"SELECT t1.value/(t2.value+t3.value) FROM " +
"V$SYSSTAT t1, V$SYSSTAT t2, " +
"V$SYSSTAT t3 WHERE " +
"t1.name = 'table fetch continued row' AND " +
"t2.name = 'table fetch by rowid' AND " +
"t3.name = 'table scan rows gotten'");
genericQueries.put("RecursiveToUserCallRatio",
"SELECT t1.value/t2.value FROM " +
"V$SYSSTAT t1, V$SYSSTAT t2 WHERE " +
"t1.name = 'recursive calls' AND " +
"t2.name = 'user calls'");
genericQueries.put("RowSourceRatio",
"SELECT t1.value/(t2.value+t3.value) FROM " +
"V$SYSSTAT t1, V$SYSSTAT t2, " +
"V$SYSSTAT t3 WHERE " +
"t1.name = 'table scan rows gotten' AND " +
"t2.name = 'table fetch by rowid' AND " +
"t3.name = 'table scan rows gotten'");
genericQueries.put("UserCallsPerParse",
"SELECT t1.value/t2.value FROM " +
"V$SYSSTAT t1, V$SYSSTAT t2 WHERE " +
"t1.name = 'user calls' AND " +
"t2.name = 'parse count (total)'");
genericQueries.put("UserRollbackRatio",
"SELECT t1.value/(t2.value+t3.value) FROM " +
"V$SYSSTAT t1, V$SYSSTAT t2, " +
"V$SYSSTAT t3 WHERE " +
"t1.name = 'user rollbacks' AND " +
"t2.name = 'user commits' AND " +
"t3.name = 'user rollbacks'");
genericQueries.put("RedoLogSpaceWaitRatio",
"SELECT t1.value/t2.value FROM " +
"V$SYSSTAT t1, V$SYSSTAT t2 WHERE " +
"t1.name = 'redo log space requests' AND " +
"t2.name = 'redo entries'");
genericQueries.put("UpTime",
"SELECT CAST(((sysdate - startup_time) * 3600 " +
"* 24 * 1000) AS INTEGER) from V$INSTANCE WHERE" +
" UPPER(INSTANCE_NAME) = UPPER('%instance%')");
genericQueries.put("InstanceUsedSpace",
"SELECT SUM(bytes) FROM SYS.DBA_DATA_FILES");
genericQueries.put("InstanceFreeSpace",
"SELECT SUM(bytes) FROM SYS.DBA_FREE_SPACE");
genericQueries.put("UsedSpace",
"SELECT SUM(bytes) FROM SYS.DBA_SEGMENTS " +
"WHERE UPPER(owner) = UPPER('%user%')");
// non-temp Tablespace metrics
genericQueries.put("TSFreeSpace",
"SELECT SUM(bytes) FROM DBA_FREE_SPACE " +
"WHERE TABLESPACE_NAME='%tablespace%'");
genericQueries.put("TSUsedDiskSpace",
"SELECT SUM(bytes) FROM DBA_DATA_FILES " +
"WHERE TABLESPACE_NAME='%tablespace%'");
genericQueries.put("TSFreeExtents",
"SELECT SUM(bytes/initial_extent) " +
"FROM DBA_TABLESPACES ts, DBA_FREE_SPACE fs " +
"WHERE ts.tablespace_name = fs.tablespace_name " +
"AND ts.tablespace_name='%tablespace%'");
genericQueries.put("TSNumDataFiles",
"SELECT COUNT(*) FROM DBA_DATA_FILES " +
"WHERE TABLESPACE_NAME='%tablespace%'");
genericQueries.put("TSSpaceUsedPercent",
"SELECT 1-(SELECT sum(bytes)/1024" +
" FROM sys.dba_free_space" +
" WHERE tablespace_name = '%tablespace%') /" +
" (SELECT sum(bytes/1024) from sys.dba_data_files" +
" WHERE tablespace_name = '%tablespace%')"+
" AS percent_used" +
" FROM dual");
// temp Tablespace metrics (MUST be defined after the genericQueries)
tempTspaceQueries.put("TSFreeSpace",
"SELECT SUM(bytes_free) FROM V$temp_space_header " +
"WHERE tablespace_name='%tablespace%'");
tempTspaceQueries.put("TSUsedDiskSpace",
"SELECT SUM(bytes_used) FROM V$temp_space_header " +
"WHERE tablespace_name='%tablespace%'");
tempTspaceQueries.put("TSFreeExtents",
"SELECT SUM(bytes_free/initial_extent) " +
"FROM DBA_TABLESPACES ts, V$temp_space_header fs " +
"WHERE ts.tablespace_name = fs.tablespace_name " +
"AND ts.tablespace_name='%tablespace%'");
tempTspaceQueries.put("TSNumDataFiles",
"SELECT COUNT(*) FROM DBA_TEMP_FILES " +
"WHERE TABLESPACE_NAME='%tablespace%'");
tempTspaceQueries.put("TSSpaceUsedPercent",
"SELECT 1-(SELECT sum(bytes_free)/1024 " +
"FROM V$temp_space_header " +
"WHERE tablespace_name='%tablespace%') /" +
" (SELECT sum(bytes/1024) from DBA_TEMP_FILES" +
" WHERE tablespace_name = '%tablespace%')"+
" AS percent_used" +
" FROM dual");
// Oracle 8i queries
ora8Queries.put("SortsDisk", baseQuery + "'sorts (disk)'");
ora8Queries.put("SortsMemory", baseQuery +
"'sorts (memory)'");
ora8Queries.put("SortsRows", baseQuery + "'sorts (rows)'");
ora8Queries.put("SortsOverflowRatio",
"SELECT t1.value/(t2.value+t3.value) FROM " +
"V$SYSSTAT t1, V$SYSSTAT t2, " +
"V$SYSSTAT t3 WHERE t1.name = 'sorts (disk)' AND " +
"t2.name = 'sorts (memory)' AND " +
"t3.name = 'sorts (disk)'");
// Oracle 9i queries
ora9Queries.put("SortsDisk", baseQuery + "'sorts disk'");
ora9Queries.put("SortsMemory", baseQuery + "'sorts memory'");
ora9Queries.put("SortsRows", baseQuery + "'sorts rows'");
ora9Queries.put("SortsOverflowRatio",
"SELECT t1.value/(t2.value+t3.value) FROM " +
"V$SYSSTAT t1, V$SYSSTAT t2, " +
"V$SYSSTAT t3 WHERE t1.name = 'sorts disk' AND " +
"t2.name = 'sorts memory' AND " +
"t3.name = 'sorts disk'");
// Oracle 10g queries
ora10Queries.put("SegmentSize", "select sum(bytes)" +
" FROM USER_SEGMENTS" +
" WHERE SEGMENT_NAME not like 'BIN$%'" +
" and SEGMENT_NAME not like 'SYS_%'" +
" and SEGMENT_NAME = '%segment%'");
ora10Queries.put("NumberOfRows", "select num_rows" +
" FROM %tablename%" +
" WHERE %identifier% = '%segment%'");
// Alias for avail.
// If we can fetch any metric, consider the server/service available
//
genericQueries.put(AVAIL_ATTR,
genericQueries.get("PhysicalReads"));
}
private String getSegmentQuery(Metric metric)
{
String segment = metric.getObjectProperty(PROP_SEGMENT);
String tablespace = metric.getObjectProperty(PROP_TABLESPACE);
String sql = (String)ora10Queries.get(metric.getAttributeName());
sql = StringUtil.replace(sql, "%segment%", segment);
Connection conn = null;
try {
conn = getCachedConnection(metric);
if (OracleControlPlugin.isTable(conn, segment, tablespace)) {
sql = StringUtil.replace(sql, "%tablename%", "all_tables");
sql = StringUtil.replace(sql, "%identifier%", "table_name");
}
else {
sql = StringUtil.replace(sql, "%tablename%", "all_indexes");
sql = StringUtil.replace(sql, "%identifier%", "index_name");
}
} catch (SQLException e) {
_log.error(e.getMessage(), e);
}
finally{
//Return the connection
returnCachedConnection(metric, conn);
}
return sql;
}
protected String getQuery(Metric metric)
{
String alias = metric.getAttributeName();
String objName = metric.getObjectName();
if (-1 != objName.indexOf("Type=Segment")) {
return getSegmentQuery(metric);
}
String query = (String)genericQueries.get(alias);
Properties props = metric.getObjectProperties();
if (query == null) {
// Not in the generic queries, check the version specific table
// XXX: grab the version from the Metric, this will currently ignore
// any Oracle 9i specific Metric
query = (String)ora8Queries.get(alias);
}
//XXX: Would have been nice to put the processed query to execute in the
// template to avoid this substituion on each collection.
String tablespace = metric.getObjectProperties().
getProperty(PROP_TABLESPACE);
if (tablespace != null) {
if (tablespaceIsOffline(tablespace, metric)) {
_log.debug("Tablespace " + tablespace +
" is offline, will return 0 for all metrics.");
return "select 0 from dual";
}
else if (tablespaceIsTemporary(tablespace, metric)) {
// replace generic query with temporary tablespace-specific query
query = (String)tempTspaceQueries.get(alias);
}
query = StringUtil.replace(query, "%tablespace%", tablespace);
}
// Do substituion on the user name in the SQL query
String user = metric.getObjectProperties().getProperty(PROP_USERNAME);
if (user == null) {
// Backwards compat
user = metric.getProperties().getProperty(PROP_USERNAME);
}
if (user != null) {
query = StringUtil.replace(query, "%user%", user);
}
// XXX: could cache this
String url = metric.getProperties().getProperty(PROP_URL);
String instance = url.substring(url.lastIndexOf(":") + 1,
url.length());
query = StringUtil.replace(query, "%instance%", instance);
return query;
}
private boolean tablespaceIsTemporary(String tablespace, Metric metric) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try
{
conn = getCachedConnection(metric);
stmt = conn.createStatement();
String sql = "select lower(contents) as contents" +
" FROM dba_tablespaces " +
" WHERE lower(tablespace_name) = " +
"lower('"+tablespace+"')";
rs = stmt.executeQuery(sql);
if (rs.next()) {
return (rs.getString("contents").equals("temporary")) ? true : false;
}
} catch (SQLException e) {
// not sure what we can do if this happens, so just log the error
_log.error(e.getMessage(), e);
} finally {
// don't close the conn, since it is shared
DBUtil.closeJDBCObjects(getLog(), null, stmt, rs);
//Return the connection
returnCachedConnection(metric, conn);
}
// assume non-temporary tablespace in general
return false;
}
private boolean tablespaceIsOffline(String tablespace, Metric metric)
{
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try
{
conn = getCachedConnection(metric);
stmt = conn.createStatement();
String sql = "select lower(status) as status" +
" FROM dba_tablespaces " +
" WHERE lower(tablespace_name) = " +
"lower('"+tablespace+"')";
rs = stmt.executeQuery(sql);
if (rs.next()) {
return (rs.getString("status").equals("offline")) ? true : false;
}
} catch (SQLException e) {
// not sure what we can do if this happens, so just log the error
_log.error(e.getMessage(), e);
} finally {
// don't close the conn, since it is shared
DBUtil.closeJDBCObjects(getLog(), null, stmt, rs);
//Return the connection
returnCachedConnection(metric, conn);
}
// we don't want to collect any metrics if the tablespace does not exist
return true;
}
/**
* Override for tablespace avail.
* AND segment avail.
*/
protected double getQueryValue(Metric metric)
throws MetricNotFoundException, PluginException,
MetricUnreachableException
{
String alias = metric.getAttributeName();
boolean isAvail = alias.equalsIgnoreCase(AVAIL_ATTR);
if (!isAvail) {
return super.getQueryValue(metric);
}
String tablespace = metric.getObjectProperties().
getProperty(PROP_TABLESPACE);
String segment = metric.getObjectProperties().
getProperty(PROP_SEGMENT);
if (tablespace == null && segment == null) {
return super.getQueryValue(metric);
} else if (tablespace != null) {
return (tablespaceIsOffline(tablespace, metric)) ?
MeasurementConstants.AVAIL_DOWN : MeasurementConstants.AVAIL_UP;
}
// else, tablespace avail
Properties props = metric.getProperties();
String
url = props.getProperty(PROP_URL),
user = props.getProperty(PROP_USER),
pass = props.getProperty(PROP_PASSWORD);
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = getCachedConnection(url, user, pass);
String query = SEGMENT_QUERY + "'" + segment + "'";
ps = conn.prepareStatement(query);
rs = ps.executeQuery();
if (rs != null && rs.next()) {
return MeasurementConstants.AVAIL_UP;
} else {
return MeasurementConstants.AVAIL_DOWN;
}
} catch (SQLException e) {
if (isAvail) {
getLog().debug("AVAIL_DOWN", e);
return MeasurementConstants.AVAIL_DOWN;
}
// Remove this connection from the cache.
removeCachedConnection(url, user, pass);
String msg = "Query failed for " + alias +
": " + e.getMessage();
// Catch divide by 0 errors and return 0
if(e.getErrorCode() == DBUtil.ORACLE_ERROR_DIVIDE_BY_ZERO)
return 0;
if(e.getErrorCode() == DBUtil.ORACLE_ERROR_NOT_AVAILABLE)
throw new MetricUnreachableException(msg, e);
throw new MetricNotFoundException(msg, e);
} finally {
DBUtil.closeJDBCObjects(getLog(), null, ps, rs);
//Return the connection
returnCachedConnection(metric, conn);
}
}
protected void returnCachedConnection(Metric metric, Connection conn)
{
Properties props = metric.getProperties();
String url = props.getProperty(PROP_URL),
user = props.getProperty(PROP_USER),
pass = props.getProperty(PROP_PASSWORD);
returnCachedConnection(url, user, pass, conn);
}
}