/* * 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.listener; import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.io.File; import java.util.HashSet; import java.util.ResourceBundle; import java.util.Set; import kg.apc.jmeter.PluginsCMDWorker; import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.reporters.AbstractListenerElement; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.testbeans.TestBean; import org.apache.jmeter.testbeans.gui.GenericTestBeanCustomizer; import org.apache.jmeter.testelement.TestElement; import org.apache.jmeter.testelement.TestStateListener; import org.apache.jmeter.testelement.property.JMeterProperty; import org.apache.jmeter.testelement.property.StringProperty; import org.apache.jmeter.visualizers.Visualizer; import org.apache.jorphan.logging.LoggingManager; import org.apache.log.Logger; /** * Listener that generates graphs * @since 1.1.3 */ public class GraphsGeneratorListener extends AbstractListenerElement implements TestStateListener, TestBean, TestElement, Visualizer { private static final Logger log = LoggingManager.getLoggerForClass(); private static final String PNG_SUFFIX = ".png"; private static final String CSV_SUFFIX = ".csv"; public enum ExportMode { PNG((byte)0), CSV((byte)1), BOTH((byte)2); private byte value; private ExportMode(byte value) { this.value = value; } public byte getValue() { return value; } } /** * */ private static final long serialVersionUID = -136031193118302572L; private static final String[] pluginTypes = new String[] { "SynthesisReportGui", "AggregateReportGui", "ResponseTimesOverTime", "HitsPerSecond", "BytesThroughputOverTime", "LatenciesOverTime", "ResponseCodesPerSecond", "TransactionsPerSecond", "ResponseTimesDistribution", "ResponseTimesPercentiles", "ThreadsStateOverTime", "TimesVsThreads", "ThroughputVsThreads" }; private static final Set<String> TIME_BASED_GRAPHS = new HashSet<String>(); static { TIME_BASED_GRAPHS.add("ResponseTimesOverTime"); TIME_BASED_GRAPHS.add("HitsPerSecond"); TIME_BASED_GRAPHS.add("BytesThroughputOverTime"); TIME_BASED_GRAPHS.add("LatenciesOverTime"); TIME_BASED_GRAPHS.add("ResponseCodesPerSecond"); TIME_BASED_GRAPHS.add("TransactionsPerSecond"); } private static final Set<String> CSV_ONLY = new HashSet<String>(); static { CSV_ONLY.add("SynthesisReportGui"); CSV_ONLY.add("AggregateReportGui"); } private String outputBaseFolder; private String resultsFileName; private ExportMode exportMode; private String filePrefix; private int graphWidth; private int graphHeight; private boolean aggregateRows; private String paintMarkers; private boolean paintZeroing; private boolean paintGradient; private boolean preventOutliers; private boolean relativeTimes; private boolean autoScaleRows; private String limitRows; private String forceY; private String granulation; private String lineWeight; private String lowCountLimit; private String successFilter; private String includeLabels; private String excludeLabels; private boolean includeSamplesWithRegex; private boolean excludeSamplesWithRegex; private String startOffset; private String endOffset; /* (non-Javadoc) * @see org.apache.jmeter.testelement.TestStateListener#testEnded() */ @Override public void testEnded() { testEnded(""); } /* (non-Javadoc) * @see org.apache.jmeter.testelement.TestStateListener#testEnded(java.lang.String) */ @Override public void testEnded(String host) { for (String pluginType : pluginTypes) { PluginsCMDWorker worker = new PluginsCMDWorker(); worker.setInputFile(resultsFileName); worker.setGraphWidth(graphWidth); worker.setGraphHeight(graphHeight); if (!StringUtils.isEmpty(forceY)) { worker.setForceY(Integer.parseInt(forceY)); } if (!StringUtils.isEmpty(limitRows)) { worker.setRowsLimit(Integer.parseInt(limitRows)); } worker.setAggregate(aggregateRows ? 1 : 0); worker.setPreventOutliers(preventOutliers ? 1 : 0); worker.setAggregate(aggregateRows ? 1 : 0); if (!StringUtils.isEmpty(paintMarkers)) { worker.setMarkers("True". equalsIgnoreCase(paintMarkers) ? 1 : 0); } worker.setZeroing(paintZeroing ? 1 : 0); if (isTimeBasedGraph(pluginType)) { worker.setRelativeTimes(relativeTimes ? 1 : 0); } worker.setGradient(paintGradient ? 1 : 0); worker.setAutoScaleRows(autoScaleRows ? 1 : 0); if (!StringUtils.isEmpty(successFilter)) { worker.setSuccessFilter( "True". equalsIgnoreCase(successFilter) ? 1 : 0); } if (!StringUtils.isEmpty(granulation)) { worker.setGranulation(Integer.parseInt(granulation)); } if (!StringUtils.isEmpty(lineWeight)) { worker.setLineWeight(Float.parseFloat(lineWeight)); } if (!StringUtils.isEmpty(lowCountLimit)) { worker.setHideLowCounts(Integer.parseInt(lowCountLimit)); } if (!StringUtils.isEmpty(includeLabels)) { worker.setIncludeLabels(includeLabels); } if (!StringUtils.isEmpty(excludeLabels)) { worker.setExcludeLabels(excludeLabels); } worker.setIncludeSamplesWithRegex(includeSamplesWithRegex ? 1 : 0); worker.setExcludeSamplesWithRegex(excludeSamplesWithRegex ? 1 : 0); if (!StringUtils.isEmpty(startOffset)) { worker.setStartOffset(startOffset); } if (!StringUtils.isEmpty(endOffset)) { worker.setEndOffset(endOffset); } String fileName; if (!StringUtils.isEmpty(outputBaseFolder)) { fileName = outputBaseFolder + File.separatorChar + filePrefix + pluginType; } else { // Handle backward compatibility fileName = filePrefix + pluginType; } if (!CSV_ONLY.contains(pluginType)) { if (exportMode == ExportMode.PNG) { worker.setOutputPNGFile(fileName+PNG_SUFFIX); worker.addExportMode(PluginsCMDWorker.EXPORT_PNG); } else if (exportMode == ExportMode.CSV) { worker.setOutputCSVFile(fileName+CSV_SUFFIX); worker.addExportMode(PluginsCMDWorker.EXPORT_CSV); } else { worker.setOutputPNGFile(fileName+PNG_SUFFIX); worker.addExportMode(PluginsCMDWorker.EXPORT_PNG); worker.setOutputCSVFile(fileName+CSV_SUFFIX); worker.addExportMode(PluginsCMDWorker.EXPORT_CSV); } } else { worker.setOutputCSVFile(fileName+CSV_SUFFIX); worker.addExportMode(PluginsCMDWorker.EXPORT_CSV); } worker.setPluginType(pluginType); int status = worker.doJob(); if (status == 0) { log.info("Successful generation of file " + fileName + " by plugin:" + pluginType); } else { log.error("Error generating file " + fileName + " by plugin:" + pluginType); } } } /** * * @param graphName String * @return boolean */ private static boolean isTimeBasedGraph(String graphName) { return TIME_BASED_GRAPHS.contains(graphName); } /* (non-Javadoc) * @see org.apache.jmeter.testelement.TestStateListener#testStarted() */ @Override public void testStarted() { testStarted(""); } /* (non-Javadoc) * @see org.apache.jmeter.testelement.TestStateListener#testStarted(java.lang.String) */ @Override public void testStarted(String host) { // NOOP } @Override public void add(SampleResult result) { // NOOP } /** * Override the setProperty method in order to convert * the original String calcMode property. * This used the locale-dependent display value, so caused * problems when the language was changed. * Note that the calcMode StringProperty is replaced with an IntegerProperty * so the conversion only needs to happen once. */ @Override public void setProperty(JMeterProperty property) { if (property instanceof StringProperty) { final String pn = property.getName(); if (pn.equals("exportMode")) { final Object objectValue = property.getObjectValue(); try { final BeanInfo beanInfo = Introspector.getBeanInfo(this.getClass()); final ResourceBundle rb = (ResourceBundle) beanInfo.getBeanDescriptor().getValue(GenericTestBeanCustomizer.RESOURCE_BUNDLE); for(Enum<ExportMode> e : ExportMode.values()) { final String propName = e.toString(); if (objectValue.equals(rb.getObject(propName))) { final int tmpMode = e.ordinal(); if (log .isDebugEnabled()) { log.debug("Converted " + pn + "=" + objectValue + " to mode=" + tmpMode + " using Locale: " + rb.getLocale()); } super.setProperty(pn, tmpMode); return; } } log.warn("Could not convert " + pn + "=" + objectValue + " using Locale: " + rb.getLocale()); } catch (IntrospectionException e) { log.error("Could not find BeanInfo", e); } } } super.setProperty(property); } @Override public boolean isStats() { return false; } /** * @return the resultsFileName */ public String getResultsFileName() { return resultsFileName; } /** * @param resultsFileName the resultsFileName to set */ public void setResultsFileName(String resultsFileName) { this.resultsFileName = resultsFileName; } /** * @return the FilePrefix */ public String getFilePrefix() { return filePrefix; } /** * @param filePrefix the FilePrefix to set */ public void setFilePrefix(String filePrefix) { this.filePrefix = filePrefix; } /** * @return the graphWidth */ public int getGraphWidth() { return graphWidth; } /** * @param graphWidth the graphWidth to set */ public void setGraphWidth(int graphWidth) { this.graphWidth = graphWidth; } /** * @return the graphHeight */ public int getGraphHeight() { return graphHeight; } /** * @param graphHeight the graphHeight to set */ public void setGraphHeight(int graphHeight) { this.graphHeight = graphHeight; } /** * @return the aggregateRows */ public boolean isAggregateRows() { return aggregateRows; } /** * @param aggregateRows the aggregateRows to set */ public void setAggregateRows(boolean aggregateRows) { this.aggregateRows = aggregateRows; } /** * @return the paintZeroing */ public boolean isPaintZeroing() { return paintZeroing; } /** * @param paintZeroing the paintZeroing to set */ public void setPaintZeroing(boolean paintZeroing) { this.paintZeroing = paintZeroing; } /** * @return the paintGradient */ public boolean isPaintGradient() { return paintGradient; } /** * @param paintGradient the paintGradient to set */ public void setPaintGradient(boolean paintGradient) { this.paintGradient = paintGradient; } /** * @return the preventOutliers */ public boolean isPreventOutliers() { return preventOutliers; } /** * @param preventOutliers the preventOutliers to set */ public void setPreventOutliers(boolean preventOutliers) { this.preventOutliers = preventOutliers; } /** * @return the relativeTimes */ public boolean isRelativeTimes() { return relativeTimes; } /** * @param relativeTimes the relativeTimes to set */ public void setRelativeTimes(boolean relativeTimes) { this.relativeTimes = relativeTimes; } /** * @return the autoScaleRows */ public boolean isAutoScaleRows() { return autoScaleRows; } /** * @param autoScaleRows the autoScaleRows to set */ public void setAutoScaleRows(boolean autoScaleRows) { this.autoScaleRows = autoScaleRows; } /** * @return the limitRows */ public String getLimitRows() { return limitRows; } /** * @param limitRows the limitRows to set */ public void setLimitRows(String limitRows) { this.limitRows = limitRows; } /** * @return the forceY */ public String getForceY() { return forceY; } /** * @param forceY the forceY to set */ public void setForceY(String forceY) { this.forceY = forceY; } /** * @return the granulation */ public String getGranulation() { return granulation; } /** * @param granulation the granulation to set */ public void setGranulation(String granulation) { this.granulation = granulation; } /** * @return the lineWeight */ public String getLineWeight() { return lineWeight; } /** * @param lineWeight the lineWeight to set */ public void setLineWeight(String lineWeight) { this.lineWeight = lineWeight; } /** * @return the lowCountLimit */ public String getLowCountLimit() { return lowCountLimit; } /** * @param lowCountLimit the lowCountLimit to set */ public void setLowCountLimit(String lowCountLimit) { this.lowCountLimit = lowCountLimit; } /** * @return the successFilter */ public String getSuccessFilter() { return successFilter; } /** * @param successFilter the successFilter to set */ public void setSuccessFilter(String successFilter) { this.successFilter = successFilter; } /** * @return the includeLabels */ public String getIncludeLabels() { return includeLabels; } /** * @param includeLabels the includeLabels to set */ public void setIncludeLabels(String includeLabels) { this.includeLabels = includeLabels; } /** * @return the excludeLabels */ public String getExcludeLabels() { return excludeLabels; } /** * @param excludeLabels the excludeLabels to set */ public void setExcludeLabels(String excludeLabels) { this.excludeLabels = excludeLabels; } /** * @return the exportMode */ public int getExportMode() { return exportMode.ordinal(); } /** * @param exportMode the exportMode to set */ public void setExportMode(int exportMode) { this.exportMode = ExportMode.values()[exportMode]; } /** * @return the includeSamplesWithRegex */ public boolean isIncludeSamplesWithRegex() { return includeSamplesWithRegex; } /** * @param includeSamplesWithRegex * the includeSamplesWithRegex to set */ public void setIncludeSamplesWithRegex(boolean includeSamplesWithRegex) { this.includeSamplesWithRegex = includeSamplesWithRegex; } /** * @return the excludeSamplesWithRegex */ public boolean isExcludeSamplesWithRegex() { return excludeSamplesWithRegex; } /** * @param excludeSamplesWithRegex * the excludeSamplesWithRegex to set */ public void setExcludeSamplesWithRegex(boolean excludeSamplesWithRegex) { this.excludeSamplesWithRegex = excludeSamplesWithRegex; } /** * @return the start offset */ public String getStartOffset() { return startOffset; } /** * @param startOffset * the start offset to set */ public void setStartOffset(String startOffset) { this.startOffset = startOffset; } /** * @return the end offset */ public String getEndOffset() { return endOffset; } /** * @param endOffset * the start offset to set */ public void setEndOffset(String endOffset) { this.endOffset = endOffset; } /** * @return the outputBaseFolder */ public String getOutputBaseFolder() { return outputBaseFolder; } /** * @param outputBaseFolder the outputBaseFolder to set */ public void setOutputBaseFolder(String outputBaseFolder) { this.outputBaseFolder = outputBaseFolder; } /** * @return the paintMarkers */ public String getPaintMarkers() { return paintMarkers; } /** * @param paintMarkers the paintMarkers to set */ public void setPaintMarkers(String paintMarkers) { this.paintMarkers = paintMarkers; } }