/*
* 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 kg.apc.jmeter.vizualizers;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableCellEditor;
import kg.apc.charting.GraphPanelChart;
import kg.apc.jmeter.JMeterPluginsUtils;
import kg.apc.jmeter.graphs.AbstractGraphPanelVisualizer;
import kg.apc.jmeter.vizualizers.CorrectedResultCollector;
import kg.apc.jmeter.vizualizers.JSettingsPanel;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.SavePropertyDialog;
import org.apache.jmeter.gui.util.FilePanel;
import org.apache.jmeter.gui.util.MenuFactory;
import org.apache.jmeter.gui.util.PowerTableModel;
import org.apache.jmeter.gui.util.VerticalPanel;
import org.apache.jmeter.reporters.ResultCollector;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.property.CollectionProperty;
import org.apache.jmeter.testelement.property.JMeterProperty;
import org.apache.jmeter.testelement.property.NullProperty;
import org.apache.jmeter.testelement.property.StringProperty;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.gui.ComponentUtil;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
import org.jmeterplugins.save.MergeResultsService;
/**
*
* @author Felix Henry
* @author Vincent Daburon
*/
public class MergeResultsGui extends AbstractGraphPanelVisualizer implements
TableModelListener, CellEditorListener, ChangeListener, ActionListener {
private static final long serialVersionUID = 240L;
private static final Logger log = LoggingManager.getLoggerForClass();
public static final String WIKIPAGE = "MergeResults";
public static final String[] columnIdentifiers = new String[] {
"Input File Name", "Prefix label", "Offset start (sec)",
"Offset end (sec)", "Include label", "RegExp Inc.",
"Exclude label", "RegExp Exc." };
public static final Class<?>[] columnClasses = { String.class,
String.class, String.class, String.class, String.class,
Boolean.class, String.class, Boolean.class };
public static final Object[] defaultValues = { "", "", "", "", "", false,
"", false };
/** File Extensions */
private static final String[] EXTS = { ".jtl", ".csv" }; // $NON-NLS-1$
// $NON-NLS-2$
public static final String DEFAULT_FILENAME = "";
public static final String FILENAME = "MergeResultsControlGui.filename"; // $NON-NLS-1$
public static final String DATA_PROPERTY = "MergeResultsControlGui.data"; // $NON-NLS-1$
public static final int INPUT_FILE_NAME = 0;
public static final int PREFIX_LABEL = 1;
public static final int OFFSET_START = 2;
public static final int OFFSET_END = 3;
public static final int INCLUDE_LABEL = 4;
public static final int REGEX_INCLUDE = 5;
public static final int EXCLUDE_LABEL = 6;
public static final int REGEX_EXCLUDE = 7;
private static final int MAX_FILE_HANDLES = 4;
private static final long REF_START_TIME = 946681200000L; // 2000-01-01 00:00:00.000
private static final String ACTION_ADD = "add"; // $NON-NLS-1$
private static final String ACTION_COPY = "copy"; // $NON-NLS-1$
private static final String ACTION_DELETE = "delete"; // $NON-NLS-1$
private static final String ACTION_MERGE = "merge"; // $NON-NLS-1$
private static final String ACTION_SAVE_CONFIG = "save_config"; // $NON-NLS-1$
protected PowerTableModel tableModel;
protected JTable grid;
protected JButton addRowButton;
protected JButton copyRowButton;
protected JButton deleteRowButton;
protected JButton mergeButton;
private FilePanel mergeAndWritePanel;
private String prefixLabel = "";
private List<SampleResult> samples = new ArrayList<SampleResult>(0);
private MergeResultsService mergeService;
public MergeResultsGui() {
super();
init();
}
@Override
public String getLabelResource() {
return this.getClass().getSimpleName(); //$NON-NLS-1$
}
@Override
public String getStaticLabel() {
return JMeterPluginsUtils.prefixLabel("Merge Results");
}
@Override
public Collection<String> getMenuCategories() {
return Arrays.asList(new String[] { MenuFactory.NON_TEST_ELEMENTS });
}
protected Component getMergeAndWritePanel() {
return mergeAndWritePanel;
}
public void setFile(String filename) {
mergeAndWritePanel.setFilename(filename);
}
public String getFile() {
return mergeAndWritePanel.getFilename();
}
public String getWikiPage() {
return "MergeResults";
}
/** Only for use in testing */
public JTable getGrid() {
return grid;
}
/**
* Modifies a given TestElement to mirror the data in the gui components.
*
* @see org.apache.jmeter.gui.JMeterGUIComponent#modifyTestElement(TestElement)
*/
@Override
public void modifyTestElement(TestElement c) {
super.modifyTestElement(c);
if (c instanceof ResultCollector) {
ResultCollector rc = (ResultCollector) c;
rc.setFilename(getFile());
rc.setProperty(new StringProperty(
CorrectedResultCollector.FILENAME, getFile()));
CollectionProperty rows = JMeterPluginsUtils
.tableModelRowsToCollectionProperty(tableModel,
DATA_PROPERTY);
rc.setProperty(rows);
collector = rc;
}
}
@Override
public void configure(TestElement el) {
super.configure(el);
setFile(el.getPropertyAsString(CorrectedResultCollector.FILENAME));
JMeterProperty fileValues = el.getProperty(DATA_PROPERTY);
if (!(fileValues instanceof NullProperty)) {
CollectionProperty columns = (CollectionProperty) fileValues;
tableModel.removeTableModelListener(this);
JMeterPluginsUtils.collectionPropertyToTableModelRows(columns,
tableModel, columnClasses);
tableModel.addTableModelListener(this);
updateUI();
} else {
log.warn("Received null property instead of collection");
}
checkDeleteButtonStatus();
checkMergeButtonStatus();
startTimeRef = 0;
}
@Override
public void updateUI() {
super.updateUI();
if (tableModel != null) {
CorrectedResultCollector crc = new CorrectedResultCollector();
crc.setProperty(JMeterPluginsUtils
.tableModelRowsToCollectionPropertyEval(tableModel,
DATA_PROPERTY));
}
}
private void init() {
this.setLayout(new BorderLayout());
// MAIN PANEL
JPanel mainPanel = new JPanel();
Border margin = new EmptyBorder(10, 10, 5, 10);
mainPanel.setBorder(margin);
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
mainPanel.add(JMeterPluginsUtils.addHelpLinkToPanel(makeTitlePanel(),
WIKIPAGE));
JPanel containerPanel = new VerticalPanel();
JButton saveConfigButton = new JButton(
JMeterUtils.getResString("config_save_settings")); // $NON-NLS-1$
saveConfigButton.addActionListener(this);
saveConfigButton.setActionCommand(ACTION_SAVE_CONFIG);
mergeAndWritePanel = new FilePanel(
"Merge and write results to output file", EXTS); // $NON-NLS-1$
mergeAndWritePanel.addChangeListener(this);
mergeAndWritePanel.add(saveConfigButton);
mergeButton = new JButton("Merge");
mergeButton.addActionListener(this);
mergeButton.setActionCommand(ACTION_MERGE);
JPanel controlPanel = new JPanel();
controlPanel.add(mergeButton);
containerPanel.add(createParamsPanel());
containerPanel.add(getMergeAndWritePanel());
containerPanel.add(controlPanel);
add(mainPanel, BorderLayout.NORTH);
add(containerPanel, BorderLayout.CENTER);
hideFilePanel();
enableMaximize(false);
}
private boolean loadFilesFromTable(CorrectedResultCollector crc) {
for (int row = 0; row < tableModel.getRowCount(); row++) {
startTimeRef = 0;
crc.setFilename(String.valueOf(tableModel.getValueAt(row,
INPUT_FILE_NAME)));
if (crc.getFilename().isEmpty()) {
GuiPackage.showErrorMessage(
"Error merging results files - could not open file [row: "
+ row + ";filename: " + collector.getFilename()
+ "]", "Input file error");
return false;
}
crc.setStartOffset(String.valueOf(tableModel.getValueAt(row,
OFFSET_START)));
crc.setEndOffset(String.valueOf(tableModel.getValueAt(row,
OFFSET_END)));
crc.setIncludeLabels(String.valueOf(tableModel.getValueAt(row,
INCLUDE_LABEL)));
crc.setExcludeLabels(String.valueOf(tableModel.getValueAt(row,
EXCLUDE_LABEL)));
crc.setEnabledIncludeRegex(Boolean.valueOf(String
.valueOf((tableModel.getValueAt(row, REGEX_INCLUDE)))));
crc.setEnabledExcludeRegex(Boolean.valueOf(String
.valueOf((tableModel.getValueAt(row, REGEX_EXCLUDE)))));
crc.setPrefixLabel(String.valueOf((tableModel.getValueAt(row,
PREFIX_LABEL))));
setUpFiltering(crc);
crc.loadExistingFile();
}
return true;
}
@Override
public void add(SampleResult res) {
if (!isSampleIncluded(res)) {
return;
}
res.setSaveConfig(collector.getSaveConfig());
if (!prefixLabel.isEmpty()) {
String resLabel = res.getSampleLabel();
res.setSampleLabel(prefixLabel + resLabel);
}
res.setTimeStamp(res.getTimeStamp() - startTimeRef + REF_START_TIME);
samples.add(res);
}
@Override
public void setUpFiltering(CorrectedResultCollector rc) {
super.setUpFiltering(rc);
prefixLabel = rc.getPrefixLabel(CorrectedResultCollector.PREFIX_LABEL);
}
protected JPanel createParamsPanel() {
JPanel panel = new JPanel(new BorderLayout(5, 5));
panel.setBorder(BorderFactory.createTitledBorder("Merge Options"));
panel.setPreferredSize(new Dimension(200, 200));
JScrollPane scroll = new JScrollPane(createGrid());
scroll.setPreferredSize(scroll.getMinimumSize());
JPanel buttonsPanel = new JPanel(new GridLayout(1, 2));
addRowButton = new JButton("Add Row");
copyRowButton = new JButton("Copy Row");
deleteRowButton = new JButton("Delete Row");
addRowButton.addActionListener(this);
addRowButton.setActionCommand(ACTION_ADD);
copyRowButton.addActionListener(this);
copyRowButton.setActionCommand(ACTION_COPY);
deleteRowButton.addActionListener(this);
deleteRowButton.setActionCommand(ACTION_DELETE);
buttonsPanel.add(addRowButton);
buttonsPanel.add(copyRowButton);
buttonsPanel.add(deleteRowButton);
panel.add(scroll, BorderLayout.CENTER);
panel.add(buttonsPanel, BorderLayout.SOUTH);
return panel;
}
public void actionPerformed(ActionEvent action) {
String command = action.getActionCommand();
if (grid.isEditing()) {
TableCellEditor cellEditor = grid.getCellEditor(
grid.getEditingRow(), grid.getEditingColumn());
cellEditor.stopCellEditing();
}
if (command.equals(ACTION_ADD)) {
tableModel.addRow(defaultValues);
tableModel.fireTableDataChanged();
if (tableModel.getRowCount() >= MAX_FILE_HANDLES) {
addRowButton.setEnabled(false);
copyRowButton.setEnabled(false);
}
// Enable DELETE and MERGE
deleteRowButton.setEnabled(true);
mergeButton.setEnabled(true);
// Highlight (select) the appropriate row.
int rowToSelect = tableModel.getRowCount() - 1;
if (rowToSelect < grid.getRowCount()) {
grid.setRowSelectionInterval(rowToSelect, rowToSelect);
}
updateUI();
} else if (command.equals(ACTION_COPY)) {
final int selectedRow = grid.getSelectedRow();
if (tableModel.getRowCount() == 0 || selectedRow < 0) {
return;
}
tableModel.addRow(tableModel.getRowData(selectedRow));
tableModel.fireTableDataChanged();
if (tableModel.getRowCount() >= MAX_FILE_HANDLES) {
addRowButton.setEnabled(false);
copyRowButton.setEnabled(false);
}
// Enable DELETE and MERGE
deleteRowButton.setEnabled(true);
mergeButton.setEnabled(true);
// Highlight (select) the appropriate row.
int rowToSelect = selectedRow + 1;
grid.setRowSelectionInterval(rowToSelect, rowToSelect);
updateUI();
} else if (command.equals(ACTION_DELETE)) {
final int rowSelected = grid.getSelectedRow();
if (rowSelected >= 0) {
tableModel.removeRow(rowSelected);
tableModel.fireTableDataChanged();
if (tableModel.getRowCount() < MAX_FILE_HANDLES) {
addRowButton.setEnabled(true);
copyRowButton.setEnabled(true);
}
// Disable DELETE and MERGE if there are no rows in the
// table to delete.
if (tableModel.getRowCount() == 0) {
deleteRowButton.setEnabled(false);
mergeButton.setEnabled(false);
}
// Table still contains one or more rows, so highlight
// (select) the appropriate one.
else {
int rowToSelect = rowSelected;
if (rowSelected >= tableModel.getRowCount()) {
rowToSelect = rowSelected - 1;
}
grid.setRowSelectionInterval(rowToSelect, rowToSelect);
}
updateUI();
}
} else if (command.equals(ACTION_MERGE)) {
String output = getFile();
if (output.isEmpty()) {
GuiPackage.showErrorMessage(
"Error merging results files - empty output filename",
"Output file error");
return;
}
boolean isSuccess = loadFilesFromTable((CorrectedResultCollector) collector);
if (isSuccess) {
mergeService = new MergeResultsService();
collector.setFilename(output);
mergeService.mergeSamples((CorrectedResultCollector) collector,
samples);
}
samples.clear();
} else if (command.equals(ACTION_SAVE_CONFIG)) {
SavePropertyDialog d = new SavePropertyDialog(GuiPackage
.getInstance().getMainFrame(),
JMeterUtils
.getResString("sample_result_save_configuration"), // $NON-NLS-1$
true, collector.getSaveConfig());
d.pack();
ComponentUtil.centerComponentInComponent(GuiPackage.getInstance()
.getMainFrame(), d);
d.setVisible(true);
}
}
public void checkDeleteButtonStatus() {
deleteRowButton.setEnabled(tableModel != null
&& tableModel.getRowCount() > 0);
}
public void checkMergeButtonStatus() {
mergeButton.setEnabled(tableModel != null
&& tableModel.getRowCount() > 0);
}
private JTable createGrid() {
grid = new JTable();
grid.getDefaultEditor(String.class).addCellEditorListener(this);
createTableModel();
grid.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
grid.setMinimumSize(new Dimension(200, 100));
return grid;
}
private void createTableModel() {
tableModel = new PowerTableModel(columnIdentifiers, columnClasses);
tableModel.addTableModelListener(this);
grid.setModel(tableModel);
}
public void tableChanged(TableModelEvent e) {
// log.info("Model changed");
updateUI();
}
public void editingStopped(ChangeEvent e) {
// log.info("Editing stopped");
updateUI();
}
public void editingCanceled(ChangeEvent e) {
// no action needed
}
@Override
public void stateChanged(ChangeEvent e) {
log.debug("getting new collector");
collector = (ResultCollector) createTestElement();
}
@Override
public Dimension getPreferredSize() {
return getMinimumSize();
}
/**
* Implements JMeterGUIComponent.clearGui
*/
@Override
public void clearGui() {
super.clearGui();
tableModel.clearData();
mergeAndWritePanel.clearGui();
}
@Override
protected JSettingsPanel createSettingsPanel() {
return new JSettingsPanel(this, 0);
}
@Override
public GraphPanelChart getGraphPanelChart() {
return new FakeGraphPanelChart();
}
private class FakeGraphPanelChart extends GraphPanelChart {
public FakeGraphPanelChart() {
super(false);
}
@Override
public void saveGraphToCSV(File file) throws IOException {
log.info("Saving CSV to " + file.getAbsolutePath());
mergeService = new MergeResultsService();
collector.setFilename(file.getName());
mergeService.mergeSamples((CorrectedResultCollector) collector,
samples);
}
@Override
public void saveGraphToPNG(File file, int w, int h) throws IOException {
throw new UnsupportedOperationException(
"This plugin type cannot be saved as image");
}
}
}