net.bull.javamelody.JdbcDriver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javamelody-core Show documentation
Show all versions of javamelody-core Show documentation
Monitoring of JavaEE applications
/*
* Copyright 2008-2017 by Emeric Vernat
*
* This file is part of Java Melody.
*
* 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.bull.javamelody;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Collections;
import java.util.Properties;
import java.util.logging.Logger;
import net.bull.javamelody.internal.common.LOG;
import net.bull.javamelody.internal.common.Parameters;
/**
* Driver jdbc "proxy" pour le monitoring.
* C'est la classe de ce driver qui doit être déclarée si un driver jdbc et non une dataSource est utilisé.
* Dans la déclaration une propriété jdbc 'driver' doit contenir le nom de la classe du vrai driver.
* D'autres propriétés jdbc comme url, username ou password peuvent être déclarées.
* @author Emeric Vernat
*/
public class JdbcDriver implements Driver {
// cette classe est publique pour être déclarée dans une configuration jdbc
static final JdbcDriver SINGLETON = new JdbcDriver();
// initialisation statique du driver
static {
try {
DriverManager.registerDriver(SINGLETON);
LOG.debug("JDBC driver registered");
// on désinstalle et on réinstalle les autres drivers pour que le notre soit en premier
// (notamment, si le jar du driver contient un fichier java.sql.Driver dans META-INF/services
// pour initialiser automatiquement le driver contenu dans le jar)
for (final Driver driver : Collections.list(DriverManager.getDrivers())) {
if (driver != SINGLETON) {
DriverManager.deregisterDriver(driver);
DriverManager.registerDriver(driver);
}
}
} catch (final SQLException e) {
// ne peut arriver
throw new IllegalStateException(e);
}
}
/** {@inheritDoc} */
@Override
public Connection connect(String url, Properties info) throws SQLException {
if ("false".equals(info.get("javamelody"))) {
// if property javamelody=false then it's not for us
// (we pass here from the DriverManager.getConnection below)
return null;
}
String myUrl = url;
// we load first the driver class from the info or the url, to be sure that it will be found
String proxiedDriver = info.getProperty("driver");
if (proxiedDriver == null && myUrl != null) {
// if not in the info, the driver class could also be passed at the end of the url, for example ...?driver=org.h2.Driver
final int index = myUrl.indexOf("driver=");
if (index != -1) {
proxiedDriver = myUrl.substring(index + "driver=".length());
myUrl = myUrl.substring(0, index - 1);
}
}
if (proxiedDriver == null) {
// if the driver is not defined in the info or in the url
// it could still be found automatically if the driver is in the classpath
// or (in WEB-INF/lib and if the jdbc drivers are not loaded by the JDK before this webapp)
// but we don't want to create proxies and increment counts for the connections inside datasources
// so we only accept and go further if driver is defined in the info or in the url
return null;
}
try {
// on utilise Thread.currentThread().getContextClassLoader() car le driver peut ne pas être
// dans le même classLoader que les classes de javamelody
// Class driverClass =
Class.forName(proxiedDriver, true, Thread.currentThread().getContextClassLoader());
// et non Class.forName(proxiedDriver);
} catch (final ClassNotFoundException e) {
throw new SQLException(e.getMessage(), e);
}
final Properties myInfo = (Properties) info.clone();
myInfo.remove("driver");
myInfo.put("javamelody", "false");
Parameters.initJdbcDriverParameters(myUrl, myInfo);
// we could call driverClass.newInstance().connect(myUrl, myInfo)
// possibly by looking the driver which accepts the url in DriverManager.getDrivers()
// but we prefer calling the standard DriverManager.getConnection(myUrl, myInfo)
return JdbcWrapper.SINGLETON
.createConnectionProxy(DriverManager.getConnection(myUrl, myInfo));
}
/** {@inheritDoc} */
@Override
public boolean acceptsURL(String url) throws SQLException {
// test sur dbcp nécessaire pour le cas où le monitoring est utilisé avec le web.xml global
// et le répertoire lib global de tomcat et également pour les anomalies 1&2 (sonar, grails)
// (rq: Thread.currentThread().getStackTrace() a été mesuré à environ 3 micro-secondes)
for (final StackTraceElement element : Thread.currentThread().getStackTrace()) {
if (element.getClassName().endsWith("dbcp.BasicDataSource")) {
return false;
}
}
return true;
}
/** {@inheritDoc} */
@Override
public int getMajorVersion() {
return -1;
}
/** {@inheritDoc} */
@Override
public int getMinorVersion() {
return -1;
}
/** {@inheritDoc} */
@Override
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
return new DriverPropertyInfo[0];
}
/** {@inheritDoc} */
@Override
public boolean jdbcCompliant() {
return true;
}
/** {@inheritDoc} */
@Override
public String toString() {
return getClass().getSimpleName() + "[lastConnectUrl=" + Parameters.getLastConnectUrl()
+ ", lastConnectInfo=" + Parameters.getLastConnectInfo() + ']';
}
/**
* Définition de la méthode getParentLogger ajoutée dans l'interface Driver en jdk 1.7.
* @return Logger
* @throws SQLFeatureNotSupportedException e
*/
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
}
}