All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.log4jdbc.sql.jdbcapi.DriverSpy Maven / Gradle / Ivy

/**
 * Copyright 2007-2012 Arthur Blake
 *
 * Licensed 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 net.sf.log4jdbc.sql.jdbcapi;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import net.sf.log4jdbc.Properties;
import net.sf.log4jdbc.log.SpyLogDelegator;
import net.sf.log4jdbc.log.SpyLogFactory;
import net.sf.log4jdbc.sql.Spy;
import net.sf.log4jdbc.sql.rdbmsspecifics.MySqlRdbmsSpecifics;
import net.sf.log4jdbc.sql.rdbmsspecifics.OracleRdbmsSpecifics;
import net.sf.log4jdbc.sql.rdbmsspecifics.RdbmsSpecifics;
import net.sf.log4jdbc.sql.rdbmsspecifics.SqlServerRdbmsSpecifics;


/**
 * A JDBC driver which is a facade that delegates to one or more real underlying
 * JDBC drivers.  The driver will spy on any other JDBC driver that is loaded,
 * simply by prepending jdbc:log4 to the normal jdbc driver URL
 * used by any other JDBC driver. The driver, by default, also loads several
 * well known drivers at class load time, so that this driver can be
 * "dropped in" to any Java program that uses these drivers without making any
 * code changes.
 * 

* The well known driver classes that are loaded are: *

*

* *

    *
  • oracle.jdbc.driver.OracleDriver
  • *
  • com.sybase.jdbc2.jdbc.SybDriver
  • *
  • net.sourceforge.jtds.jdbc.Driver
  • *
  • com.microsoft.jdbc.sqlserver.SQLServerDriver
  • *
  • com.microsoft.sqlserver.jdbc.SQLServerDriver
  • *
  • weblogic.jdbc.sqlserver.SQLServerDriver
  • *
  • com.informix.jdbc.IfxDriver
  • *
  • org.apache.derby.jdbc.ClientDriver
  • *
  • org.apache.derby.jdbc.EmbeddedDriver
  • *
  • com.mysql.jdbc.Driver
  • *
  • org.postgresql.Driver
  • *
  • org.hsqldb.jdbcDriver
  • *
  • org.h2.Driver
  • *
* *

*

* Additional drivers can be set via a property: log4jdbc.drivers * This can be either a single driver class name or a list of comma separated * driver class names. *

* The autoloading behavior can be disabled by setting a property: * log4jdbc.auto.load.popular.drivers to false. If that is done, then * the only drivers that log4jdbc will attempt to load are the ones specified * in log4jdbc.drivers. *

* If any of the above driver classes cannot be loaded, the driver continues on * without failing. *

* Note that the getMajorVersion, getMinorVersion and * jdbcCompliant method calls attempt to delegate to the last * underlying driver requested through any other call that accepts a JDBC URL. *

* This can cause unexpected behavior in certain circumstances. For example, * if one of these 3 methods is called before any underlying driver has been * established, then they will return default values that might not be correct * in all situations. Similarly, if this spy driver is used to spy on more than * one underlying driver concurrently, the values returned by these 3 method * calls may change depending on what the last underlying driver used was at the * time. This will not usually be a problem, since the driver is retrieved by * it's URL from the DriverManager in the first place (thus establishing an * underlying real driver), and in most applications their is only one database. * *

Modifications for log4j2:

*
    *
  • The initialization of all properties have been delegated to the class * {@link net.sf.log4jdbc.log4j2.Properties}. *
  • Modification of the method connect(String, Properties) * in order to compute the time taken to open a connection to the database. * Constructors of ConnectionSpy have been modified accordingly. *
* * @author Arthur Blake * @author Frederic Bastian * @author Mathieu Seppey */ public class DriverSpy implements Driver { /** * The last actual, underlying driver that was requested via a URL. */ private Driver lastUnderlyingDriverRequested; /** * Maps driver class names to RdbmsSpecifics objects for each kind of * database. */ private final static Map rdbmsSpecifics; /** * Default RdbmsSpecifics. */ static final RdbmsSpecifics defaultRdbmsSpecifics = new RdbmsSpecifics(); /** * A SpyLogDelegator used here for logs internal to log4jdbc * (see debug(String) method of SpyLogDelegator). */ static final SpyLogDelegator log = SpyLogFactory.getSpyLogDelegator(); /** * A String representing the prefix of URL * to use log4jdbc. */ static final private String log4jdbcUrlPrefix = "jdbc:log4"; /** * Default constructor. */ public DriverSpy() { } /** * Static initializer. */ static { log.debug("DriverSpy intialization..."); // The Set of drivers that the log4jdbc driver will preload at instantiation // time. The driver can spy on any driver type, it's just a little bit // easier to configure log4jdbc if it's one of these types! Set subDrivers = new TreeSet(); if (Properties.isAutoLoadPopularDrivers()) { subDrivers.add("oracle.jdbc.driver.OracleDriver"); subDrivers.add("oracle.jdbc.OracleDriver"); subDrivers.add("com.sybase.jdbc2.jdbc.SybDriver"); subDrivers.add("net.sourceforge.jtds.jdbc.Driver"); // MS driver for Sql Server 2000 subDrivers.add("com.microsoft.jdbc.sqlserver.SQLServerDriver"); // MS driver for Sql Server 2005 subDrivers.add("com.microsoft.sqlserver.jdbc.SQLServerDriver"); subDrivers.add("weblogic.jdbc.sqlserver.SQLServerDriver"); subDrivers.add("com.informix.jdbc.IfxDriver"); subDrivers.add("org.apache.derby.jdbc.ClientDriver"); subDrivers.add("org.apache.derby.jdbc.EmbeddedDriver"); subDrivers.add("com.mysql.jdbc.Driver"); subDrivers.add("org.postgresql.Driver"); subDrivers.add("org.hsqldb.jdbcDriver"); subDrivers.add("org.h2.Driver"); } // look for additional driver specified in properties subDrivers.addAll(Properties.getAdditionalDrivers()); try { DriverManager.registerDriver(new DriverSpy()); } catch (SQLException s) { // this exception should never be thrown, JDBC just defines it // for completeness throw (RuntimeException) new RuntimeException ("could not register log4jdbc driver!").initCause(s); } // instantiate all the supported drivers and remove // those not found String driverClass; for (Iterator i = subDrivers.iterator(); i.hasNext();) { driverClass = i.next(); try { Class.forName(driverClass); log.debug(" FOUND DRIVER " + driverClass); } catch (Throwable c) { i.remove(); } } if (subDrivers.size() == 0) { log.debug("WARNING! " + "log4jdbc couldn't find any underlying jdbc drivers."); } SqlServerRdbmsSpecifics sqlServer = new SqlServerRdbmsSpecifics(); OracleRdbmsSpecifics oracle = new OracleRdbmsSpecifics(); MySqlRdbmsSpecifics mySql = new MySqlRdbmsSpecifics(); /** create lookup Map for specific rdbms formatters */ rdbmsSpecifics = new HashMap(); rdbmsSpecifics.put("oracle.jdbc.driver.OracleDriver", oracle); rdbmsSpecifics.put("oracle.jdbc.OracleDriver", oracle); rdbmsSpecifics.put("net.sourceforge.jtds.jdbc.Driver", sqlServer); rdbmsSpecifics.put("com.microsoft.jdbc.sqlserver.SQLServerDriver", sqlServer); rdbmsSpecifics.put("weblogic.jdbc.sqlserver.SQLServerDriver", sqlServer); rdbmsSpecifics.put("com.mysql.jdbc.Driver", mySql); log.debug("DriverSpy intialization done."); } /** * Get the RdbmsSpecifics object for a given Connection. * * @param conn JDBC connection to get RdbmsSpecifics for. * @return RdbmsSpecifics for the given connection. */ static RdbmsSpecifics getRdbmsSpecifics(Connection conn) { String driverName = ""; try { DatabaseMetaData dbm = conn.getMetaData(); driverName = dbm.getDriverName(); } catch (SQLException s) { // silently fail } log.debug("driver name is " + driverName); RdbmsSpecifics r = rdbmsSpecifics.get(driverName); if (r == null) { return defaultRdbmsSpecifics; } return r; } /** * Get the major version of the driver. This call will be delegated to the * underlying driver that is being spied upon (if there is no underlying * driver found, then 1 will be returned.) * * @return the major version of the JDBC driver. */ public int getMajorVersion() { if (lastUnderlyingDriverRequested == null) { return 1; } return lastUnderlyingDriverRequested.getMajorVersion(); } /** * Get the minor version of the driver. This call will be delegated to the * underlying driver that is being spied upon (if there is no underlying * driver found, then 0 will be returned.) * * @return the minor version of the JDBC driver. */ public int getMinorVersion() { if (lastUnderlyingDriverRequested == null) { return 0; } return lastUnderlyingDriverRequested.getMinorVersion(); } /** * Report whether the underlying driver is JDBC compliant. If there is no * underlying driver, false will be returned, because the driver cannot * actually do any work without an underlying driver. * * @return true if the underlying driver is JDBC Compliant; * false otherwise. */ public boolean jdbcCompliant() { return lastUnderlyingDriverRequested != null && lastUnderlyingDriverRequested.jdbcCompliant(); } /** * Returns true if this is a jdbc:log4 URL and if the URL is for * an underlying driver that this DriverSpy can spy on. * * @param url JDBC URL. * * @return true if this Driver can handle the URL. * * @throws SQLException if a database access error occurs */ public boolean acceptsURL(String url) throws SQLException { Driver d = getUnderlyingDriver(url); if (d != null) { lastUnderlyingDriverRequested = d; return true; } return false; } /** * Given a jdbc:log4 type URL, find the underlying real driver * that accepts the URL. * * @param url JDBC connection URL. * * @return Underlying driver for the given URL. Null is returned if the URL is * not a jdbc:log4 type URL or there is no underlying * driver that accepts the URL. * * @throws SQLException if a database access error occurs. */ private Driver getUnderlyingDriver(String url) throws SQLException { if (url.startsWith(log4jdbcUrlPrefix)) { url = this.getRealUrl(url); Enumeration e = DriverManager.getDrivers(); Driver d; while (e.hasMoreElements()) { d = e.nextElement(); if (d.acceptsURL(url)) { return d; } } } return null; } /** * Get the actual URL that the real driver expects * (strip off #log4jdbcUrlPrefix from url). * * @param url A String corresponding to a JDBC url for log4jdbc. * @return A String representing url * with #log4jdbcUrlPrefix stripped off. */ private String getRealUrl(String url) { return url.substring(log4jdbcUrlPrefix.length()); } /** * Get a Connection to the database from the underlying driver that this * DriverSpy is spying on. If logging is not enabled, an actual Connection to * the database returned. If logging is enabled, a ConnectionSpy object which * wraps the real Connection is returned. * * @param url JDBC connection URL * . * @param info a list of arbitrary string tag/value pairs as * connection arguments. Normally at least a "user" and * "password" property should be included. * * @return a Connection object that represents a * connection to the URL. * * @throws SQLException if a database access error occurs */ public Connection connect(String url, java.util.Properties info) throws SQLException { Driver d = getUnderlyingDriver(url); if (d == null) { return null; } // get actual URL that the real driver expects // (strip off #log4jdbcUrlPrefix from url) url = this.getRealUrl(url); lastUnderlyingDriverRequested = d; long tstart = System.currentTimeMillis(); Connection c = d.connect(url, info); if (c == null) { throw new SQLException("invalid or unknown driver url: " + url); } if (log.isJdbcLoggingEnabled()) { ConnectionSpy cspy = new ConnectionSpy(c, System.currentTimeMillis() - tstart, log); RdbmsSpecifics r = null; String dclass = d.getClass().getName(); if (dclass != null && dclass.length() > 0) { r = rdbmsSpecifics.get(dclass); } if (r == null) { r = defaultRdbmsSpecifics; } cspy.setRdbmsSpecifics(r); return cspy; } return c; } /** * Gets information about the possible properties for the underlying driver. * * @param url the URL of the database to which to connect * * @param info a proposed list of tag/value pairs that will be sent on * connect open * @return an array of DriverPropertyInfo objects describing * possible properties. This array may be an empty array if no * properties are required. * * @throws SQLException if a database access error occurs */ public DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info) throws SQLException { Driver d = getUnderlyingDriver(url); if (d == null) { return new DriverPropertyInfo[0]; } lastUnderlyingDriverRequested = d; return d.getPropertyInfo(url, info); } protected void reportException(String methodCall, SQLException exception) { log.exceptionOccured((Spy) this, methodCall, exception, null, -1L); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy