package rocks.inspectit.ui.rcp.editor.table.input; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IBaseLabelProvider; import org.eclipse.jface.viewers.IContentProvider; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.StyledString; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import rocks.inspectit.shared.all.cmr.service.ICachedDataService; import rocks.inspectit.shared.all.communication.data.AggregatedExceptionSensorData; import rocks.inspectit.shared.all.communication.data.ExceptionSensorData; import rocks.inspectit.shared.cs.cmr.service.IExceptionDataAccessService; import rocks.inspectit.shared.cs.communication.comparator.AggregatedExceptionSensorDataComparatorEnum; import rocks.inspectit.shared.cs.communication.comparator.ExceptionSensorDataComparatorEnum; import rocks.inspectit.shared.cs.communication.comparator.IDataComparator; import rocks.inspectit.shared.cs.communication.comparator.InvocationAwareDataComparatorEnum; import rocks.inspectit.shared.cs.communication.comparator.ResultComparator; import rocks.inspectit.ui.rcp.InspectIT; import rocks.inspectit.ui.rcp.InspectITImages; import rocks.inspectit.ui.rcp.editor.inputdefinition.InputDefinition; import rocks.inspectit.ui.rcp.editor.inputdefinition.extra.InputDefinitionExtrasMarkerFactory; import rocks.inspectit.ui.rcp.editor.preferences.IPreferenceGroup; import rocks.inspectit.ui.rcp.editor.preferences.PreferenceEventCallback.PreferenceEvent; import rocks.inspectit.ui.rcp.editor.preferences.PreferenceId; import rocks.inspectit.ui.rcp.editor.root.IRootEditor; import rocks.inspectit.ui.rcp.editor.table.TableViewerComparator; import rocks.inspectit.ui.rcp.editor.viewers.StyledCellIndexLabelProvider; import rocks.inspectit.ui.rcp.formatter.TextFormatter; import rocks.inspectit.ui.rcp.repository.CmrRepositoryDefinition; /** * * @author Eduard Tudenhoefner * */ public class GroupedExceptionOverviewInputController extends AbstractTableInputController { /** * The ID of this subview / controller. */ public static final String ID = "inspectit.subview.table.groupedexceptionoverview"; /** * The private inner enumeration used to define the used IDs which are mapped into the columns. * The order in this enumeration represents the order of the columns. If it is reordered, * nothing else has to be changed. * * @author Eduard Tudenhoefner * */ private static enum Column { /** The class column. */ FQN("Fully-Qualified Name", 450, InspectITImages.IMG_CLASS, ExceptionSensorDataComparatorEnum.FQN), /** Invocation Affiliation. */ INVOCATION_AFFILLIATION("In Invocations", 120, InspectITImages.IMG_INVOCATION, InvocationAwareDataComparatorEnum.INVOCATION_AFFILIATION), /** The CREATED column. */ CREATED("Created", 70, null, AggregatedExceptionSensorDataComparatorEnum.CREATED), /** The RETHROWN column. */ RETHROWN("Rethrown", 70, null, AggregatedExceptionSensorDataComparatorEnum.RETHROWN), /** The HANDLED column. */ HANDLED("Handled", 70, null, AggregatedExceptionSensorDataComparatorEnum.HANDLED); /** The name. */ private String name; /** The width of the column. */ private int width; /** The image descriptor. Can be <code>null</code> */ private Image image; /** Comparator for the column. */ private IDataComparator<? super AggregatedExceptionSensorData> dataComparator; /** * Default constructor which creates a column enumeration object. * * @param name * The name of the column. * @param width * The width of the column. * @param imageName * The name of the image. Names are defined in {@link InspectITImages}. * @param dataComparator * Comparator for the column. */ private Column(String name, int width, String imageName, IDataComparator<? super AggregatedExceptionSensorData> dataComparator) { this.name = name; this.width = width; this.image = InspectIT.getDefault().getImage(imageName); this.dataComparator = dataComparator; } /** * Converts an ordinal into a column. * * @param i * The ordinal. * @return The appropriate column. */ public static Column fromOrd(int i) { if ((i < 0) || (i >= Column.values().length)) { throw new IndexOutOfBoundsException("Invalid ordinal"); } return Column.values()[i]; } } /** * The template object which is send to the server. */ private ExceptionSensorData template; /** * Indicates from which point in time data should be shown. */ private Date fromDate; /** * Indicates until which point in time data should be shown. */ private Date toDate; /** * The list of {@link ExceptionSensorData} objects which is displayed. */ private List<AggregatedExceptionSensorData> exceptionSensorDataList = new ArrayList<>(); /** * This map holds all objects that are needed to be represented in this view. It uses the fqn of * an exception as the key. It contains as value the objects that are belonging to a specific * exception class. */ private Map<String, List<AggregatedExceptionSensorData>> overviewMap; /** * The data access service to access the data on the CMR. */ private IExceptionDataAccessService dataAccessService; /** * {@inheritDoc} */ @Override public void setInputDefinition(InputDefinition inputDefinition) { super.setInputDefinition(inputDefinition); template = new ExceptionSensorData(); template.setPlatformIdent(inputDefinition.getIdDefinition().getPlatformId()); template.setSensorTypeIdent(inputDefinition.getIdDefinition().getSensorTypeId()); template.setMethodIdent(inputDefinition.getIdDefinition().getMethodId()); if (inputDefinition.hasInputDefinitionExtra(InputDefinitionExtrasMarkerFactory.EXCEPTION_TYPE_EXTRAS_MARKER)) { String throwableType = inputDefinition.getInputDefinitionExtra(InputDefinitionExtrasMarkerFactory.EXCEPTION_TYPE_EXTRAS_MARKER).getThrowableType(); template.setThrowableType(throwableType); } dataAccessService = inputDefinition.getRepositoryDefinition().getExceptionDataAccessService(); } /** * {@inheritDoc} */ @Override public void createColumns(TableViewer tableViewer) { for (Column column : Column.values()) { TableViewerColumn viewerColumn = new TableViewerColumn(tableViewer, SWT.NONE); viewerColumn.getColumn().setMoveable(true); viewerColumn.getColumn().setResizable(true); viewerColumn.getColumn().setText(column.name); viewerColumn.getColumn().setWidth(column.width); if (null != column.image) { viewerColumn.getColumn().setImage(column.image); } mapTableViewerColumn(column, viewerColumn); } } /** * {@inheritDoc} */ @Override public Object getTableInput() { // this list will be filled with data return exceptionSensorDataList; } /** * {@inheritDoc} */ @Override public IContentProvider getContentProvider() { return new GroupedExceptionOverviewContentProvider(); } /** * {@inheritDoc} */ @Override public IBaseLabelProvider getLabelProvider() { return new GroupedExceptionOverviewLabelProvider(); } /** * {@inheritDoc} */ @Override public ViewerComparator getComparator() { ICachedDataService cachedDataService = getInputDefinition().getRepositoryDefinition().getCachedDataService(); TableViewerComparator<AggregatedExceptionSensorData> exceptionOverviewViewerComparator = new TableViewerComparator<>(); for (Column column : Column.values()) { ResultComparator<AggregatedExceptionSensorData> resultComparator = new ResultComparator<>(column.dataComparator, cachedDataService); exceptionOverviewViewerComparator.addColumn(getMappedTableViewerColumn(column).getColumn(), resultComparator); } return exceptionOverviewViewerComparator; } /** * {@inheritDoc} */ @Override public Set<PreferenceId> getPreferenceIds() { Set<PreferenceId> preferences = EnumSet.noneOf(PreferenceId.class); if (getInputDefinition().getRepositoryDefinition() instanceof CmrRepositoryDefinition) { preferences.add(PreferenceId.CLEAR_BUFFER); preferences.add(PreferenceId.LIVEMODE); } preferences.add(PreferenceId.UPDATE); preferences.add(PreferenceId.TIMELINE); return preferences; } /** * {@inheritDoc} */ @Override public boolean canOpenInput(List<? extends Object> data) { if (data.isEmpty()) { return true; } return false; } /** * {@inheritDoc} */ @Override public void preferenceEventFired(PreferenceEvent preferenceEvent) { switch (preferenceEvent.getPreferenceId()) { case TIMELINE: Map<IPreferenceGroup, Object> preferenceMap = preferenceEvent.getPreferenceMap(); if (preferenceMap.containsKey(PreferenceId.TimeLine.FROM_DATE_ID)) { fromDate = (Date) preferenceMap.get(PreferenceId.TimeLine.FROM_DATE_ID); } if (preferenceMap.containsKey(PreferenceId.TimeLine.TO_DATE_ID)) { toDate = (Date) preferenceMap.get(PreferenceId.TimeLine.TO_DATE_ID); } break; default: break; } } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public void doRefresh(IProgressMonitor monitor, IRootEditor rootEditor) { monitor.beginTask("Updating Grouped Exception Overview", IProgressMonitor.UNKNOWN); monitor.subTask("Retrieving the Grouped Exception Overview"); List<AggregatedExceptionSensorData> ungroupedList = null; // if fromDate and toDate are set, then we retrieve only the data for // this time interval if ((null != fromDate) && (null != toDate)) { ungroupedList = dataAccessService.getDataForGroupedExceptionOverview(template, fromDate, toDate); } else { ungroupedList = dataAccessService.getDataForGroupedExceptionOverview(template); } List<AggregatedExceptionSensorData> groupedOverviewList = new ArrayList<>(); overviewMap = new HashMap<>(); for (AggregatedExceptionSensorData ungroupedObject : ungroupedList) { List<AggregatedExceptionSensorData> groupedObjects = Collections.EMPTY_LIST; if (!overviewMap.containsKey(ungroupedObject.getThrowableType())) { // map doesn't contain the actual exception class, so we create // and add a new list for exception classes of the same type groupedObjects = new ArrayList<>(); groupedObjects.add(ungroupedObject); overviewMap.put(ungroupedObject.getThrowableType(), groupedObjects); } else { // map contains the actual exception class, so we get the list // and search for the object within the list where the counter // values must be updated groupedObjects = overviewMap.get(ungroupedObject.getThrowableType()); groupedObjects.add(ungroupedObject); } } // we are creating the list that contains all object to be shown in the // overview for (Map.Entry<String, List<AggregatedExceptionSensorData>> entry : overviewMap.entrySet()) { String throwableType = entry.getKey(); AggregatedExceptionSensorData data = createObjectForOverview(throwableType, entry.getValue()); groupedOverviewList.add(data); } if (null != groupedOverviewList) { exceptionSensorDataList.clear(); monitor.subTask("Displaying the Exception Overview"); exceptionSensorDataList.addAll(groupedOverviewList); } monitor.done(); } /** * Creates the {@link AggregatedExceptionSensorData} object for the table input from the list of * same type aggregated objects. * * @param throwableType * Throwable type. * @param dataList * List of {@link AggregatedExceptionSensorData} * @return Aggregated data for the table. */ private AggregatedExceptionSensorData createObjectForOverview(String throwableType, List<AggregatedExceptionSensorData> dataList) { AggregatedExceptionSensorData data = new AggregatedExceptionSensorData(); data.setThrowableType(throwableType); for (AggregatedExceptionSensorData object : dataList) { if (data.getPlatformIdent() == 0) { data.setPlatformIdent(object.getPlatformIdent()); } data.aggregateExceptionData(object); } return data; } /** * {@inheritDoc} */ @Override public void doubleClick(DoubleClickEvent event) { final StructuredSelection selection = (StructuredSelection) event.getSelection(); if (!selection.isEmpty()) { try { PlatformUI.getWorkbench().getProgressService().busyCursorWhile(new IRunnableWithProgress() { @Override public void run(final IProgressMonitor monitor) { monitor.beginTask("Retrieving Exception Messages", IProgressMonitor.UNKNOWN); AggregatedExceptionSensorData data = (AggregatedExceptionSensorData) selection.getFirstElement(); final List<AggregatedExceptionSensorData> dataList = overviewMap.get(data.getThrowableType()); Display.getDefault().asyncExec(new Runnable() { @Override public void run() { IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); IWorkbenchPage page = window.getActivePage(); IRootEditor rootEditor = (IRootEditor) page.getActiveEditor(); rootEditor.setDataInput(dataList); } }); monitor.done(); } }); } catch (InvocationTargetException e) { MessageDialog.openError(Display.getDefault().getActiveShell().getShell(), "Error", e.getCause().toString()); } catch (InterruptedException e) { MessageDialog.openInformation(Display.getDefault().getActiveShell().getShell(), "Cancelled", e.getCause().toString()); } } } /** * The label provider for this view. * * @author Eduard Tudenhoefner * */ private final class GroupedExceptionOverviewLabelProvider extends StyledCellIndexLabelProvider { /** * {@inheritDoc} */ @Override protected StyledString getStyledText(Object element, int index) { AggregatedExceptionSensorData data = (AggregatedExceptionSensorData) element; Column enumId = Column.fromOrd(index); return getStyledTextForColumn(data, enumId); } } /** * The content provider for this view. * * @author Eduard Tudenhoefner * */ private static final class GroupedExceptionOverviewContentProvider implements IStructuredContentProvider { /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public Object[] getElements(Object inputElement) { List<AggregatedExceptionSensorData> exceptionSensorData = (List<AggregatedExceptionSensorData>) inputElement; return exceptionSensorData.toArray(); } /** * {@inheritDoc} */ @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } /** * {@inheritDoc} */ @Override public void dispose() { } } /** * Returns the styled text for a specific column. * * @param data * The data object to extract the information from. * @param enumId * The enumeration ID. * @return The styled string containing the information from the data object. */ private StyledString getStyledTextForColumn(AggregatedExceptionSensorData data, Column enumId) { switch (enumId) { case FQN: return new StyledString(data.getThrowableType()); case INVOCATION_AFFILLIATION: int percentage = (int) (data.getInvocationAffiliationPercentage() * 100); int invocations = 0; if (null != data.getInvocationParentsIdSet()) { invocations = data.getInvocationParentsIdSet().size(); } return TextFormatter.getInvocationAffilliationPercentageString(percentage, invocations); case CREATED: return new StyledString(String.valueOf(data.getCreated())); case RETHROWN: return new StyledString(String.valueOf(data.getPassed())); case HANDLED: return new StyledString(String.valueOf(data.getHandled())); default: return new StyledString("error"); } } /** * {@inheritDoc} */ @Override public String getReadableString(Object object) { if (object instanceof AggregatedExceptionSensorData) { AggregatedExceptionSensorData data = (AggregatedExceptionSensorData) object; StringBuilder sb = new StringBuilder(); for (Column column : Column.values()) { sb.append(getStyledTextForColumn(data, column).toString()); sb.append('\t'); } return sb.toString(); } throw new RuntimeException("Could not create the human readable string!"); } /** * {@inheritDoc} */ @Override public List<String> getColumnValues(Object object) { if (object instanceof AggregatedExceptionSensorData) { AggregatedExceptionSensorData data = (AggregatedExceptionSensorData) object; List<String> values = new ArrayList<>(); for (Column column : Column.values()) { values.add(getStyledTextForColumn(data, column).toString()); } return values; } throw new RuntimeException("Could not create the column values!"); } }