package rocks.inspectit.server.processor.impl; import java.util.List; import javax.persistence.EntityManager; import org.apache.commons.collections.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import rocks.inspectit.server.processor.AbstractChainedCmrDataProcessor; import rocks.inspectit.server.processor.AbstractCmrDataProcessor; import rocks.inspectit.shared.all.communication.DefaultData; import rocks.inspectit.shared.all.communication.ExceptionEvent; import rocks.inspectit.shared.all.communication.data.ExceptionSensorData; import rocks.inspectit.shared.all.communication.data.InvocationSequenceData; import rocks.inspectit.shared.all.communication.data.SqlStatementData; import rocks.inspectit.shared.all.communication.data.TimerData; import rocks.inspectit.shared.cs.communication.data.InvocationSequenceDataHelper; /** * Processor performing necessary calculation and fixes. This is special type of chained processor * that does not pass the incoming object to the chained processors, but might do so with some other * objects. * * @author Ivan Senic * */ public class InvocationModifierCmrProcessor extends AbstractChainedCmrDataProcessor { /** * Message processor for exception that we need to call directly. It's because we need to do * that for all exceptions, but we will send only one to the chained processors, cause in the * chain there will be indexed and stuff and we don’t want that for all exceptions, but only * that survive constructor delegation. */ @Autowired ExceptionMessageCmrProcessor exceptionMessageCmrProcessor; /** * Default constructor. * * @param dataProcessors * Chained processors. */ public InvocationModifierCmrProcessor(List<AbstractCmrDataProcessor> dataProcessors) { super(dataProcessors); } /** * {@inheritDoc} */ @Override protected void processData(DefaultData defaultData, EntityManager entityManager) { InvocationSequenceData invocation = (InvocationSequenceData) defaultData; extractDataFromInvocation(entityManager, invocation, invocation); } /** * {@inheritDoc} */ @Override protected boolean shouldBePassedToChainedProcessors(DefaultData defaultData) { return false; } /** * {@inheritDoc} */ @Override public boolean canBeProcessed(DefaultData defaultData) { return defaultData instanceof InvocationSequenceData; } /** * Extract data from the invocation in the way that timer data is saved to the Db, while SQL * statements and Exceptions are indexed into the root branch. * * @param entityManager * {@link EntityManager} needed for DB persistence. * @param invData * Invocation data to be extracted. * @param topInvocationParent * Top invocation object. * */ private void extractDataFromInvocation(EntityManager entityManager, InvocationSequenceData invData, InvocationSequenceData topInvocationParent) { double exclusiveDurationDelta = 0d; for (InvocationSequenceData child : invData.getNestedSequences()) { // pass child to chained processors passToChainedProcessors(child, entityManager); // include times from timer, sql or invocation itself if (null != child.getTimerData()) { exclusiveDurationDelta += child.getTimerData().getDuration(); } else if (null != child.getSqlStatementData()) { // I don't know if the situation that both timer and sql are set in one // invocation, but just to be sure I only include the time of the sql, if i did // not already included the time of the timer before exclusiveDurationDelta += child.getSqlStatementData().getDuration(); } else { exclusiveDurationDelta += InvocationSequenceDataHelper.computeNestedDuration(child); } // go to the recursion extractDataFromInvocation(entityManager, child, topInvocationParent); } // process the SQL Statement and Timer processSqlStatementData(entityManager, invData, topInvocationParent); processTimerData(entityManager, invData, topInvocationParent, exclusiveDurationDelta); processExceptionSensorData(entityManager, invData, topInvocationParent); } /** * Process SQL statement if one exists in the invData object and passes it to the chained * processors. * * @param entityManager * {@link EntityManager} needed for DB persistence. * @param invData * Invocation data to be processed. * @param topInvocationParent * Top invocation object. */ private void processSqlStatementData(EntityManager entityManager, InvocationSequenceData invData, InvocationSequenceData topInvocationParent) { SqlStatementData sqlStatementData = invData.getSqlStatementData(); if (null != sqlStatementData) { topInvocationParent.setNestedSqlStatements(Boolean.TRUE); sqlStatementData.addInvocationParentId(topInvocationParent.getId()); passToChainedProcessors(sqlStatementData, entityManager); } } /** * Process timer data if one exists in the invData object and passes it to the chained * processors. * * @param entityManager * {@link EntityManager} needed for DB persistence. * @param invData * Invocation data to be processed. * @param topInvocationParent * Top invocation object. * @param exclusiveDurationDelta * Duration to subtract from timer duration to get the exclusive duration. */ private void processTimerData(EntityManager entityManager, InvocationSequenceData invData, InvocationSequenceData topInvocationParent, double exclusiveDurationDelta) { TimerData timerData = invData.getTimerData(); if (null != timerData) { double exclusiveTime = invData.getTimerData().getDuration() - exclusiveDurationDelta; timerData.setExclusiveCount(1L); timerData.setExclusiveDuration(exclusiveTime); timerData.calculateExclusiveMax(exclusiveTime); timerData.calculateExclusiveMin(exclusiveTime); timerData.addInvocationParentId(topInvocationParent.getId()); passToChainedProcessors(invData.getTimerData(), entityManager); } } /** * Process all the exceptions in the invData and passes exceptions to the chained processors. * <br> * <br> * Note also that only exception data with CREATED event are processed, since the PASSED and * HANDLED should be connected as children to the CREATED one. * * @param entityManager * {@link EntityManager} needed for DB persistence. * @param invData * Invocation data to be processed. * @param topInvocationParent * Top invocation object. */ private void processExceptionSensorData(EntityManager entityManager, InvocationSequenceData invData, InvocationSequenceData topInvocationParent) { if (CollectionUtils.isNotEmpty(invData.getExceptionSensorDataObjects())) { for (ExceptionSensorData exceptionData : invData.getExceptionSensorDataObjects()) { if (exceptionData.getExceptionEvent() == ExceptionEvent.CREATED) { // only if created exception is in invocation set to the parent topInvocationParent.setNestedExceptions(Boolean.TRUE); // we need to directly call Exception message processor, cause it can not be // chained exceptionMessageCmrProcessor.process(exceptionData, entityManager); exceptionData.addInvocationParentId(topInvocationParent.getId()); passToChainedProcessors(exceptionData, entityManager); } } } } }