Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you 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 org.apache.jmeter.visualizers;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Image;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.text.NumberFormat;
import java.util.Deque;
import java.util.concurrent.ConcurrentLinkedDeque;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.Timer;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import org.apache.jmeter.gui.TestElementMetadata;
import org.apache.jmeter.gui.util.JMeterColor;
import org.apache.jmeter.samplers.Clearable;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.gui.AbstractVisualizer;
/**
* This class implements a statistical analyser that calculates both the average
* and the standard deviation of the sampling process and outputs them as
* autoscaling plots.
*
*/
@TestElementMetadata(labelResource = "graph_results_title")
public class GraphVisualizer extends AbstractVisualizer implements ImageVisualizer, ItemListener, Clearable {
private static final long serialVersionUID = 240L;
private static final String ZERO = "0"; //$NON-NLS-1$
private final NumberFormat nf = NumberFormat.getInstance(); // OK, because used in synchronised method
private final CachingStatCalculator model;
private JTextField maxYField = null;
private JTextField minYField = null;
private JTextField noSamplesField = null;
private final String minute = JMeterUtils.getResString("minute"); // $NON-NLS-1$
private final int REFRESH_PERIOD = JMeterUtils.getPropDefault("jmeter.gui.refresh_period", 500); // $NON-NLS-1$
private final Graph graph;
private JCheckBox data;
private JCheckBox average;
private JCheckBox deviation;
private JCheckBox throughput;
private JCheckBox median;
private JTextField dataField;
private JTextField averageField;
private JTextField deviationField;
private JTextField throughputField;
private JTextField medianField;
private final Deque newSamples = new ConcurrentLinkedDeque<>();
/**
* Constructor for the GraphVisualizer object.
*/
public GraphVisualizer() {
model = new CachingStatCalculator("Graph");
graph = new Graph(model);
init();
}
/**
* Gets the Image attribute of the GraphVisualizer object.
*
* @return the Image value
*/
@Override
public Image getImage() {
Image result = graph.createImage(graph.getWidth(), graph.getHeight());
graph.paintComponent(result.getGraphics());
return result;
}
/**
* @param s Sample
* @deprecated use {@link GraphVisualizer#add(SampleResult)} instead
*/
@Deprecated
public void updateGui(Sample s) {
JMeterUtils.runSafe(false, () -> updateGuiInAWTThread(s));
}
// called inside AWT Thread
private void collectSamplesFromQueue() {
// We have received one more sample
Sample s = null;
synchronized (graph) {
while (!newSamples.isEmpty()) {
s = model.addSample(newSamples.pop());
}
}
updateGuiInAWTThread(s);
}
// called inside AWT Thread
private void updateGuiInAWTThread(Sample s) {
if (s == null) {
return;
}
graph.updateGui(s);
noSamplesField.setText(Long.toString(s.getCount()));
dataField.setText(Long.toString(s.getData()));
averageField.setText(Long.toString(s.getAverage()));
deviationField.setText(Long.toString(s.getDeviation()));
throughputField.setText(nf.format(60 * s.getThroughput()) + "/" + minute); // $NON-NLS-1$
medianField.setText(Long.toString(s.getMedian()));
updateYAxis();
}
@Override
public void add(final SampleResult res) {
newSamples.add(res);
}
@Override
public String getLabelResource() {
return "graph_results_title"; // $NON-NLS-1$
}
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getItem() == data) {
this.graph.enableData(e.getStateChange() == ItemEvent.SELECTED);
} else if (e.getItem() == average) {
this.graph.enableAverage(e.getStateChange() == ItemEvent.SELECTED);
} else if (e.getItem() == deviation) {
this.graph.enableDeviation(e.getStateChange() == ItemEvent.SELECTED);
} else if (e.getItem() == throughput) {
this.graph.enableThroughput(e.getStateChange() == ItemEvent.SELECTED);
} else if (e.getItem() == median) {
this.graph.enableMedian(e.getStateChange() == ItemEvent.SELECTED);
}
this.graph.repaint();
}
@Override
public void clearData() {
synchronized (graph) {
graph.clearData();
model.clear();
newSamples.clear();
}
dataField.setText(ZERO);
averageField.setText(ZERO);
deviationField.setText(ZERO);
throughputField.setText("0/" + minute); //$NON-NLS-1$
medianField.setText(ZERO);
noSamplesField.setText(ZERO);
updateYAxis();
repaint();
}
@Override
public String toString() {
return "Show the samples analysis as dot plots";
}
/**
* Update the max and min value of the Y axis.
*/
private void updateYAxis() {
maxYField.setText(Long.toString(graph.getGraphMax()));
minYField.setText(ZERO);
}
/**
* Initialize the GUI.
*/
private void init() { // WARNING: called from ctor so must not be overridden (i.e. must be private or final)
this.setLayout(new BorderLayout());
// MAIN PANEL
Border margin = new EmptyBorder(10, 10, 5, 10);
this.setBorder(margin);
// Set up the graph with header, footer, Y axis and graph display
JPanel graphPanel = new JPanel(new BorderLayout());
graphPanel.add(createYAxis(), BorderLayout.WEST);
graphPanel.add(createChoosePanel(), BorderLayout.NORTH);
graphPanel.add(createGraphPanel(), BorderLayout.CENTER);
graphPanel.add(createGraphInfoPanel(), BorderLayout.SOUTH);
// Add the main panel and the graph
this.add(makeTitlePanel(), BorderLayout.NORTH);
this.add(graphPanel, BorderLayout.CENTER);
new Timer(REFRESH_PERIOD, e -> collectSamplesFromQueue()).start();
}
// Methods used in creating the GUI
/**
* Creates the panel containing the graph's Y axis labels.
*
* @return the Y axis panel
*/
private JPanel createYAxis() {
JPanel graphYAxisPanel = new JPanel();
graphYAxisPanel.setLayout(new BorderLayout());
maxYField = createYAxisField(5);
minYField = createYAxisField(3);
graphYAxisPanel.add(createYAxisPanel("graph_results_ms", maxYField), BorderLayout.NORTH); // $NON-NLS-1$
graphYAxisPanel.add(createYAxisPanel("graph_results_ms", minYField), BorderLayout.SOUTH); // $NON-NLS-1$
return graphYAxisPanel;
}
/**
* Creates a text field to be used for the value of a Y axis label. These
* fields hold the minimum and maximum values for the graph. The units are
* kept in a separate label outside of this field.
*
* @param length
* the number of characters which the field will use to calculate
* its preferred width. This should be set to the maximum number
* of digits that are expected to be necessary to hold the label
* value.
*
* @see #createYAxisPanel(String, JTextField)
*
* @return a text field configured to be used in the Y axis
*/
private JTextField createYAxisField(int length) {
JTextField field = new JTextField(length);
field.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0));
field.setEditable(false);
field.setForeground(Color.black);
field.setBackground(getBackground());
field.setHorizontalAlignment(SwingConstants.RIGHT);
return field;
}
/**
* Creates a panel for an entire Y axis label. This includes the dynamic
* value as well as the unit label.
*
* @param labelResourceName
* the name of the label resource. This is used to look up the
* label text using {@link JMeterUtils#getResString(String)}.
*
* @return a panel containing both the dynamic and static parts of a Y axis
* label
*/
private JPanel createYAxisPanel(String labelResourceName, JTextField field) {
JPanel panel = new JPanel(new FlowLayout());
JLabel label = new JLabel(JMeterUtils.getResString(labelResourceName));
panel.add(field);
panel.add(label);
return panel;
}
/**
* Creates a panel which allows the user to choose which graphs to display.
* This panel consists of a check box for each type of graph (current
* sample, average, deviation, and throughput).
*
* @return a panel allowing the user to choose which graphs to display
*/
private JPanel createChoosePanel() {
JPanel chooseGraphsPanel = new JPanel();
chooseGraphsPanel.setLayout(new FlowLayout());
JLabel selectGraphsLabel = new JLabel(JMeterUtils.getResString("graph_choose_graphs")); //$NON-NLS-1$
data = createChooseCheckBox("graph_results_data", Color.black); // $NON-NLS-1$
average = createChooseCheckBox("graph_results_average", Color.blue); // $NON-NLS-1$
deviation = createChooseCheckBox("graph_results_deviation", Color.red); // $NON-NLS-1$
throughput = createChooseCheckBox("graph_results_throughput", JMeterColor.DARK_GREEN); // $NON-NLS-1$
median = createChooseCheckBox("graph_results_median", JMeterColor.PURPLE); // $NON-NLS-1$
chooseGraphsPanel.add(selectGraphsLabel);
chooseGraphsPanel.add(data);
chooseGraphsPanel.add(average);
chooseGraphsPanel.add(median);
chooseGraphsPanel.add(deviation);
chooseGraphsPanel.add(throughput);
return chooseGraphsPanel;
}
/**
* Creates a check box configured to be used to in the choose panel allowing
* the user to select whether or not a particular kind of graph data will be
* displayed.
*
* @param labelResourceName
* the name of the label resource. This is used to look up the
* label text using {@link JMeterUtils#getResString(String)}.
* @param color
* the color used for the checkbox text. By convention this is
* the same color that is used to draw the graph and for the
* corresponding info field.
*
* @return a checkbox allowing the user to select whether or not a kind of
* graph data will be displayed
*/
private JCheckBox createChooseCheckBox(String labelResourceName, Color color) {
JCheckBox checkBox = new JCheckBox(JMeterUtils.getResString(labelResourceName));
checkBox.setSelected(true);
checkBox.addItemListener(this);
checkBox.setForeground(color);
return checkBox;
}
/**
* Creates a scroll pane containing the actual graph of the results.
*
* @return a scroll pane containing the graph
*/
private Component createGraphPanel() {
JScrollPane graphScrollPanel = makeScrollPane(graph, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
graphScrollPanel.setViewportBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
graphScrollPanel.setPreferredSize(graphScrollPanel.getMinimumSize());
return graphScrollPanel;
}
/**
* Creates a panel which numerically displays the current graph values.
*
* @return a panel showing the current graph values
*/
private Box createGraphInfoPanel() {
Box graphInfoPanel = Box.createHorizontalBox();
noSamplesField = createInfoField(Color.black, 6);
dataField = createInfoField(Color.black, 5);
averageField = createInfoField(Color.blue, 5);
deviationField = createInfoField(Color.red, 5);
throughputField = createInfoField(JMeterColor.DARK_GREEN, 15);
medianField = createInfoField(JMeterColor.PURPLE, 5);
graphInfoPanel.add(createInfoColumn(createInfoLabel("graph_results_no_samples", noSamplesField), // $NON-NLS-1$
noSamplesField, createInfoLabel("graph_results_deviation", deviationField), deviationField)); // $NON-NLS-1$
graphInfoPanel.add(Box.createHorizontalGlue());
graphInfoPanel.add(createInfoColumn(createInfoLabel("graph_results_latest_sample", dataField), dataField, // $NON-NLS-1$
createInfoLabel("graph_results_throughput", throughputField), throughputField)); // $NON-NLS-1$
graphInfoPanel.add(Box.createHorizontalGlue());
graphInfoPanel.add(createInfoColumn(createInfoLabel("graph_results_average", averageField), averageField, // $NON-NLS-1$
createInfoLabel("graph_results_median", medianField), medianField)); // $NON-NLS-1$
graphInfoPanel.add(Box.createHorizontalGlue());
return graphInfoPanel;
}
/**
* Creates one of the fields used to display the graph's current values.
*
* @param color
* the color used to draw the value. By convention this is the
* same color that is used to draw the graph for this value and
* in the choose panel.
* @param length
* the number of digits which the field should be able to display
*
* @return a text field configured to display one of the current graph
* values
*/
private JTextField createInfoField(Color color, int length) {
JTextField field = new JTextField(length);
field.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
field.setEditable(false);
field.setForeground(color);
field.setBackground(getBackground());
// The text field should expand horizontally, but have
// a fixed height
field.setMaximumSize(new Dimension(field.getMaximumSize().width, field.getPreferredSize().height));
return field;
}
/**
* Creates a label for one of the fields used to display the graph's current
* values. Neither the label created by this method or the
* field passed as a parameter is added to the GUI here.
*
* @param labelResourceName
* the name of the label resource. This is used to look up the
* label text using {@link JMeterUtils#getResString(String)}.
* @param field
* the field this label is being created for.
*/
private JLabel createInfoLabel(String labelResourceName, JTextField field) {
JLabel label = new JLabel(JMeterUtils.getResString(labelResourceName));
label.setForeground(field.getForeground());
label.setLabelFor(field);
return label;
}
/**
* Creates a panel containing two pairs of labels and fields for displaying
* the current graph values. This method exists to help with laying out the
* fields in columns. If one or more components are null then these
* components will be represented by blank space.
*
* @param label1
* the label for the first field. This label will be placed in
* the upper left section of the panel. If this parameter is
* null, this section of the panel will be left blank.
* @param field1
* the field corresponding to the first label. This field will be
* placed in the upper right section of the panel. If this
* parameter is null, this section of the panel will be left
* blank.
* @param label2
* the label for the second field. This label will be placed in
* the lower left section of the panel. If this parameter is
* null, this section of the panel will be left blank.
* @param field2
* the field corresponding to the second label. This field will
* be placed in the lower right section of the panel. If this
* parameter is null, this section of the panel will be left
* blank.
*/
private Box createInfoColumn(JLabel label1, JTextField field1, JLabel label2, JTextField field2) {
// This column actually consists of a row with two sub-columns
// The first column contains the labels, and the second
// column contains the fields.
Box row = Box.createHorizontalBox();
Box col = Box.createVerticalBox();
col.add(label1 != null ? label1 : Box.createVerticalGlue());
col.add(label2 != null ? label2 : Box.createVerticalGlue());
row.add(col);
row.add(Box.createHorizontalStrut(5));
col = Box.createVerticalBox();
col.add(field1 != null ? field1 : Box.createVerticalGlue());
col.add(field2 != null ? field2 : Box.createVerticalGlue());
row.add(col);
row.add(Box.createHorizontalStrut(5));
return row;
}
}