/* * Copyright 2008-2017 by Emeric Vernat * * This file is part of Java Melody. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.bull.javamelody; import java.awt.Color; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.text.DecimalFormat; import javax.swing.BorderFactory; import javax.swing.JTable; import javax.swing.border.MatteBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import net.bull.javamelody.swing.MButton; import net.bull.javamelody.swing.table.MDefaultTableCellRenderer; /** * Tableau de requêtes avec les colonnes des requêtes en cours. * @author Emeric Vernat */ class CounterRequestForContextTable extends CounterRequestTable { private static final long serialVersionUID = 1L; private CounterRequestContextData data; private abstract class CounterRequestTableCellRenderer extends MDefaultTableCellRenderer { private static final long serialVersionUID = 1L; private final MatteBorder contextBorder = BorderFactory.createMatteBorder(1, 0, 0, 0, Color.GRAY); CounterRequestTableCellRenderer() { super(); } protected CounterRequest getCounterRequest(int row) { final int modelRow = convertRowIndexToModel(row); return getData().getAllRequests().get(modelRow); } protected CounterRequestContext getCounterRequestContext(int row) { final int modelRow = convertRowIndexToModel(row); return getData().getAllContexts().get(modelRow); } int getParentContextLevel(int row) { CounterRequestContext counterRequestContext = getCounterRequestContext(row); int level = 0; while (counterRequestContext.getParentContext() != null) { level++; counterRequestContext = counterRequestContext.getParentContext(); } return level; } @Override public Component getTableCellRendererComponent(JTable jtable, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final Component result = super.getTableCellRendererComponent(jtable, value, isSelected, hasFocus, row, column); if (row != 0 && getParentContextLevel(row) == 0) { setBorder(BorderFactory.createCompoundBorder(contextBorder, getBorder())); } return result; } } private final class ThreadTableCellRenderer extends CounterRequestTableCellRenderer { private static final long serialVersionUID = 1L; ThreadTableCellRenderer() { super(); } @Override public Component getTableCellRendererComponent(JTable jtable, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final CounterRequestContext counterRequestContext = getCounterRequestContext(row); final ThreadInformations threadInformations = getData() .getThreadInformationsByCounterRequestContext(counterRequestContext); final String threadName; if (threadInformations == null) { threadName = null; // un décalage n'a pas permis de récupérer le thread de ce context setToolTipText(null); } else { threadName = threadInformations.getName(); setToolTipText(ThreadInformationsPanel.convertStackTraceToHtml( threadInformations.getName(), threadInformations.getStackTrace())); } return super.getTableCellRendererComponent(jtable, threadName, isSelected, hasFocus, row, column); } } private final class ExecutedMethodTableCellRenderer extends CounterRequestTableCellRenderer { private static final long serialVersionUID = 1L; ExecutedMethodTableCellRenderer() { super(); } @Override public Component getTableCellRendererComponent(JTable jtable, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final CounterRequestContext counterRequestContext = getCounterRequestContext(row); final ThreadInformations threadInformations = getData() .getThreadInformationsByCounterRequestContext(counterRequestContext); final String executedMethod; if (threadInformations == null) { executedMethod = null; // un décalage n'a pas permis de récupérer le thread de ce context } else { executedMethod = threadInformations.getExecutedMethod(); } return super.getTableCellRendererComponent(jtable, executedMethod, isSelected, hasFocus, row, column); } } private final class RemoteUserTableCellRenderer extends CounterRequestTableCellRenderer { private static final long serialVersionUID = 1L; RemoteUserTableCellRenderer() { super(); } @Override public Component getTableCellRendererComponent(JTable jtable, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final CounterRequestContext counterRequestContext = getCounterRequestContext(row); final String remoteUser = counterRequestContext.getRemoteUser(); return super.getTableCellRendererComponent(jtable, remoteUser, isSelected, hasFocus, row, column); } } private final class NameTableCellRenderer extends CounterRequestTableCellRenderer { private static final long serialVersionUID = 1L; NameTableCellRenderer() { super(); } @Override public Component getTableCellRendererComponent(JTable jtable, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final CounterRequestContext context = getCounterRequestContext(row); final int margin = 10 * getParentContextLevel(row); final Counter counter = context.getParentCounter(); setIcon(CounterRequestAbstractPanel.getCounterIcon(counter, margin)); return super.getTableCellRendererComponent(jtable, value, isSelected, hasFocus, row, column); } } private class IntegerTableCellRenderer extends CounterRequestTableCellRenderer { private static final long serialVersionUID = 1L; private final Integer minusOne = Integer.valueOf(-1); private final DecimalFormat integerFormat = I18N.createIntegerFormat(); IntegerTableCellRenderer() { super(); setHorizontalAlignment(RIGHT); } @Override public void setValue(Object value) { final String text; if (value != null && !minusOne.equals(value)) { text = integerFormat.format(value); } else { text = null; } super.setValue(text); } } private final class DurationTableCellRenderer extends IntegerTableCellRenderer { private static final long serialVersionUID = 1L; DurationTableCellRenderer() { super(); } @Override public Component getTableCellRendererComponent(JTable jtable, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final CounterRequestContext context = getCounterRequestContext(row); final int margin = 10 * getParentContextLevel(row); final Counter counter = context.getParentCounter(); setIcon(CounterRequestAbstractPanel.getCounterIcon(counter, margin)); final CounterRequestContext counterRequestContext = getCounterRequestContext(row); final int duration = counterRequestContext .getDuration(counterRequestContext.getParentCounter().getStartDate().getTime()); final CounterRequestAggregation aggregation = getData() .getAggregationForCounter(counter); final Component result = super.getTableCellRendererComponent(jtable, duration, isSelected, hasFocus, row, column); StatisticsTablePanel.setStyleBasedOnThresholds(this, duration, aggregation); return result; } } private final class CpuTableCellRenderer extends IntegerTableCellRenderer { private static final long serialVersionUID = 1L; CpuTableCellRenderer() { super(); } @Override public Component getTableCellRendererComponent(JTable jtable, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final Integer cpu; final CounterRequest counterRequest = getCounterRequest(row); if (counterRequest != null && counterRequest.getCpuTimeMean() >= 0) { final CounterRequestContext counterRequestContext = getCounterRequestContext(row); cpu = counterRequestContext.getCpuTime(); } else { cpu = null; } return super.getTableCellRendererComponent(jtable, cpu, isSelected, hasFocus, row, column); } } private final class TotalChildHitsTableCellRenderer extends IntegerTableCellRenderer { private static final long serialVersionUID = 1L; TotalChildHitsTableCellRenderer() { super(); } @Override public Component getTableCellRendererComponent(JTable jtable, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final Integer totalChildHits; final CounterRequestContext context = getCounterRequestContext(row); if (context.getParentCounter().getChildCounterName() == null) { // si le compteur parent du contexte n'a pas de compteur fils // (comme le compteur sql et au contraire du compteur http), // alors la valeur de childHits ou childDurations n'a pas de sens // (comme les hits sql fils pour une requête sql) totalChildHits = -1; } else { totalChildHits = context.getTotalChildHits(); } return super.getTableCellRendererComponent(jtable, totalChildHits, isSelected, hasFocus, row, column); } } private final class ChildHitsMeanTableCellRenderer extends IntegerTableCellRenderer { private static final long serialVersionUID = 1L; ChildHitsMeanTableCellRenderer() { super(); } @Override public Component getTableCellRendererComponent(JTable jtable, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final Integer childHitsMean; final CounterRequestContext context = getCounterRequestContext(row); if (context.getParentCounter().getChildCounterName() == null) { // si le compteur parent du contexte n'a pas de compteur fils // (comme le compteur sql et au contraire du compteur http), // alors la valeur de childHits ou childDurations n'a pas de sens // (comme les hits sql fils pour une requête sql) childHitsMean = -1; } else { final CounterRequest counterRequest = getCounterRequest(row); if (counterRequest == null) { childHitsMean = null; } else { childHitsMean = counterRequest.getChildHitsMean(); } } return super.getTableCellRendererComponent(jtable, childHitsMean, isSelected, hasFocus, row, column); } } private final class TotalChildDurationsSumTableCellRenderer extends IntegerTableCellRenderer { private static final long serialVersionUID = 1L; TotalChildDurationsSumTableCellRenderer() { super(); } @Override public Component getTableCellRendererComponent(JTable jtable, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final Integer totalChildDurationsSum; final CounterRequestContext context = getCounterRequestContext(row); if (context.getParentCounter().getChildCounterName() == null) { // si le compteur parent du contexte n'a pas de compteur fils // (comme le compteur sql et au contraire du compteur http), // alors la valeur de childHits ou childDurations n'a pas de sens // (comme les hits sql fils pour une requête sql) totalChildDurationsSum = -1; } else { totalChildDurationsSum = context.getTotalChildDurationsSum(); } return super.getTableCellRendererComponent(jtable, totalChildDurationsSum, isSelected, hasFocus, row, column); } } private final class ChildsDurationsMeanTableCellRenderer extends IntegerTableCellRenderer { private static final long serialVersionUID = 1L; ChildsDurationsMeanTableCellRenderer() { super(); } @Override public Component getTableCellRendererComponent(JTable jtable, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final Integer childDurationsMean; final CounterRequestContext context = getCounterRequestContext(row); if (context.getParentCounter().getChildCounterName() == null) { // si le compteur parent du contexte n'a pas de compteur fils // (comme le compteur sql et au contraire du compteur http), // alors la valeur de childHits ou childDurations n'a pas de sens // (comme les hits sql fils pour une requête sql) childDurationsMean = -1; } else { final CounterRequest counterRequest = getCounterRequest(row); if (counterRequest == null) { childDurationsMean = null; } else { childDurationsMean = counterRequest.getChildDurationsMean(); } } return super.getTableCellRendererComponent(jtable, childDurationsMean, isSelected, hasFocus, row, column); } } CounterRequestForContextTable(RemoteCollector remoteCollector) { super(remoteCollector); } void init(CounterRequestContextData newData) { this.data = newData; addColumns(); setList(data.getAllRequests()); } CounterRequestContextData getData() { return data; } private void addColumns() { final boolean remoteUserDisplayed = getData().isRemoteUserDisplayed(); final boolean childHitsDisplayed = getData().isChildHitsDisplayed(); final boolean stackTraceEnabled = getData().isStackTraceEnabled(); addCustomColumn(getString("Thread"), new ThreadTableCellRenderer()); if (remoteUserDisplayed) { addCustomColumn(getString("Utilisateur"), new RemoteUserTableCellRenderer()); } addColumn("name", getString("Requete")); setColumnCellRenderer("name", new NameTableCellRenderer()); addCustomColumn(getString("Duree_ecoulee"), new DurationTableCellRenderer()); addColumn("mean", getString("Temps_moyen")); setColumnCellRenderer("mean", new IntegerTableCellRenderer()); addCustomColumn(getString("Temps_cpu"), new CpuTableCellRenderer()); addColumn("cpuTimeMean", getString("Temps_cpu_moyen")); setColumnCellRenderer("cpuTimeMean", new IntegerTableCellRenderer()); // rq : tous ces contextes viennent du même compteur donc peu importe lequel des parentCounter if (childHitsDisplayed) { final String childCounterName = getData().getRootContexts().get(0).getParentCounter() .getChildCounterName(); addCustomColumn(getFormattedString("hits_fils", childCounterName), new TotalChildHitsTableCellRenderer()); addCustomColumn(getFormattedString("hits_fils_moyens", childCounterName), new ChildHitsMeanTableCellRenderer()); addCustomColumn(getFormattedString("temps_fils", childCounterName), new TotalChildDurationsSumTableCellRenderer()); addCustomColumn(getFormattedString("temps_fils_moyen", childCounterName), new ChildsDurationsMeanTableCellRenderer()); } if (stackTraceEnabled) { addCustomColumn(getString("Methode_executee"), new ExecutedMethodTableCellRenderer()); } } private void addCustomColumn(String headerValue, TableCellRenderer tableCellRenderer) { final TableColumn tableColumn = new TableColumn(getColumnCount()); tableColumn.setIdentifier(getColumnCount()); tableColumn.setHeaderValue(headerValue); tableColumn.setCellRenderer(tableCellRenderer); addColumn(tableColumn); } MButton createKillThreadButton(final CounterRequestContextPanel contextPanel) { final MButton killThreadButton = new MButton(getString("Tuer"), ImageIconCache.getImageIcon("stop.png")); getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { final ThreadInformations threadInformations = getSelectedThreadInformations(); killThreadButton.setEnabled(threadInformations != null); if (threadInformations != null) { killThreadButton.setToolTipText( getFormattedString("kill_thread", threadInformations.getName())); } else { killThreadButton.setToolTipText(null); } } }); killThreadButton.setEnabled(getSelectedThreadInformations() != null); killThreadButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { final ThreadInformations threadInformations = getSelectedThreadInformations(); contextPanel.actionKillThread(threadInformations); } }); return killThreadButton; } ThreadInformations getSelectedThreadInformations() { final int selectedRow = getSelectedRow(); if (selectedRow >= 0) { final int modelRow = convertRowIndexToModel(selectedRow); final CounterRequestContext counterRequestContext = getData().getAllContexts() .get(modelRow); return getData().getThreadInformationsByCounterRequestContext(counterRequestContext); } return null; } /** * Retourne une traduction dans la locale courante. * @param key clé d'un libellé dans les fichiers de traduction * @return String */ static String getString(String key) { return I18N.getString(key); } /** * Retourne une traduction dans la locale courante et insère les arguments aux positions {i}. * @param key clé d'un libellé dans les fichiers de traduction * @param arguments Valeur à inclure dans le résultat * @return String */ static String getFormattedString(String key, Object... arguments) { return I18N.getFormattedString(key, arguments); } }