/*
* Copyright 2011-2016 the original author or authors.
*
* 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 org.glowroot.agent.plugin.jdbc;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.annotation.Nonnull;
import org.glowroot.agent.plugin.api.Agent;
import org.glowroot.agent.plugin.api.Logger;
import org.glowroot.agent.plugin.api.QueryEntry;
import org.glowroot.agent.plugin.api.Timer;
import org.glowroot.agent.plugin.api.config.BooleanProperty;
import org.glowroot.agent.plugin.api.config.ConfigService;
import org.glowroot.agent.plugin.api.weaving.BindReceiver;
import org.glowroot.agent.plugin.api.weaving.BindReturn;
import org.glowroot.agent.plugin.api.weaving.BindTraveler;
import org.glowroot.agent.plugin.api.weaving.IsEnabled;
import org.glowroot.agent.plugin.api.weaving.OnAfter;
import org.glowroot.agent.plugin.api.weaving.OnBefore;
import org.glowroot.agent.plugin.api.weaving.OnReturn;
import org.glowroot.agent.plugin.api.weaving.Pointcut;
import org.glowroot.agent.plugin.jdbc.StatementAspect.HasStatementMirror;
public class ResultSetAspect {
private static final Logger logger = Agent.getLogger(ResultSetAspect.class);
private static final ConfigService configService = Agent.getConfigService("jdbc");
@Pointcut(className = "java.sql.ResultSet", methodName = "next", methodParameterTypes = {},
nestingGroup = "jdbc")
public static class NextAdvice {
private static final BooleanProperty timerEnabled =
configService.getBooleanProperty("captureResultSetNavigate");
@IsEnabled
public static boolean isEnabled(@BindReceiver HasStatementMirror resultSet) {
return timerEnabled.value() && isEnabledCommon(resultSet);
}
@OnBefore
public static Timer onBefore(@BindReceiver HasStatementMirror resultSet) {
return onBeforeCommon(resultSet);
}
@OnReturn
public static void onReturn(@BindReturn boolean currentRowValid,
@BindReceiver HasStatementMirror resultSet) {
StatementMirror mirror = resultSet.glowroot$getStatementMirror();
if (mirror == null) {
// this shouldn't happen since just checked above in isEnabled(), unless some
// bizarre concurrent mis-usage of ResultSet
return;
}
QueryEntry lastQueryEntry = mirror.getLastQueryEntry();
if (lastQueryEntry == null) {
// tracing must be disabled (e.g. exceeded trace entry limit)
return;
}
if (currentRowValid) {
// ResultSet.getRow() is sometimes not super duper fast due to ResultSet
// wrapping and other checks, so this optimizes the common case
lastQueryEntry.incrementCurrRow();
} else {
lastQueryEntry.rowNavigationAttempted();
}
}
@OnAfter
public static void onAfter(@BindTraveler Timer timer) {
timer.stop();
}
}
@Pointcut(className = "java.sql.ResultSet",
methodName = "previous|relative|absolute|first|last", methodParameterTypes = "..",
nestingGroup = "jdbc")
public static class NavigateAdvice {
private static final BooleanProperty timerEnabled =
configService.getBooleanProperty("captureResultSetNavigate");
@IsEnabled
public static boolean isEnabled(@BindReceiver HasStatementMirror resultSet) {
return timerEnabled.value() && isEnabledCommon(resultSet);
}
@OnBefore
public static Timer onBefore(@BindReceiver HasStatementMirror resultSet) {
return onBeforeCommon(resultSet);
}
@OnReturn
public static void onReturn(@BindReceiver HasStatementMirror resultSet) {
try {
StatementMirror mirror = resultSet.glowroot$getStatementMirror();
if (mirror == null) {
// this shouldn't happen since just checked above in isEnabled(), unless some
// bizarre concurrent mis-usage of ResultSet
return;
}
QueryEntry lastQueryEntry = mirror.getLastQueryEntry();
if (lastQueryEntry == null) {
// tracing must be disabled (e.g. exceeded trace entry limit)
return;
}
lastQueryEntry.setCurrRow(((ResultSet) resultSet).getRow());
} catch (SQLException e) {
logger.warn(e.getMessage(), e);
}
}
@OnAfter
public static void onAfter(@BindTraveler Timer timer) {
timer.stop();
}
}
@Pointcut(className = "java.sql.ResultSet", methodName = "get*",
methodParameterTypes = {"int", ".."}, nestingGroup = "jdbc")
public static class ValueAdvice {
private static final BooleanProperty timerEnabled =
configService.getBooleanProperty("captureResultSetGet");
@IsEnabled
public static boolean isEnabled(@BindReceiver HasStatementMirror resultSet) {
return timerEnabled.value() && isEnabledCommon(resultSet);
}
@OnBefore
public static Timer onBefore(@BindReceiver HasStatementMirror resultSet) {
return onBeforeCommon(resultSet);
}
@OnAfter
public static void onAfter(@BindTraveler Timer timer) {
timer.stop();
}
}
@Pointcut(className = "java.sql.ResultSet", methodName = "get*",
methodParameterTypes = {"java.lang.String", ".."}, nestingGroup = "jdbc")
public static class ValueAdvice2 {
private static final BooleanProperty timerEnabled =
configService.getBooleanProperty("captureResultSetGet");
@IsEnabled
public static boolean isEnabled(@BindReceiver HasStatementMirror resultSet) {
return timerEnabled.value() && isEnabledCommon(resultSet);
}
@OnBefore
public static Timer onBefore(@BindReceiver HasStatementMirror resultSet) {
return onBeforeCommon(resultSet);
}
@OnAfter
public static void onAfter(@BindTraveler Timer timer) {
timer.stop();
}
}
private static boolean isEnabledCommon(HasStatementMirror resultSet) {
StatementMirror mirror = resultSet.glowroot$getStatementMirror();
return mirror != null && mirror.getLastQueryEntry() != null;
}
private static Timer onBeforeCommon(HasStatementMirror resultSet) {
@SuppressWarnings("nullness") // just checked above in isEnabledCommon()
@Nonnull
StatementMirror mirror = resultSet.glowroot$getStatementMirror();
@SuppressWarnings("nullness") // just checked above in isEnabledCommon()
@Nonnull
QueryEntry lastQueryEntry = mirror.getLastQueryEntry();
return lastQueryEntry.extend();
}
}