package kg.apc.jmeter.vizualizers;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import kg.apc.charting.AbstractGraphRow;
import kg.apc.jmeter.CMDLineArgumentsProcessor;
import kg.apc.jmeter.JMeterPluginsUtils;
import kg.apc.jmeter.graphs.AbstractOverTimeVisualizer;
import kg.apc.jmeter.gui.ButtonPanelAddCopyRemove;
import org.apache.jmeter.gui.util.PowerTableModel;
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.PropertyIterator;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
public class PageDataExtractorOverTimeGui extends AbstractOverTimeVisualizer implements CMDLineArgumentsProcessor {
private static final String REGEXPS_PROPERTY = "regexps";
private static final Logger log = LoggingManager.getLoggerForClass();
private PowerTableModel tableModel;
private JTable grid;
public static final String[] columnIdentifiers = new String[]{
"Chart label", "Regular expression value extractor", "Delta", "RegExp label"
};
public static final Class[] columnClasses = new Class[]{
String.class, String.class, Boolean.class, Boolean.class
};
private static Object[] defaultValues = new Object[]{
"", "", false, false
};
private CollectionProperty regExps = null;
private HashMap<String, Pattern> patterns = new HashMap<String, Pattern>();
private ArrayList<Object> cmdRegExps = null;
private HashMap<String, Double> oldValues = new HashMap<String, Double>();
public PageDataExtractorOverTimeGui() {
setGranulation(1000);
graphPanel.getGraphObject().setYAxisLabel("Metrics Extracted");
graphPanel.getGraphObject().getChartSettings().setExpendRows(true);
}
@Override
protected JSettingsPanel createSettingsPanel() {
return new JSettingsPanel(this,
JSettingsPanel.GRADIENT_OPTION
| JSettingsPanel.MAXY_OPTION
| JSettingsPanel.RELATIVE_TIME_OPTION
| JSettingsPanel.AUTO_EXPAND_OPTION
| JSettingsPanel.MARKERS_OPTION);
}
@Override
public String getStaticLabel() {
return JMeterPluginsUtils.prefixLabel("Page Data Extractor");
}
@Override
public String getLabelResource() {
return getClass().getSimpleName();
}
@Override
public String getWikiPage() {
return "PageDataExtractor";
}
private JTable createGrid() {
grid = new JTable();
createTableModel();
grid.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
grid.setMinimumSize(new Dimension(200, 100));
grid.getColumnModel().getColumn(0).setPreferredWidth(350);
grid.getColumnModel().getColumn(1).setPreferredWidth(350);
grid.getColumnModel().getColumn(2).setPreferredWidth(50);
grid.getColumnModel().getColumn(3).setPreferredWidth(110);
return grid;
}
private Component createRegExpPanel() {
JPanel panel = new JPanel(new BorderLayout(5, 5));
panel.setBorder(BorderFactory.createTitledBorder("Regular Expressions Data Extractor"));
panel.setPreferredSize(new Dimension(150, 150));
JScrollPane scroll = new JScrollPane(createGrid());
scroll.setPreferredSize(scroll.getMinimumSize());
panel.add(scroll, BorderLayout.CENTER);
panel.add(new ButtonPanelAddCopyRemove(grid, tableModel, defaultValues), BorderLayout.SOUTH);
return panel;
}
@Override
protected JPanel getGraphPanelContainer() {
JPanel panel = new JPanel(new BorderLayout());
JPanel innerTopPanel = new JPanel(new BorderLayout());
innerTopPanel.add(createRegExpPanel(), BorderLayout.CENTER);
innerTopPanel.add(getFilePanel(), BorderLayout.SOUTH);
panel.add(innerTopPanel, BorderLayout.NORTH);
return panel;
}
private void createTableModel() {
tableModel = new PowerTableModel(columnIdentifiers, columnClasses);
grid.setModel(tableModel);
}
@Override
public TestElement createTestElement() {
TestElement te = super.createTestElement();
modifyTestElement(te);
te.setComment(JMeterPluginsUtils.getWikiLinkText(getWikiPage()));
return te;
}
@Override
public void modifyTestElement(TestElement te) {
super.modifyTestElement(te);
if (grid.isEditing()) {
grid.getCellEditor().stopCellEditing();
}
if (te instanceof CorrectedResultCollector) {
CorrectedResultCollector rc = (CorrectedResultCollector) te;
CollectionProperty rows = JMeterPluginsUtils.tableModelRowsToCollectionProperty(tableModel, REGEXPS_PROPERTY);
rc.setProperty(rows);
}
}
@Override
public void configure(TestElement te) {
super.configure(te);
CorrectedResultCollector rc = (CorrectedResultCollector) te;
JMeterProperty regexpValues = rc.getProperty(REGEXPS_PROPERTY);
if (!(regexpValues instanceof NullProperty)) {
JMeterPluginsUtils.collectionPropertyToTableModelRows((CollectionProperty) regexpValues, tableModel, columnClasses);
regExps = (CollectionProperty) regexpValues;
} else {
log.warn("Received null property instead of collection");
}
}
private void processPageRegExpLabel(String pageBody, String regExpKey, String regExpValue, boolean isDelta, long time) {
//handle multiple keys with same name found with the regexp
ArrayList<String> labels = new ArrayList<String>();
Pattern patternKey = patterns.get(regExpKey);
if (patternKey == null) {
try {
patternKey = Pattern.compile(regExpKey);
patterns.put(regExpKey, patternKey);
} catch (PatternSyntaxException ex) {
log.error("Error compiling pattern: " + regExpKey);
}
}
Pattern patternValue = patterns.get(regExpValue);
if (patternValue == null) {
try {
patternValue = Pattern.compile(regExpValue);
patterns.put(regExpValue, patternValue);
} catch (PatternSyntaxException ex) {
log.error("Error compiling pattern: " + regExpValue);
}
}
if (patternKey != null && patternValue != null) {
Matcher mKey = patternKey.matcher(pageBody);
Matcher mValue = patternValue.matcher(pageBody);
boolean found = false;
while (mKey.find() && mValue.find()) {
found = true;
String key = mKey.group(1);
if (labels.contains(key)) {
int i = 2;
while (labels.contains(key + "_" + i)) {
i++;
}
key = key + "_" + i;
}
labels.add(key);
String sValue = mValue.group(1);
try {
double value = Double.parseDouble(sValue);
if (isDelta) {
if (oldValues.containsKey(key)) {
double delta = value - oldValues.get(key);
addRecord(key, time, delta);
}
oldValues.put(key, value);
} else {
addRecord(key, time, value);
}
} catch (NumberFormatException ex) {
log.error("Value extracted is not a number: " + sValue);
}
}
if (!found) {
log.warn("No data found for regExpKey: " + regExpKey + " and regExpValue: " + regExpValue);
}
}
}
private void processPageLabel(String pageBody, String label, String regExpValue, boolean isDelta, long time) {
Pattern patternValue = patterns.get(regExpValue);
if (patternValue == null) {
try {
patternValue = Pattern.compile(regExpValue);
patterns.put(regExpValue, patternValue);
} catch (PatternSyntaxException ex) {
log.error("Error compiling pattern: " + regExpValue);
}
}
if (patternValue != null) {
Matcher mValue = patternValue.matcher(pageBody);
int counter = 0;
while (mValue.find()) {
counter++;
String key;
if (counter > 1) {
key = label + " (" + counter + ")";
} else {
key = label;
}
String sValue = mValue.group(1);
try {
double value = Double.parseDouble(sValue);
if (isDelta) {
if (oldValues.containsKey(key)) {
double delta = value - oldValues.get(key);
addRecord(key, time, delta);
}
oldValues.put(key, value);
} else {
addRecord(key, time, value);
}
} catch (NumberFormatException ex) {
log.error("Value extracted is not a number: " + sValue);
}
}
if (counter == 0) {
log.warn("No data found for regExpValue: " + regExpValue);
}
}
}
private void addRecord(String label, long time, double value) {
if (!isSampleIncluded(label)) {
return;
}
AbstractGraphRow row = model.get(label);
if (row == null) {
row = getNewRow(model, AbstractGraphRow.ROW_AVERAGES, label, AbstractGraphRow.MARKER_SIZE_SMALL, false, false, false, true, true);
}
row.add(time, value);
}
@Override
public void add(SampleResult res) {
super.add(res);
if (regExps == null && cmdRegExps == null) {
return;
}
String pageBody = res.getResponseDataAsString();
long time = normalizeTime(res.getEndTime());
if (cmdRegExps == null) {
PropertyIterator iter = regExps.iterator();
while (iter.hasNext()) {
CollectionProperty props = (CollectionProperty) iter.next();
String label = props.get(0).getStringValue();
String regExpValue = props.get(1).getStringValue();
Boolean isDelta = props.get(2).getBooleanValue();
Boolean isLabelRegExp = props.get(3).getBooleanValue();
if (isLabelRegExp) {
processPageRegExpLabel(pageBody, label, regExpValue, isDelta, time);
} else {
processPageLabel(pageBody, label, regExpValue, isDelta, time);
}
}
} else {
Iterator<Object> regExpIter = cmdRegExps.iterator();
while (regExpIter.hasNext()) {
String label = (String) regExpIter.next();
String regExpValue = (String) regExpIter.next();
boolean isDelta = (Boolean) regExpIter.next();
boolean isLabelRegExp = (Boolean) regExpIter.next();
if (isLabelRegExp) {
processPageRegExpLabel(pageBody, label, regExpValue, isDelta, time);
} else {
processPageLabel(pageBody, label, regExpValue, isDelta, time);
}
}
}
updateGui(null);
}
@Override
public void clearData() {
oldValues.clear();
super.clearData();
}
private void storeRegExps(String regExps) {
String[] regStrings = regExps.split("\\{;\\}");
if (regStrings.length % 4 != 0) {
throw new IllegalArgumentException("Regular expressions must be succession of key/value/isDelta(true or false)/isRegExpLabel(true or false) separated by {;}");
}
ArrayList<Object> data = new ArrayList<Object>();
for (int i = 0; i < regStrings.length; i = i + 4) {
data.add(regStrings[i]);
data.add(regStrings[i + 1]);
data.add("true".equalsIgnoreCase(regStrings[i + 2]));
data.add("true".equalsIgnoreCase(regStrings[i + 3]));
}
this.cmdRegExps = data;
}
public void processCMDOption(String nextArg, ListIterator args) {
if (nextArg.equalsIgnoreCase("--extractor-regexps")) {
//
if (!args.hasNext()) {
throw new IllegalArgumentException("Missing regular expressions");
}
storeRegExps((String) args.next());
} else {
throw new UnsupportedOperationException("Unknown option: " + nextArg);
}
}
}