/** * @author Trask Stalnaker * @since 0.5 */ public class ResultSetAspect { private static final Logger logger = LoggerFactory.getLogger(ResultSetAspect.class); private static final PluginServices pluginServices = PluginServices.get("jdbc"); @Pointcut( typeName = "java.sql.ResultSet", methodName = "next|previous|relative|absolute|first|last", methodArgs = "..", captureNested = false, metricName = "jdbc resultset navigate") public static class NavigateAdvice { private static final MetricName metricName = pluginServices.getMetricName(NavigateAdvice.class); private static volatile boolean pluginEnabled; // plugin configuration property captureResultSetNext is cached to limit map lookups private static volatile boolean metricEnabled; static { pluginServices.registerConfigListener( new ConfigListener() { @Override public void onChange() { pluginEnabled = pluginServices.isEnabled(); metricEnabled = pluginEnabled && pluginServices.getBooleanProperty("captureResultSetNext"); } }); pluginEnabled = pluginServices.isEnabled(); metricEnabled = pluginEnabled && pluginServices.getBooleanProperty("captureResultSetNext"); } @IsEnabled public static boolean isEnabled() { // don't capture if implementation detail of a DatabaseMetaData method return pluginEnabled && !DatabaseMetaDataAspect.isCurrentlyExecuting(); } @OnBefore @Nullable public static MetricTimer onBefore() { if (metricEnabled) { return pluginServices.startMetricTimer(metricName); } else { return null; } } @OnReturn public static void onReturn( @BindReturn boolean currentRowValid, @BindTarget ResultSet resultSet) { try { Statement statement = resultSet.getStatement(); if (statement == null) { // this is not a statement execution, it is some other execution of // ResultSet.next(), e.g. Connection.getMetaData().getTables().next() return; } StatementMirror mirror = getStatementMirror(statement); JdbcMessageSupplier lastJdbcMessageSupplier = mirror.getLastJdbcMessageSupplier(); if (lastJdbcMessageSupplier == null) { // tracing must be disabled (e.g. exceeded span limit per trace) return; } if (currentRowValid) { lastJdbcMessageSupplier.updateNumRows(resultSet.getRow()); } else { lastJdbcMessageSupplier.setHasPerformedNavigation(); } } catch (SQLException e) { logger.warn(e.getMessage(), e); } } @OnAfter public static void onAfter(@BindTraveler @Nullable MetricTimer metricTimer) { if (metricTimer != null) { metricTimer.stop(); } } } @Pointcut( typeName = "java.sql.ResultSet", methodName = "get*", methodArgs = {"int", ".."}, metricName = "jdbc resultset value") public static class ValueAdvice { private static final MetricName metricName = pluginServices.getMetricName(ValueAdvice.class); // plugin configuration property captureResultSetGet is cached to limit map lookups private static volatile boolean metricEnabled; static { pluginServices.registerConfigListener( new ConfigListener() { @Override public void onChange() { metricEnabled = pluginServices.isEnabled() && pluginServices.getBooleanProperty("captureResultSetGet"); } }); metricEnabled = pluginServices.isEnabled() && pluginServices.getBooleanProperty("captureResultSetGet"); } @IsEnabled public static boolean isEnabled() { // don't capture if implementation detail of a DatabaseMetaData method return metricEnabled && !DatabaseMetaDataAspect.isCurrentlyExecuting(); } @OnBefore public static MetricTimer onBefore() { return pluginServices.startMetricTimer(metricName); } @OnAfter public static void onAfter(@BindTraveler MetricTimer metricTimer) { metricTimer.stop(); } } @Pointcut( typeName = "java.sql.ResultSet", methodName = "get*", methodArgs = {"java.lang.String", ".."}, metricName = "jdbc resultset value") public static class ValueAdvice2 { private static final MetricName metricName = pluginServices.getMetricName(ValueAdvice2.class); // plugin configuration property captureResultSetGet is cached to limit map lookups private static volatile boolean metricEnabled; static { pluginServices.registerConfigListener( new ConfigListener() { @Override public void onChange() { metricEnabled = pluginServices.isEnabled() && pluginServices.getBooleanProperty("captureResultSetGet"); } }); metricEnabled = pluginServices.isEnabled() && pluginServices.getBooleanProperty("captureResultSetGet"); } @IsEnabled public static boolean isEnabled() { // don't capture if implementation detail of a DatabaseMetaData method return metricEnabled && !DatabaseMetaDataAspect.isCurrentlyExecuting(); } @OnBefore public static MetricTimer onBefore() { return pluginServices.startMetricTimer(metricName); } @OnAfter public static void onAfter(@BindTraveler MetricTimer metricTimer) { metricTimer.stop(); } } private static StatementMirror getStatementMirror(Statement statement) { StatementMirror mirror = ((HasStatementMirror) statement).getGlowrootStatementMirror(); if (mirror == null) { mirror = new StatementMirror(); ((HasStatementMirror) statement).setGlowrootStatementMirror(mirror); } return mirror; } }