package org.stagemonitor.jdbc;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.stagemonitor.core.CorePlugin;
import org.stagemonitor.core.instrument.StagemonitorByteBuddyTransformer;
import java.sql.Connection;
import java.util.Collection;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.nameContains;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.none;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
public class ConnectionMonitoringTransformer extends StagemonitorByteBuddyTransformer {
private static final Logger logger = LoggerFactory.getLogger(ConnectionMonitoringTransformer.class);
protected static final ConnectionMonitor connectionMonitor;
private static final boolean active;
static {
active = ConnectionMonitor.isActive(configuration.getConfig(CorePlugin.class));
connectionMonitor = new ConnectionMonitor(configuration);
}
@Override
public ElementMatcher.Junction<TypeDescription> getIncludeTypeMatcher() {
final Collection<String> dataSourceImplementations = configuration.getConfig(JdbcPlugin.class).getDataSourceImplementations();
if (dataSourceImplementations.isEmpty()) {
// This does not work reliably as connections might be wrapped twice.
// Also, don't add isSubTypeOf(DataSource.class) as the getConnection method
// might be on a super class not implementing DataSource like in tomcat-jdbc.
return nameContains("DataSource");
}
ElementMatcher.Junction<TypeDescription> matcher = none();
for (String impl : dataSourceImplementations) {
matcher = matcher.or(named(impl));
}
return matcher;
}
@Override
protected ElementMatcher.Junction<MethodDescription> getExtraMethodElementMatcher() {
return named("getConnection")
.and(returns(Connection.class))
.and(isPublic())
.and(takesArguments(String.class, String.class).or(takesArguments(0)));
}
@Override
public boolean isActive() {
return active;
}
@Override
public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader) {
if (DEBUG_INSTRUMENTATION && typeDescription.getName().contains("DataSource")) {
if (getTypeMatcher().matches(typeDescription)) {
final boolean classLoaderMatches = getClassLoaderMatcher().matches(classLoader);
logger.warn("IGNORE {} in {}. ClassLoader: {} matches: {}", typeDescription.getName(),
getClass().getSimpleName(), classLoader, classLoaderMatches);
if (!classLoaderMatches) {
logger.warn("excluded by classloader matcher");
} else if (!getRawMatcher().matches(typeDescription, classLoader, null, null, null)) {
logger.warn("excluded by raw matcher");
} else {
logger.warn("excluded by global matcher");
}
} else {
logger.info("IGNORE {} in {}", typeDescription.getName(), getClass().getSimpleName());
}
}
}
@Override
public void beforeTransformation(TypeDescription typeDescription, ClassLoader classLoader) {
if (DEBUG_INSTRUMENTATION && logger.isDebugEnabled()) {
logger.info("TRANSFORM DataSource {} ({})", typeDescription.getName(), getClass().getSimpleName());
}
}
}