package rocks.inspectit.ui.rcp.util; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.mutable.MutableInt; import org.eclipse.jface.viewers.ViewerFilter; import rocks.inspectit.shared.all.communication.MethodSensorData; import rocks.inspectit.shared.all.communication.data.AggregatedTimerData; 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.all.tracing.data.ServerSpan; import rocks.inspectit.shared.all.tracing.data.Span; import rocks.inspectit.shared.all.tracing.data.SpanIdent; import rocks.inspectit.shared.all.util.ObjectUtils; import rocks.inspectit.shared.cs.communication.data.InvocationSequenceDataHelper; import rocks.inspectit.ui.rcp.InspectIT; /** * Factory for finding the occurrence of elements in the {@link InvocationSequenceData} based on the * element type. * * @author Ivan Senic * */ public final class OccurrenceFinderFactory { /** * String used buy templates to denote a String that will not be checked when matching. */ private static final String TEMPLATE_STRING = "TEMPLATE_STRING"; /** * Map used by templates to denote a Map that will not be checked when matching. Currently not * in use, but in future it might be needed. */ @SuppressWarnings("unused") private static final Map<Object, Object> TEMPLATE_MAP = new HashMap<>(0); /** * List used buy templates to denote a List that will not be checked when matching. */ private static final List<String> TEMPLATE_LIST = new ArrayList<>(0); /** * Set used buy templates to denote a Set that will not be checked when matching. Currently not * in use, but in future it might be needed. */ @SuppressWarnings("unused") private static final Set<Object> TEMPLATE_SET = new HashSet<>(0); /** * Private constructor because of the factory. */ private OccurrenceFinderFactory() { } /** * Occurrence finder for {@link TimerData}. */ private static TimerOccurrenceFinder timerOccurrenceFinder = new TimerOccurrenceFinder(); /** * Occurrence finder for {@link SqlStatementData}. */ private static SqlOccurrenceFinder sqlOccurrenceFinder = new SqlOccurrenceFinder(); /** * Occurrence finder for {@link ExceptionSensorData}. */ private static ExceptionOccurrenceFinder exceptionOccurrenceFinder = new ExceptionOccurrenceFinder(); /** * Occurrence finder for {@link InvocationSequenceData}. */ private static InvocationOccurenceFinder invocationOccurenceFinder = new InvocationOccurenceFinder(); /** * Occurrence finder for {@link SpanIdent}. */ private static SpanOccurenceFinder spanOccurenceFinder = new SpanOccurenceFinder(); /** * Counts number of occurrences of the element in the given invocations. * * @param invocations * Invocations to search in. * @param element * Wanted element. * @param filters * Array of filters that each found occurrence has to pass. * @return Number of occurrences found and filtered. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static ElementOccurrenceCount getOccurrenceCount(List<InvocationSequenceData> invocations, Object element, ViewerFilter[] filters) { OccurrenceFinder finder = getOccurrenceFinder(element); ElementOccurrenceCount total = new ElementOccurrenceCount(); for (InvocationSequenceData invocation : invocations) { ElementOccurrenceCount occurrenceCount = finder.getOccurrenceCount(invocation, element, filters, null); total.setFilteredOccurrences(total.getFilteredOccurrences() + occurrenceCount.getFilteredOccurrences()); total.setVisibleOccurrences(total.getVisibleOccurrences() + occurrenceCount.getTotalOccurrences()); } return total; } /** * Counts number of occurrences of the element in the given invocation. * * @param invocation * Invocation to search in. * @param element * Wanted element. * @param filters * Array of filters that each found occurrence has to pass. * @return Number of occurrences found and filtered. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static ElementOccurrenceCount getOccurrenceCount(InvocationSequenceData invocation, Object element, ViewerFilter[] filters) { OccurrenceFinder finder = getOccurrenceFinder(element); return finder.getOccurrenceCount(invocation, element, filters, null); } /** * Returns the {@link InvocationSequenceData} that holds the proper occurrence of the wanted * element if it exists. * * @param invocations * Invocations to search in. * @param element * Wanted element. * @param occurrence * Wanted occurrence. * @param filters * Array of filters that each found occurrence has to pass. * @return Returns the {@link InvocationSequenceData} that holds the proper occurrence of the * wanted element if it exists. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static InvocationSequenceData getOccurrence(List<InvocationSequenceData> invocations, Object element, int occurrence, ViewerFilter[] filters) { OccurrenceFinder finder = getOccurrenceFinder(element); MutableInt occurrencesLeft = new MutableInt(occurrence); for (InvocationSequenceData invocation : invocations) { InvocationSequenceData found = finder.getOccurrence(invocation, element, occurrencesLeft, filters); if (found != null) { return found; } } return null; } /** * Returns the {@link InvocationSequenceData} that holds the proper occurrence of the wanted * element if it exists. * * @param invocation * Invocation to search in. * @param element * Wanted element. * @param occurrence * Wanted occurrence. * @param filters * Array of filters that each found occurrence has to pass. * @return Returns the {@link InvocationSequenceData} that holds the proper occurrence of the * wanted element if it exists. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static InvocationSequenceData getOccurrence(InvocationSequenceData invocation, Object element, int occurrence, ViewerFilter[] filters) { OccurrenceFinder finder = getOccurrenceFinder(element); return finder.getOccurrence(invocation, element, new MutableInt(occurrence), filters); } /** * Returns empty template. * * @param <E> * Type of template. * @param element * For element. * @return Template to be used. */ @SuppressWarnings({ "unchecked" }) public static <E> E getEmptyTemplate(E element) { OccurrenceFinder<E> finder = getOccurrenceFinder(element); return finder.getEmptyTemplate(); } /** * Returns the proper {@link OccurrenceFinder} for the given element, based on elements class. * * @param element * Element. * @return {@link OccurrenceFinder} or null if the finder does not exists for the given object * type. */ @SuppressWarnings("rawtypes") private static OccurrenceFinder getOccurrenceFinder(Object element) { if (SqlStatementData.class.isAssignableFrom(element.getClass())) { return sqlOccurrenceFinder; } else if (element.getClass().equals(TimerData.class) || element.getClass().equals(AggregatedTimerData.class)) { return timerOccurrenceFinder; } else if (ExceptionSensorData.class.isAssignableFrom(element.getClass())) { return exceptionOccurrenceFinder; } else if (InvocationSequenceData.class.isAssignableFrom(element.getClass())) { return invocationOccurenceFinder; } else if (Span.class.isAssignableFrom(element.getClass())) { return spanOccurenceFinder; } RuntimeException exception = new RuntimeException( "Occurrence finder factory was not able to supply the correct occurrence finder for the object of class " + element.getClass().getName() + "."); InspectIT.getDefault().createErrorDialog("Exception thrown during locating of stepping object.", exception, -1); throw exception; } /** * Abstract class that holds the shared functionality of all occurrence finders. * * @author Ivan Senic * * @param <E> * Type of the element finder can locate. */ private abstract static class OccurrenceFinder<E> { /** * Returns the number of children objects in invocation sequence that have the wanted * template object defined. This method is recursive, and traverse the whole invocation * tree. * * @param invocationData * Top parent invocation sequence. * @param template * Template data to search for. * @param filters * Active filters of the tree viewer. * @param elementOccurrence * Element occurrence count. * @return Number of children in invocation that have template data set. */ public ElementOccurrenceCount getOccurrenceCount(InvocationSequenceData invocationData, E template, ViewerFilter[] filters, ElementOccurrenceCount elementOccurrence) { if (!getConcreteClass().isAssignableFrom(template.getClass())) { return null; } ElementOccurrenceCount occurrenceCount; if (null == elementOccurrence) { occurrenceCount = new ElementOccurrenceCount(); } else { occurrenceCount = elementOccurrence; } boolean found = occurrenceFound(invocationData, template); if (found && filtersPassed(invocationData, filters)) { occurrenceCount.increaseVisibleOccurrences(); } else if (found) { occurrenceCount.increaseFilteredOccurrences(); } if (null != invocationData.getNestedSequences()) { for (InvocationSequenceData child : invocationData.getNestedSequences()) { getOccurrenceCount(child, template, filters, occurrenceCount); } } return occurrenceCount; } /** * Get empty template. * * @return Empty template. */ public abstract E getEmptyTemplate(); /** * Returns the {@link InvocationSequenceData} object that has the wanted template data * object defined. The wanted occurrence of E object is defined via {@link #occurrencesLeft} * , before this method is called. This method is recursive, and stops traversing the * invocation sequence tree as soon the wanted element is found. * * @param invocationData * Top parent invocation sequence. * @param template * Template data. * @param occurrencesLeft * Occurrence to search for. * @param filters * Active filters of the tree viewer. * @return Invocation sequence that has the Exception data set in Exceptions list and is * same as template data. */ public InvocationSequenceData getOccurrence(InvocationSequenceData invocationData, E template, MutableInt occurrencesLeft, ViewerFilter[] filters) { if (!getConcreteClass().isAssignableFrom(template.getClass())) { return null; } if (occurrenceFound(invocationData, template) && filtersPassed(invocationData, filters)) { occurrencesLeft.decrement(); if (occurrencesLeft.intValue() == 0) { return invocationData; } } if (null != invocationData.getNestedSequences()) { for (InvocationSequenceData child : invocationData.getNestedSequences()) { InvocationSequenceData foundData = getOccurrence(child, template, occurrencesLeft, filters); if (null != foundData) { return foundData; } } } return null; } /** * Returns if the template objects is found in the invocation data. * * @param invocationData * Invocation data to look in. * @param template * Template object. * @return Return depends on the implementing classes. */ public abstract boolean occurrenceFound(InvocationSequenceData invocationData, E template); /** * Compares if the template equals the element from the view of the finder. * * @param template * Template. * @param element * Element. * @return True if templates are equal. */ public abstract boolean doesTemplateEqualsElement(E template, E element); /** * Returns the concrete class that finder is working with. * * @return Returns the concrete class that finder is working with. */ public abstract Class<? extends E> getConcreteClass(); /** * Returns if the invocation data object is passing all given filters. * * @param invocationData * Invocation data. * @param filters * Array of filters. * @return True if all filters are passed, or filters array is null or empty. */ private boolean filtersPassed(InvocationSequenceData invocationData, ViewerFilter[] filters) { boolean passed = true; if (null != filters) { for (ViewerFilter filter : filters) { if (!filter.select(null, invocationData.getParentSequence(), invocationData)) { passed = false; break; } } } return passed; } } /** * Occurrence finder for {@link ExceptionSensorData}. * * @author Ivan Senic * */ private static class ExceptionOccurrenceFinder extends OccurrenceFinder<ExceptionSensorData> { /** * {@inheritDoc} */ @Override public boolean occurrenceFound(InvocationSequenceData invocationData, ExceptionSensorData template) { if (invocationData.getExceptionSensorDataObjects() != null) { for (ExceptionSensorData exData : invocationData.getExceptionSensorDataObjects()) { return doesTemplateEqualsElement(template, exData); } } return false; } /** * {@inheritDoc} */ @Override public Class<ExceptionSensorData> getConcreteClass() { return ExceptionSensorData.class; } /** * {@inheritDoc} */ @Override public boolean doesTemplateEqualsElement(ExceptionSensorData template, ExceptionSensorData element) { if (0 != template.getId()) { if (template.getId() != element.getId()) { return false; } } if (TEMPLATE_STRING != template.getCause()) { if (!ObjectUtils.equals(template.getCause(), element.getCause())) { return false; } } if (TEMPLATE_STRING != template.getErrorMessage()) { if (!ObjectUtils.equals(template.getErrorMessage(), element.getErrorMessage())) { return false; } } if (TEMPLATE_STRING != template.getStackTrace()) { // allow also parts of stack traces if (!StringUtils.contains(element.getStackTrace(), template.getStackTrace())) { return false; } } if (TEMPLATE_STRING != template.getThrowableType()) { if (!ObjectUtils.equals(template.getThrowableType(), element.getThrowableType())) { return false; } } if (0 != template.getThrowableIdentityHashCode()) { if (template.getThrowableIdentityHashCode() != element.getThrowableIdentityHashCode()) { return false; } } if (null != template.getExceptionEvent()) { if (!ObjectUtils.equals(template.getExceptionEvent(), element.getExceptionEvent())) { return false; } } return true; } /** * {@inheritDoc} */ @Override public ExceptionSensorData getEmptyTemplate() { ExceptionSensorData exceptionSensorData = new ExceptionSensorData(); exceptionSensorData.setCause(TEMPLATE_STRING); exceptionSensorData.setErrorMessage(TEMPLATE_STRING); exceptionSensorData.setStackTrace(TEMPLATE_STRING); exceptionSensorData.setThrowableType(TEMPLATE_STRING); exceptionSensorData.setThrowableIdentityHashCode(0); exceptionSensorData.setExceptionEvent(null); return exceptionSensorData; } } /** * Occurrence finder for {@link SqlStatementData}. * * @author Ivan Senic * */ private static class SqlOccurrenceFinder extends OccurrenceFinder<SqlStatementData> { /** * {@inheritDoc} */ @Override public boolean occurrenceFound(InvocationSequenceData invocationData, SqlStatementData template) { if (invocationData.getSqlStatementData() != null) { return doesTemplateEqualsElement(template, invocationData.getSqlStatementData()); } return false; } /** * {@inheritDoc} */ @Override public Class<SqlStatementData> getConcreteClass() { return SqlStatementData.class; } /** * {@inheritDoc} */ @Override public boolean doesTemplateEqualsElement(SqlStatementData template, SqlStatementData element) { if (0 != template.getId()) { if (template.getId() != element.getId()) { return false; } } if (TEMPLATE_STRING != template.getSql()) { if (!ObjectUtils.equals(template.getSql(), element.getSql())) { return false; } } if (TEMPLATE_LIST != template.getParameterValues()) { if (!ObjectUtils.equals(template.getParameterValues(), element.getParameterValues())) { return false; } } if (0 != template.getMethodIdent()) { if (template.getMethodIdent() != element.getMethodIdent()) { return false; } } return true; } /** * {@inheritDoc} */ @Override public SqlStatementData getEmptyTemplate() { SqlStatementData sqlStatementData = new SqlStatementData(); sqlStatementData.setSql(TEMPLATE_STRING); sqlStatementData.setParameterValues(TEMPLATE_LIST); sqlStatementData.setMethodIdent(0); return sqlStatementData; } } /** * Occurrence finder for {@link TimerData}. * * @author Ivan Senic * */ private static class TimerOccurrenceFinder extends OccurrenceFinder<MethodSensorData> { /** * {@inheritDoc} */ @Override public boolean occurrenceFound(InvocationSequenceData invocationData, MethodSensorData template) { if (invocationData.getTimerData() != null) { return doesTemplateEqualsElement(template, invocationData.getTimerData()); } else { return doesTemplateEqualsElement(template, invocationData); } } /** * {@inheritDoc} */ @Override public Class<TimerData> getConcreteClass() { return TimerData.class; } /** * {@inheritDoc} */ @Override public boolean doesTemplateEqualsElement(MethodSensorData template, MethodSensorData element) { if (0 != template.getId()) { if (template.getId() != element.getId()) { return false; } } if (0 != template.getMethodIdent()) { if (template.getMethodIdent() != element.getMethodIdent()) { return false; } } return true; } /** * {@inheritDoc} */ @Override public MethodSensorData getEmptyTemplate() { TimerData timerData = new TimerData(); timerData.setMethodIdent(0); return timerData; } } /** * Occurrence finder for {@link InvocationSequenceData}. * * @author Ivan Senic * */ private static class InvocationOccurenceFinder extends OccurrenceFinder<InvocationSequenceData> { /** * {@inheritDoc} */ @Override public InvocationSequenceData getEmptyTemplate() { InvocationSequenceData invocation = new InvocationSequenceData(); invocation.setMethodIdent(0); return invocation; } /** * {@inheritDoc} */ @Override public boolean occurrenceFound(InvocationSequenceData invocationData, InvocationSequenceData template) { return doesTemplateEqualsElement(template, invocationData); } /** * {@inheritDoc} */ @Override public boolean doesTemplateEqualsElement(InvocationSequenceData template, InvocationSequenceData element) { if (0 != template.getId()) { if (template.getId() != element.getId()) { return false; } } if (0 != template.getMethodIdent()) { if (template.getMethodIdent() != element.getMethodIdent()) { return false; } } return true; } /** * {@inheritDoc} */ @Override public Class<? extends InvocationSequenceData> getConcreteClass() { return InvocationSequenceData.class; } } /** * Occurrence finder for {@link SpanIdent} in the invocation. * * @author Ivan Senic * */ private static class SpanOccurenceFinder extends OccurrenceFinder<Span> { /** * {@inheritDoc} */ @Override public Span getEmptyTemplate() { return new ServerSpan(); } /** * {@inheritDoc} */ @Override public boolean occurrenceFound(InvocationSequenceData invocationData, Span template) { return InvocationSequenceDataHelper.hasSpanIdent(invocationData) && Objects.equals(invocationData.getSpanIdent(), template.getSpanIdent()); } /** * {@inheritDoc} */ @Override public boolean doesTemplateEqualsElement(Span template, Span element) { return Objects.equals(template.getSpanIdent(), element.getSpanIdent()); } /** * {@inheritDoc} */ @Override public Class<? extends Span> getConcreteClass() { return Span.class; } } }