All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.anotheria.moskito.sql.callingAspect.ConnectionCallAspect Maven / Gradle / Ivy

The newest version!
package net.anotheria.moskito.sql.callingAspect;

import net.anotheria.moskito.core.calltrace.CurrentlyTracedCall;
import net.anotheria.moskito.core.calltrace.RunningTraceContainer;
import net.anotheria.moskito.core.calltrace.TraceStep;
import net.anotheria.moskito.core.calltrace.TracedCall;
import net.anotheria.moskito.core.dynamic.EntryCountLimitedOnDemandStatsProducer;
import net.anotheria.moskito.core.dynamic.OnDemandStatsProducer;
import net.anotheria.moskito.core.dynamic.OnDemandStatsProducerException;
import net.anotheria.moskito.core.registry.ProducerRegistryFactory;
import net.anotheria.moskito.sql.stats.QueryStats;
import net.anotheria.moskito.sql.stats.QueryStatsFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Aspect used to intercept  SQL query calls.
 *
 * @author Vitaliy Zhovtiuk
 *         Date: 11/29/11
 *         Time: 2:14 PM
 */
@Aspect
public class ConnectionCallAspect {

	private static Logger log = LoggerFactory.getLogger(ConnectionCallAspect.class);

	/**
	 * List of jdbc calls for interception.
	 */
	private static final String SQL_STATEMENT_EXECUTE_CALLS = "(call(java.sql.ResultSet java.sql.Statement.executeQuery(String))" +
			"|| call(int java.sql.Statement.executeUpdate(String))" +
			"|| call(int java.sql.Statement.executeUpdate(String, int))" +
			"|| call(int java.sql.Statement.executeUpdate(String, int[]))" +
			"|| call(int java.sql.Statement.executeUpdate(String, String[]))" +
			"|| call(boolean java.sql.Statement.execute(String))" +
			"|| call(boolean java.sql.Statement.execute(String,int))" +
			"|| call(boolean java.sql.Statement.execute(String,int[]))" +
			"|| call(boolean java.sql.Statement.execute(String,String[]))" +
			"|| execution(long java.sql.Statement.executeLargeUpdate(String)) " +
			"|| execution(long java.sql.Statement.executeLargeUpdate(String,String[])) " +
			"|| execution(long java.sql.Statement.executeLargeUpdate(String, int)) " +
			"|| execution(long java.sql.Statement.executeLargeUpdate(String, int[])) " +
			')' +
			"&& args(smt)";

	private static final String SQL_PREPARED_STATEMENT_EXECUTE_CALLS = "call(boolean java.sql.PreparedStatement.execute())" +
			"|| call(long java.sql.PreparedStatement.executeLargeUpdate())" +
			"|| call(java.sql.ResultSet java.sql.PreparedStatement.executeQuery())" +
			"|| call(int java.sql.PreparedStatement.executeUpdate()) ";

	/**
	 * Query failed.
	 */
	private static final String SQL_QUERY_FAILED = " FAILED!!! ";
	/**
	 * Empty string .
	 */
	private static final String EMPTY = "";

	/**
	 * Query stats producer.
	 */
	private OnDemandStatsProducer producer;

	/**
	 * Constructor.
	 */
	public ConnectionCallAspect() {
		producer = new EntryCountLimitedOnDemandStatsProducer("SQLQueries", "sql", "sql", QueryStatsFactory.DEFAULT_INSTANCE, 1000);
		ProducerRegistryFactory.getProducerRegistryInstance().registerProducer(producer);
	}

	@Pointcut(SQL_STATEMENT_EXECUTE_CALLS)
	public void statementExecuteCalls(String smt) {
	}

	@Pointcut(SQL_PREPARED_STATEMENT_EXECUTE_CALLS)
	public void preparedStatementExecuteCalls() {
	}

	@Around(value = "statementExecuteCalls(statement)", argNames = "pjp,statement")
	public Object doBasicProfiling(ProceedingJoinPoint pjp, String statement) throws Throwable {
		return doMoskitoProfiling(pjp, statement);
	}

	@Around(value = "preparedStatementExecuteCalls()", argNames = "pjp")
	public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
		String preparedStatement = pjp.getTarget().toString();
		return doMoskitoProfiling(pjp, preparedStatement);
	}

	/* test scope */ static String removeParametersFromStatement(String statement){

		String replacement = statement.replaceAll("'.+?'", "?");
		replacement = replacement.replaceAll(",\\s*\\d+", ", ?");
		replacement = replacement.replaceAll("\\(\\s*\\d+", "(?");
		replacement = replacement.replaceAll("=\\s*\\d+", "=?");
		return replacement;
	}

	/**
	 * Perform MoSKito profiling.
	 *
	 * @param pjp       {@link ProceedingJoinPoint}
	 * @param statement sql statement
	 * @return invocation result - both with profiling
	 * @throws Throwable on errors
	 */
	private Object doMoskitoProfiling(ProceedingJoinPoint pjp, String statement) throws Throwable {
		String statementGeneralized = removeParametersFromStatement(statement);
		long callTime = System.nanoTime();
		QueryStats cumulatedStats = producer.getDefaultStats();
		QueryStats statementStats = null;
		try{
			statementStats = producer.getStats(statementGeneralized);
		}catch(OnDemandStatsProducerException limitReachedException){
			log.warn("Query limit reached for query "+statement+", --> "+statementGeneralized);
		}

		//add Request Count, increase CR,MCR
		cumulatedStats.addRequest();
		if (statementStats != null)
			statementStats.addRequest();
		// start stopwatch
		boolean success = true;
		try {
			Object retVal = pjp.proceed();
			// stop stopwatch
			return retVal;
		} catch (Throwable t) {
			success = false;
			cumulatedStats.notifyError(t);
			if (statementStats != null)
				statementStats.notifyError();
			throw t;
		} finally {
			final long callDurationTime = System.nanoTime() - callTime;
			//add execution time
			cumulatedStats.addExecutionTime(callDurationTime);
			if (statementStats != null)
				statementStats.addExecutionTime(callDurationTime);
			//notify request finished / decrease CR/MCR
			cumulatedStats.notifyRequestFinished();
			if (statementStats != null) {
				statementStats.notifyRequestFinished();
			}

			addTrace(statement, success, callDurationTime);

		}


	}

	/**
	 * Perform additional profiling - for Journey stuff.
	 *
	 * @param statement prepared statement
	 * @param isSuccess is success
	 */
	private void addTrace(String statement, final boolean isSuccess, final long duration) {
		TracedCall aRunningTrace = RunningTraceContainer.getCurrentlyTracedCall();
		CurrentlyTracedCall currentTrace = aRunningTrace.callTraced() ? (CurrentlyTracedCall) aRunningTrace : null;
		if (currentTrace != null) {
			TraceStep currentStep = currentTrace.startStep((isSuccess ? EMPTY : SQL_QUERY_FAILED) + "SQL : (' " + statement + "')", producer, statement);
			if (!isSuccess)
				currentStep.setAborted();
			currentStep.setDuration(duration);
			currentTrace.endStep();
		}

	}


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy