kg.apc.jmeter.graphs.AbstractGraphPanelVisualizer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jmeter-plugins-cmn-jmeter Show documentation
Show all versions of jmeter-plugins-cmn-jmeter Show documentation
Various utility classes to ease development of plugins
// fixme: perhaps we should replace model via setModel and use ONE model...
// TODO: https://groups.google.com/forum/#!topic/jmeter-plugins/qflK3oCjv4c
package kg.apc.jmeter.graphs;
import kg.apc.charting.*;
import kg.apc.jmeter.JMeterPluginsUtils;
import kg.apc.jmeter.vizualizers.CompositeResultCollector;
import kg.apc.jmeter.vizualizers.CorrectedResultCollector;
import kg.apc.jmeter.vizualizers.JSettingsPanel;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.tree.JMeterTreeModel;
import org.apache.jmeter.gui.tree.JMeterTreeNode;
import org.apache.jmeter.samplers.Clearable;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.property.BooleanProperty;
import org.apache.jmeter.testelement.property.LongProperty;
import org.apache.jmeter.testelement.property.StringProperty;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.ImageVisualizer;
import org.apache.jmeter.visualizers.Sample;
import org.apache.jmeter.visualizers.gui.AbstractVisualizer;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentSkipListMap;
public abstract class AbstractGraphPanelVisualizer
extends AbstractVisualizer
implements Clearable,
ImageVisualizer,
SettingsInterface {
private static final Logger log = LoggingManager.getLoggerForClass();
/**
*
*/
protected ConcurrentSkipListMap model;
protected ConcurrentSkipListMap modelAggregate;
/**
*
*/
protected long lastRepaint = 0;
/**
*
*/
private int interval = 500;
protected boolean isAggregate = false;
/**
*
*/
protected GraphPanel graphPanel;
/**
*
*/
protected ColorsDispatcher colors;
private static final long REPAINT_INTERVAL = 500;
public static final String INTERVAL_PROPERTY = "interval_grouping";
public static final String GRAPH_AGGREGATED = "graph_aggregated";
private JSettingsPanel settingsPanel = null;
//
private List includes = new ArrayList<>(0);
private List excludes = new ArrayList<>(0);
private String incRegex;
private String excRegex;
private boolean includeRegexChkboxState;
private boolean excludeRegexChkboxState;
protected long startTimeRef = 0;
protected long startTimeInf;
protected long startTimeSup;
private long startOffset;
private long endOffset;
private JPanel container;
private boolean filePanelVisible = true;
private boolean maximized = false;
private JButton maximizeButton;
private boolean ignoreCurrentTestStartTime;
private LabelToColorMapping labelToColorMapping = null;
private void reloadLabelToColorMapping() {
String labelToColorMappingString = JMeterUtils.getProperty("jmeterPlugin.labelToColorMapping");
labelToColorMapping = LabelToColorMapping.load(labelToColorMappingString);
}
/**
*
*/
public AbstractGraphPanelVisualizer() {
super();
model = new ConcurrentSkipListMap<>();
modelAggregate = new ConcurrentSkipListMap<>();
colors = ColorsDispatcherFactory.getColorsDispatcher();
//RowsProviderResultCollector resCollector = new RowsProviderResultCollector();
//setModel(resCollector);
initGui();
}
protected abstract JSettingsPanel createSettingsPanel();
@Override
public abstract String getStaticLabel();
private void initGui() {
setBorder(makeBorder());
setLayout(new BorderLayout());
add(JMeterPluginsUtils.addHelpLinkToPanel(makeTitlePanel(), getWikiPage()), BorderLayout.NORTH);
container = getGraphPanelContainer();
container.add(createGraphPanel(), BorderLayout.CENTER);
add(container, BorderLayout.CENTER);
reloadLabelToColorMapping();
addMouseClickColorChangeListener();
}
@Override
protected Component createTitleLabel() {
JPanel pan = new JPanel(new GridBagLayout());
JLabel titleLabel = new JLabel(getStaticLabel());
Font curFont = titleLabel.getFont();
titleLabel.setFont(curFont.deriveFont((float) curFont.getSize() + 4));
GridBagConstraints labelConstraints = new GridBagConstraints();
labelConstraints.weightx = 1.0;
labelConstraints.fill = GridBagConstraints.HORIZONTAL;
labelConstraints.gridx = 0;
labelConstraints.gridy = 0;
pan.add(titleLabel, labelConstraints);
GridBagConstraints buttonConstraints = new GridBagConstraints();
buttonConstraints.gridx = 1;
buttonConstraints.gridy = 0;
buttonConstraints.insets = new java.awt.Insets(0, 0, 0, 1);
maximizeButton = new JButton(new javax.swing.ImageIcon(getClass().getResource("/kg/apc/jmeter/img/maximize.png")));
maximizeButton.setFocusable(false);
maximizeButton.setPreferredSize(new java.awt.Dimension(20, 20));
maximizeButton.setToolTipText("Maximize Panel");
maximizeButton.addActionListener(new MaximizeAction());
pan.add(maximizeButton, buttonConstraints);
return pan;
}
protected void enableMaximize(boolean enable) {
maximizeButton.setVisible(enable);
}
protected void hideFilePanel() {
filePanelVisible = false;
getFilePanel().setVisible(false);
}
private void toogleMaximize() {
maximized = !maximized;
Component[] components = container.getComponents();
for (Component cmp : components) {
if (!(cmp instanceof GraphPanel)) {
cmp.setVisible(!maximized);
}
}
getFilePanel().setVisible(!maximized && filePanelVisible);
}
/**
* Provide a JPanel with BorderLayout, holder of the GraphPanelChart, which
* will be placed in the BorderLayout.CENTER. It can be overridden to create
* custom Visualizer layout. Note the JMeter file panel can be retrieved
* with getFilePanel() and moved in.
*
* @return a JPanel with a BorderLayout
*/
protected JPanel getGraphPanelContainer() {
return new JPanel(new BorderLayout());
}
protected GraphPanel createGraphPanel() {
graphPanel = new GraphPanel();
graphPanel.getGraphObject().setRows(model);
graphPanel.getGraphObject().setPrecisionLabel(interval);
setOptionsFromProperties(graphPanel.getGraphObject());
setExtraChartSettings();
// should be placed after creating graph panel
settingsPanel = createSettingsPanel();
graphPanel.getSettingsTab().add(getSettingsPanel(), BorderLayout.CENTER);
//graphPanel.setSettingsTabPanel(settingsPanel);
return graphPanel;
}
/*
* Extra chart settings must be placed here as they must be called before createSettingsPanel()
* Basically, all what is calling getChartSettings()
*/
protected void setExtraChartSettings() {
}
public void updateGui(Sample sample) {
long time = System.currentTimeMillis();
if ((time - lastRepaint) >= REPAINT_INTERVAL) {
updateGui();
repaint();
lastRepaint = time;
}
}
public void updateGui() {
graphPanel.updateGui();
}
@Override
public void clearData() {
startTimeRef = 0;
clearRowsFromCompositeModels(getModel().getName());
model.clear();
modelAggregate.clear();
colors.reset();
graphPanel.clearRowsTab();
updateGui();
repaint();
}
@Override
public Image getImage() {
return graphPanel.getGraphImage();
}
@Override
public int getGranulation() {
return interval;
}
@Override
public void setGranulation(int granulation) {
if (granulation < 1) {
throw new IllegalArgumentException("Interval cannot be less than 1");
}
interval = granulation;
getSettingsPanel().setGranulationValue(granulation);
graphPanel.getGraphObject().setPrecisionLabel(granulation);
}
/**
* Invoked when the target of the listener has changed its state. This
* implementation assumes that the target is the FilePanel, and will update
* the result collector for the new filename.
*
* @param e the event that has occurred
*/
@Override
public void stateChanged(ChangeEvent e) {
log.debug("getting new collector");
collector = (CorrectedResultCollector) createTestElement();
if (collector instanceof CorrectedResultCollector) {
setUpFiltering((CorrectedResultCollector) collector);
}
collector.loadExistingFile();
}
@Override
public TestElement createTestElement() {
if (collector == null || !(collector instanceof CorrectedResultCollector)) {
collector = new CorrectedResultCollector();
}
return super.createTestElement();
}
@Override
public void modifyTestElement(TestElement c) {
super.modifyTestElement(c);
c.setProperty(new LongProperty(INTERVAL_PROPERTY, interval));
c.setProperty(new BooleanProperty(GRAPH_AGGREGATED, isAggregate));
c.setProperty(new StringProperty(CorrectedResultCollector.INCLUDE_SAMPLE_LABELS, graphPanel.getRowSelectorPanel().getIncludeSampleLabels()));
c.setProperty(new StringProperty(CorrectedResultCollector.EXCLUDE_SAMPLE_LABELS, graphPanel.getRowSelectorPanel().getExcludeSampleLabels()));
c.setProperty(new StringProperty(CorrectedResultCollector.START_OFFSET,
graphPanel.getRowSelectorPanel().getStartOffset()));
c.setProperty(new StringProperty(CorrectedResultCollector.END_OFFSET,
graphPanel.getRowSelectorPanel().getEndOffset()));
c.setProperty(new BooleanProperty(
CorrectedResultCollector.INCLUDE_REGEX_CHECKBOX_STATE,
graphPanel.getRowSelectorPanel().isSelectedRegExpInc()));
c.setProperty(new BooleanProperty(
CorrectedResultCollector.EXCLUDE_REGEX_CHECKBOX_STATE,
graphPanel.getRowSelectorPanel().isSelectedRegExpExc()));
}
@Override
public void configure(TestElement el) {
super.configure(el);
int intervalProp = el.getPropertyAsInt(INTERVAL_PROPERTY);
boolean aggregatedProp = el.getPropertyAsBoolean(GRAPH_AGGREGATED, false);
if (intervalProp > 0) {
setGranulation(intervalProp);
}
graphPanel.getRowSelectorPanel().setIncludeSampleLabels(el.getPropertyAsString(CorrectedResultCollector.INCLUDE_SAMPLE_LABELS));
graphPanel.getRowSelectorPanel().setExcludeSampleLabels(el.getPropertyAsString(CorrectedResultCollector.EXCLUDE_SAMPLE_LABELS));
if (!CorrectedResultCollector.EMPTY_FIELD.equals(el
.getPropertyAsString(CorrectedResultCollector.START_OFFSET))) {
graphPanel
.getRowSelectorPanel()
.setStartOffset(
(el.getPropertyAsLong(CorrectedResultCollector.START_OFFSET)));
}
if (!CorrectedResultCollector.EMPTY_FIELD.equals(el
.getPropertyAsString(CorrectedResultCollector.END_OFFSET))) {
graphPanel
.getRowSelectorPanel()
.setEndOffset(
(el.getPropertyAsLong(CorrectedResultCollector.END_OFFSET)));
}
graphPanel
.getRowSelectorPanel()
.setSelectedRegExpInc(
el.getPropertyAsBoolean(CorrectedResultCollector.INCLUDE_REGEX_CHECKBOX_STATE));
graphPanel
.getRowSelectorPanel()
.setSelectedRegExpExc(
el.getPropertyAsBoolean(CorrectedResultCollector.EXCLUDE_REGEX_CHECKBOX_STATE));
if (el instanceof CorrectedResultCollector) {
setUpFiltering((CorrectedResultCollector) el);
}
switchModel(aggregatedProp);
}
@Override
public GraphPanelChart getGraphPanelChart() {
return graphPanel.getGraphObject();
}
@Override
public void switchModel(boolean aggregate) {
ConcurrentSkipListMap selectedModel;
if (aggregate) {
// issue 64: we must fail requests for aggregate in unsupported cases
if (modelAggregate.isEmpty() && !model.isEmpty()) {
throw new UnsupportedOperationException("Seems you've requested "
+ "aggregate mode for graph that don't support it. We apologize...");
}
selectedModel = modelAggregate;
} else {
selectedModel = model;
}
graphPanel.getGraphObject().setRows(selectedModel);
graphPanel.clearRowsTab();
for (AbstractGraphRow abstractGraphRow : selectedModel.values()) {
graphPanel.addRow(abstractGraphRow);
}
isAggregate = aggregate;
getSettingsPanel().setAggregateMode(aggregate);
}
private void addRowToCompositeModels(String rowName, AbstractGraphRow row) {
GuiPackage gui = GuiPackage.getInstance();
if (gui == null) {
log.debug("No GUI Package present, ignored adding to composite");
return;
}
JMeterTreeModel testTree = gui.getTreeModel();
for (JMeterTreeNode obj : testTree.getNodesOfType(CompositeResultCollector.class)) {
CompositeResultCollector compositeResultCollector = (CompositeResultCollector) obj.getTestElement();
compositeResultCollector.getCompositeModel().addRow(rowName, row);
}
}
private void clearRowsFromCompositeModels(String vizualizerName) {
GuiPackage gui = GuiPackage.getInstance();
if (gui == null) {
log.debug("No GUI Package present, ignored removing from composite");
return;
}
JMeterTreeModel testTree = gui.getTreeModel();
for (JMeterTreeNode obj : testTree.getNodesOfType(CompositeResultCollector.class)) {
//System.out.println("obj");
CompositeResultCollector compositeResultCollector = (CompositeResultCollector) obj.getTestElement();
compositeResultCollector.getCompositeModel().clearRows(vizualizerName);
}
}
protected synchronized AbstractGraphRow getNewRow(
ConcurrentSkipListMap model,
int rowType,
String label,
int markerSize,
boolean isBarRow,
boolean displayLabel,
boolean thickLines,
boolean showInLegend,
Color color,
boolean canCompose) {
AbstractGraphRow row;
if (log.isDebugEnabled()) {
log.debug("This AbstractGraphPanelVisualizer is an instance of [" + this.getClass().getName() + "]");
}
if (!model.containsKey(label)) {
row = AbstractGraphRow.instantiateNewRow(rowType);
row.setLabel(label);
row.setMarkerSize(markerSize);
row.setDrawBar(isBarRow);
row.setDrawLine(!isBarRow);
row.setDrawValueLabel(displayLabel);
row.setDrawThickLines(thickLines);
row.setShowInLegend(showInLegend);
Color overrideColor = null;
if (this.labelToColorMapping != null)
overrideColor = labelToColorMapping.getColorForLabel(row.getLabel());
if (log.isDebugEnabled())
if (row != null) {
log.debug("%#@ Found override color [" + (overrideColor == null ? "null" : overrideColor.toString()) + "]");
log.debug("%#@ for label [" + row.getLabel() + "] color in-parm [" + (color == null ? "null" : color.toString()) + "]");
log.debug("%#@ prev row.getColor() [" + (row.getColor() == null ? "null" : row.getColor().toString()) + "]");
} else
log.debug("%#@ Found null row displayLabel[" + displayLabel + "] and label [" + label + "]");
row.setColor(overrideColor != null ? overrideColor : colors.getNextColor());
if (log.isDebugEnabled())
log.debug("%#@ new row.getColor() [" + (row.getColor() == null ? "null" : row.getColor().toString()) + "]");
model.put(label, row);
graphPanel.addRow(row);
if (canCompose) {
addRowToCompositeModels(getModel().getName(), row);
}
} else {
row = model.get(label);
}
return row;
}
protected synchronized AbstractGraphRow getNewRow(
ConcurrentSkipListMap model,
int rowType,
String label,
int markerSize,
boolean isBarRow,
boolean displayLabel,
boolean thickLines,
boolean showInLegend,
boolean canCompose) {
return getNewRow(model, rowType, label, markerSize, isBarRow, displayLabel, thickLines, showInLegend, null, canCompose);
}
protected boolean isFromTransactionControler(SampleResult res) {
return res.getResponseMessage() != null && res.getResponseMessage().startsWith("Number of samples in transaction");
}
/**
* @return the settingsPanel
*/
public JSettingsPanel getSettingsPanel() {
return settingsPanel;
}
private void setOptionsFromProperties(GraphPanelChart graph) {
// moved this into jmeter part, because charting package knows nothing on JMEters
//properties from user.properties
String cfgDrawGradient = JMeterUtils.getProperty("jmeterPlugin.drawGradient");
if (cfgDrawGradient != null) {
graph.getChartSettings().setDrawGradient("true".equalsIgnoreCase(cfgDrawGradient.trim()));
}
String cfgNeverDrawFinalZeroingLines = JMeterUtils.getProperty("jmeterPlugin.neverDrawFinalZeroingLines");
if (cfgNeverDrawFinalZeroingLines != null) {
graph.getChartSettings().setConfigNeverDrawFinalZeroingLines("true".equalsIgnoreCase(cfgNeverDrawFinalZeroingLines.trim()));
}
String cfgOptimizeYAxis = JMeterUtils.getProperty("jmeterPlugin.optimizeYAxis");
if (cfgOptimizeYAxis != null) {
graph.getChartSettings().setConfigOptimizeYAxis("true".equalsIgnoreCase(cfgOptimizeYAxis.trim()));
}
String cfgNeverDrawFinalCurrentX = JMeterUtils.getProperty("jmeterPlugin.neverDrawCurrentX");
if (cfgNeverDrawFinalCurrentX != null) {
graph.getChartSettings().setConfigNeverDrawCurrentX("true".equalsIgnoreCase(cfgNeverDrawFinalCurrentX.trim()));
}
String cfgCsvSeparator = JMeterUtils.getProperty("jmeterPlugin.csvSeparator");
if (cfgCsvSeparator != null) {
graph.getChartSettings().setConfigCsvSeparator(cfgCsvSeparator);
}
String cfgUseRelativeTime = JMeterUtils.getProperty("jmeterPlugin.useRelativeTime");
if (cfgUseRelativeTime != null) {
graph.getChartSettings().setUseRelativeTime("true".equalsIgnoreCase(cfgUseRelativeTime.trim()));
}
String cfgGraphLineWitdh = JMeterUtils.getProperty("jmeterPlugin.graphLineWidth");
if (cfgGraphLineWitdh != null) {
graph.getChartSettings().setLineWidth(JMeterPluginsUtils.getFloatFromString(cfgGraphLineWitdh, 1.0f));
}
String cfgGraphLineMarker = JMeterUtils.getProperty("jmeterPlugin.drawLineMarker");
if (cfgGraphLineMarker != null) {
boolean removeMarkers = "false".equalsIgnoreCase(cfgGraphLineMarker.trim());
if (removeMarkers) {
graph.getChartSettings().setChartMarkers(ChartSettings.CHART_MARKERS_NO);
}
}
}
protected boolean isSampleIncluded(SampleResult res) {
if (null == res) {
return true;
}
if (startTimeRef == 0) {
startTimeRef = res.getStartTime();
startTimeInf = startTimeRef - startTimeRef % 1000;
startTimeSup = startTimeRef + (1000 - startTimeRef % 1000) % 1000;
}
if (includeRegexChkboxState && !incRegex.isEmpty()
&& !res.getSampleLabel().matches(incRegex)) {
return false;
}
if (excludeRegexChkboxState && !excRegex.isEmpty()
&& res.getSampleLabel().matches(excRegex)) {
return false;
}
if (!includeRegexChkboxState && !includes.isEmpty()
&& !includes.contains(res.getSampleLabel())) {
return false;
}
if (!excludeRegexChkboxState && !excludes.isEmpty()
&& excludes.contains(res.getSampleLabel())) {
return false;
}
if (startOffset > res.getStartTime() - startTimeInf) {
return false;
}
if (endOffset < res.getStartTime() - startTimeSup) {
return false;
}
return true;
}
protected boolean isSampleIncluded(String sampleLabel) {
if (includeRegexChkboxState && !incRegex.isEmpty()
&& !sampleLabel.matches(incRegex)) {
return false;
}
if (excludeRegexChkboxState && !excRegex.isEmpty()
&& sampleLabel.matches(excRegex)) {
return false;
}
if (!includeRegexChkboxState && !includes.isEmpty()
&& !includes.contains(sampleLabel)) {
return false;
}
if (!excludeRegexChkboxState && !excludes.isEmpty()
&& excludes.contains(sampleLabel)) {
return false;
}
return true;
}
public void setUpFiltering(CorrectedResultCollector rc) {
startOffset = rc.getTimeDelimiter(
CorrectedResultCollector.START_OFFSET, Long.MIN_VALUE);
endOffset = rc.getTimeDelimiter(CorrectedResultCollector.END_OFFSET,
Long.MAX_VALUE);
includeRegexChkboxState = rc
.getRegexChkboxState(CorrectedResultCollector.INCLUDE_REGEX_CHECKBOX_STATE);
excludeRegexChkboxState = rc
.getRegexChkboxState(CorrectedResultCollector.EXCLUDE_REGEX_CHECKBOX_STATE);
if (includeRegexChkboxState)
incRegex = rc
.getRegex(CorrectedResultCollector.INCLUDE_SAMPLE_LABELS);
else
includes = rc
.getList(CorrectedResultCollector.INCLUDE_SAMPLE_LABELS);
if (excludeRegexChkboxState)
excRegex = rc
.getRegex(CorrectedResultCollector.EXCLUDE_SAMPLE_LABELS);
else
excludes = rc
.getList(CorrectedResultCollector.EXCLUDE_SAMPLE_LABELS);
}
private class MaximizeAction implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
toogleMaximize();
if (!maximized) {
maximizeButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/kg/apc/jmeter/img/maximize.png")));
maximizeButton.setToolTipText("Maximize Panel");
} else {
maximizeButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/kg/apc/jmeter/img/restore.png")));
maximizeButton.setToolTipText("Restore Panel");
}
}
}
/**
* Ignore current test start time(ie take it from reload test data)
* this is needed for visualizer that don't use current tests data
* but reload from file
*/
public void setIgnoreCurrentTestStartTime() {
this.ignoreCurrentTestStartTime = true;
}
/**
* Ignore current test start time(ie take it from reload test data)
* this is needed for visualizer that don't use current tests data
* but reload from file
*
* @return the ignoreTestStartTime
*/
public boolean isIgnoreCurrentTestStartTime() {
return ignoreCurrentTestStartTime;
}
@Override
public void clearGui() {
super.clearGui();
graphPanel.getRowSelectorPanel().clearGui();
}
private void addMouseClickColorChangeListener() {
this.graphPanel.getGraphObject().addMouseListener(new MouseClickColorChangeListener());
}
class MouseClickColorChangeListener extends MouseAdapter {
@Override
public void mouseClicked(MouseEvent e) {
log.debug("mouse click [" + e.getX() + "," + e.getY() + "] ClickCount[" + e.getClickCount() + "]");
if (e.getClickCount() == 2) {
for (Entry row : model.entrySet()) {
AbstractGraphRow agr = row.getValue();
if (agr != null) {
Rectangle r = agr.getLegendColorBox();
if (r != null) {
Rectangle slightlyLargerTarget = r.getBounds();
slightlyLargerTarget.height += 4;
slightlyLargerTarget.width += 2;
boolean hit = slightlyLargerTarget.contains(e.getPoint());
log.debug("hit [" + hit + "] rectangle [" + slightlyLargerTarget.toString() + "] point [" + e.getPoint() + "]");
if (hit) {
row.getValue().setColor(colors.getNextColor());
}
}
}
}
updateGui(null);
}
//repaint();
}
}
}