Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2008-2019 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.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.servlet.ServletContext;
import javax.sql.DataSource;
import net.bull.javamelody.internal.common.LOG;
import net.bull.javamelody.internal.common.Parameters;
import net.bull.javamelody.internal.model.ConnectionInformations;
import net.bull.javamelody.internal.model.Counter;
/**
* Cette classe est utile pour construire des proxy de {@link DataSource}s ou de {@link Connection}s jdbc.
* Et notamment elle rebinde dans l'annuaire JNDI la dataSource jdbc en la remplaçant
* par un proxy de monitoring.
* @author Emeric Vernat
*/
public final class JdbcWrapper {
/**
* Instance singleton de JdbcWrapper (ici on ne connaît pas le ServletContext).
*/
public static final JdbcWrapper SINGLETON = new JdbcWrapper(
new Counter(Counter.SQL_COUNTER_NAME, "db.png"));
// au lieu d'utiliser int avec des synchronized partout, on utilise AtomicInteger
static final AtomicInteger ACTIVE_CONNECTION_COUNT = new AtomicInteger();
static final AtomicInteger USED_CONNECTION_COUNT = new AtomicInteger();
static final AtomicLong TRANSACTION_COUNT = new AtomicLong();
static final AtomicInteger ACTIVE_THREAD_COUNT = new AtomicInteger();
static final AtomicInteger RUNNING_BUILD_COUNT = new AtomicInteger();
static final AtomicInteger BUILD_QUEUE_LENGTH = new AtomicInteger();
static final AtomicLong BUILD_QUEUE_WAITING_DURATIONS_SUM = new AtomicLong();
static final Map USED_CONNECTION_INFORMATIONS = new ConcurrentHashMap();
private static final int MAX_USED_CONNECTION_INFORMATIONS = 500;
// Cette variable sqlCounter conserve un état qui est global au filtre et à l'application (donc thread-safe).
private final Counter sqlCounter;
private ServletContext servletContext;
private boolean connectionInformationsEnabled;
private boolean jboss;
private boolean glassfish;
private boolean weblogic;
static final class ConnectionInformationsComparator
implements Comparator, Serializable {
private static final long serialVersionUID = 1L;
/** {@inheritDoc} */
@Override
public int compare(ConnectionInformations connection1, ConnectionInformations connection2) {
return connection1.getOpeningDate().compareTo(connection2.getOpeningDate());
}
}
/**
* Handler de proxy d'un {@link Statement} jdbc.
*/
private class StatementInvocationHandler implements InvocationHandler {
// Rq : dans les proxy de DataSource, Connection et Statement,
// si la méthode appelée est java.sql.Wrapper.unwrap
// on invoque toujours unwrap sur l'objet initial pour qu'il retourne lui-même
// ou son objet wrappé. Par exemple, l'appel de unwrap sur le proxy d'un Statement
// retournera le Statement initial du serveur ou même du driver bdd (OracleStatement...)
// sans notre proxy pour pouvoir appeler les méthodes non standard du driver par ex.
private String requestName;
private final Statement statement;
StatementInvocationHandler(String query, Statement statement) {
super();
assert statement != null;
this.requestName = query;
this.statement = statement;
}
/** {@inheritDoc} */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// performance : on évite method.invoke pour equals & hashCode
final String methodName = method.getName();
if (isEqualsMethod(methodName, args)) {
return statement.equals(args[0]);
} else if (isHashCodeMethod(methodName, args)) {
return statement.hashCode();
} else if (methodName.startsWith("execute")) {
if (isFirstArgAString(args)) {
// la méthode est du type executeQuery(String), executeUpdate(String),
// executeUpdate(String, ...) ou execute(String sql),
// alors la requête sql est le premier argument (et pas query)
requestName = (String) args[0];
} else if (("executeBatch".equals(methodName)
|| "executeLargeBatch".equals(methodName)) && requestName != null
&& !requestName.startsWith("/* BATCH */ ")) {
// if executeBatch, add a prefix in the request name to explain that
// 1 batch "hit" is equivalent to several exec of the request in the db.
// requestName may be null if executeBatch()
// without prepareStatement(String) or addBatch(String)
requestName = "/* BATCH */ " + requestName;
}
// si on n'a pas trouvé la requête, on prend "null"
requestName = String.valueOf(requestName);
return doExecute(requestName, statement, method, args);
} else if ("addBatch".equals(methodName) && isFirstArgAString(args)) {
// Bien que déconseillée la méthode est addBatch(String),
// la requête sql est alors le premier argument
// (elle sera utilisée lors de l'appel à executeBatch())
// Rq : on ne conserve que la dernière requête de addBatch.
// Rq : si addBatch(String) est appelée, puis que executeUpdate(String)
// la requête du batch est correctement ignorée ci-dessus.
// Rq : si connection.prepareStatement(String).addBatch(String) puis executeUpdate()
// sont appelées (et pas executeBatch()) alors la requête conservée est
// faussement celle du batch mais l'application cloche grave.
requestName = (String) args[0];
}
// ce n'est pas une méthode executeXxx du Statement
return method.invoke(statement, args);
}
private boolean isFirstArgAString(Object[] args) {
return args != null && args.length > 0 && args[0] instanceof String;
}
}
/**
* Handler de proxy d'une {@link Connection} jdbc.
*/
private class ConnectionInvocationHandler implements InvocationHandler {
private final Connection connection;
private boolean alreadyClosed;
ConnectionInvocationHandler(Connection connection) {
super();
assert connection != null;
this.connection = connection;
}
void init() {
// on limite la taille pour éviter une éventuelle saturation mémoire
if (isConnectionInformationsEnabled()
&& USED_CONNECTION_INFORMATIONS.size() < MAX_USED_CONNECTION_INFORMATIONS) {
USED_CONNECTION_INFORMATIONS.put(
ConnectionInformations.getUniqueIdOfConnection(connection),
new ConnectionInformations());
}
USED_CONNECTION_COUNT.incrementAndGet();
TRANSACTION_COUNT.incrementAndGet();
}
/** {@inheritDoc} */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// performance : on évite method.invoke pour equals & hashCode
final String methodName = method.getName();
if (isEqualsMethod(methodName, args)) {
return areConnectionsEquals(args[0]);
} else if (isHashCodeMethod(methodName, args)) {
return connection.hashCode();
}
try {
Object result = method.invoke(connection, args);
if (result instanceof Statement) {
final String requestName;
if ("prepareStatement".equals(methodName) || "prepareCall".equals(methodName)) {
// la méthode est du type prepareStatement(String) ou prepareCall(String),
// alors la requête sql est le premier argument
requestName = (String) args[0];
} else {
requestName = null;
}
result = createStatementProxy(requestName, (Statement) result);
}
return result;
} finally {
if ("close".equals(methodName) && !alreadyClosed) {
USED_CONNECTION_COUNT.decrementAndGet();
USED_CONNECTION_INFORMATIONS
.remove(ConnectionInformations.getUniqueIdOfConnection(connection));
alreadyClosed = true;
}
}
}
private boolean areConnectionsEquals(Object object) {
// Special case if what we're being passed is one of our proxies (specifically a connection proxy)
// This way the equals call is truely transparent for our proxies (cf issue 78)
if (Proxy.isProxyClass(object.getClass())) {
final InvocationHandler invocationHandler = Proxy.getInvocationHandler(object);
if (invocationHandler instanceof DelegatingInvocationHandler) {
final DelegatingInvocationHandler d = (DelegatingInvocationHandler) invocationHandler;
if (d.getDelegate() instanceof ConnectionInvocationHandler) {
final ConnectionInvocationHandler c = (ConnectionInvocationHandler) d
.getDelegate();
return connection.equals(c.connection);
}
}
}
return connection.equals(object);
}
}
private static class ConnectionManagerInvocationHandler
extends AbstractInvocationHandler