/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2006-2011 The OpenNMS Group, Inc. * OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc. * * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc. * * OpenNMS(R) is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * OpenNMS(R) 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 OpenNMS(R). If not, see: * http://www.gnu.org/licenses/ * * For more information contact: * OpenNMS(R) Licensing <license@opennms.org> * http://www.opennms.org/ * http://www.opennms.com/ *******************************************************************************/ package org.opennms.netmgt.capsd.plugins; import java.net.InetAddress; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.Driver; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.HashMap; import java.util.Map; import java.util.Properties; import org.opennms.core.utils.DBTools; import org.opennms.core.utils.LogUtils; import org.opennms.core.utils.ParameterMap; import org.opennms.netmgt.capsd.AbstractPlugin; /** * This OpenNMS capsd plugin checks if a given server is running a server that * can talk JDBC on a given interface. If true then the interface is "saved" for * future service state checking. This plugin is slow; Stablishing a connection * between the client and the server is an slow operation. A connection pool * doesn't make any sense when discovering a database, Also opening and closing * a connection every time helps to discover problems like a RDBMS running out * of connections. * <p> * More plugin information available at: <a * href="http://www.opennms.org/users/docs/docs/html/devref.html">OpenNMS * developer site </a> * </p> * * @author Jose Vicente Nunez Zuleta (josevnz@users.sourceforge.net) - RHCE, * SJCD, SJCP * @version 0.1 - 07/22/2002 * @since 0.1 */ public class JDBCPlugin extends AbstractPlugin { /** * The protocol supported by the plugin */ private final static String PROTOCOL_NAME = "JDBC"; /** * Default number of retries for TCP requests */ private final static int DEFAULT_RETRY = 0; /** * Default timeout (in milliseconds) for TCP requests */ private final static int DEFAULT_TIMEOUT = 5000; // in milliseconds /** * Class constructor. Load the JDBC drivers. */ public JDBCPlugin() { super(); LogUtils.debugf(this, "JDBCPlugin class loaded"); } /** * Checks if a given server is listening o a given interface * @param hostname * name of the RDBMS server * @param user * Database user * @param password * Database password * @param db_url * Database connection url * @param timeout * Default login timeout * @param retries * Number of retrys before giving up a connection attempts * @param db_driver * JDBC driver to use * * @see DBTools#constructUrl */ private boolean isServer(String hostname, Map<String, Object> qualifiers) { String user = ParameterMap.getKeyedString(qualifiers, "user", DBTools.DEFAULT_DATABASE_USER); String password = ParameterMap.getKeyedString(qualifiers, "password", DBTools.DEFAULT_DATABASE_PASSWORD); String db_url = ParameterMap.getKeyedString(qualifiers, "url", DBTools.DEFAULT_URL); int timeout = ParameterMap.getKeyedInteger(qualifiers, "timeout", DEFAULT_TIMEOUT); int retries = ParameterMap.getKeyedInteger(qualifiers, "retry", DEFAULT_RETRY); String db_driver = ParameterMap.getKeyedString(qualifiers, "driver", DBTools.DEFAULT_JDBC_DRIVER); boolean status = false; Connection con = null; Statement statement = null; boolean connected = false; for (int attempts = 0; attempts <= retries && !connected;) { LogUtils.infof(this, "Trying to detect JDBC server on '%s', attempt #: %d", hostname, attempts); try { LogUtils.infof(this, "Loading JDBC driver: '%s'", db_driver); Driver driver = (Driver)Class.forName(db_driver).newInstance(); LogUtils.debugf(this, "JDBC driver loaded: '%s'", db_driver); String url = DBTools.constructUrl(db_url, hostname); LogUtils.debugf(this, "Constructed JDBC url: '%s'", url); Properties props = new Properties(); props.setProperty("user", user); props.setProperty("password", password); props.setProperty("timeout", String.valueOf(timeout/1000)); con = driver.connect(url, props); connected = true; LogUtils.debugf(this, "Got database connection: '%s' (%s, %s, %s)", con, url, user, password); status = checkStatus(con, qualifiers); if (status) LogUtils.infof(this, "JDBC server detected on: '%s', attempt #: %d", hostname, attempts); } catch (final Exception e) { LogUtils.infof(this, e, "failed to make JDBC connection"); } finally { attempts++; closeStmt(statement); closeConn(con); } } return status; } /** * <p>checkStatus</p> * * @param con a {@link java.sql.Connection} object. * @param qualifiers a {@link java.util.Map} object. * @return a boolean. */ public boolean checkStatus(Connection con, Map<String, Object> qualifiers ) { boolean status = false; ResultSet result = null; try { DatabaseMetaData metadata = con.getMetaData(); LogUtils.debugf(this, "Got database metadata"); result = metadata.getCatalogs(); while (result.next()) { result.getString(1); LogUtils.debugf(this, "Metadata catalog: '%s'", result.getString(1)); } // The JDBC server was detected using JDBC, update the status if ( result != null ) status = true; } catch ( SQLException sqlException ) { LogUtils.warnf(this, sqlException, "error while getting database metadata"); } finally { closeResult(result); } return status; } private void closeConn(Connection con) { if (con != null) { try { con.close(); } catch (SQLException ignore) { } } } /** * <p>closeStmt</p> * * @param statement a {@link java.sql.Statement} object. */ protected void closeStmt(Statement statement) { if (statement != null) { try { statement.close(); } catch (SQLException ignore) { } } } private void closeResult(ResultSet result) { if (result != null) { try { result.close(); } catch (SQLException ignore) { } } } /** * Returns the default protocol name * * @return String Protocol Name */ public String getProtocolName() { return PROTOCOL_NAME; } /** * {@inheritDoc} * * Default checking method, asuming all the parameters by default. This * method is likely to skip some machines because the default password is * empty. is recomended to use the parametric method instead (unless your * DBA is dummy enugh to leave a JDBC server with no password!!!). */ public boolean isProtocolSupported(InetAddress address) { boolean status = false; try { status = isServer(address.getCanonicalHostName(), new HashMap<String, Object>()); } catch (final Exception exp) { LogUtils.errorf(this, exp, "an error occurred while checking whether the protocol is supported"); } return status; } /** * {@inheritDoc} * * Checking method, receives all the parameters as a Map. Currently * supported: * <ul> * <li><b>port </b>- Port where the JDBC server is listening (defaults to * DEFAULT_PORT). Type: Integer * <li><b>user </b>- Database user (defaults to DEFAULT_DATABASE_USER if * not provided). Type String * <li><b>password </b>- Database password (defaults to * DEFAULT_DATABASE_PASSWORD). Type String * <li><b>timeout </b>- Timeout * <li><b>retry </b>- How many times will try to check for the service * </ul> */ public boolean isProtocolSupported(InetAddress address, Map<String, Object> qualifiers) { boolean status = false; if (address == null) { throw new NullPointerException(getClass().getName() + ": Internet address cannot be null"); } if (qualifiers == null) { throw new NullPointerException(getClass().getName() + ": Map argument cannot be null"); } try { status = isServer(address.getCanonicalHostName(), qualifiers); } catch (final Exception exp) { LogUtils.errorf(this, exp, "an error occurred while checking if the protocol is supported"); } return status; } } // End of class