Index: build.properties
===================================================================
--- build.properties (revision 1656228)
+++ build.properties (working copy)
@@ -171,6 +171,11 @@
excalibur-pool-instrumented.loc = ${maven2.repo}/excalibur-pool/excalibur-pool-instrumented/${excalibur-pool-instrumented.version}
excalibur-pool-instrumented.md5 = 1b5425fe0fe63dc67da6fe995db6be31
+freemarker.version = 2.3.21
+freemarker.loc = ${maven2.repo}/org/freemarker/freemarker/${freemarker.version}
+freemarker.jar = freemarker-${freemarker.version}.jar
+freemarker.md5 = bdc6a9d3a41bc13e5965fc06e74c16c2
+
# Common file containing both htmlparser and htmllexer jars
htmlparser.version = 2.1
htmllexer.loc = ${maven2.repo}/org/htmlparser/htmllexer/${htmlparser.version}
Index: build.xml
===================================================================
--- build.xml (revision 1656250)
+++ build.xml (working copy)
@@ -371,6 +371,7 @@
+
@@ -446,6 +447,7 @@
+
@@ -2871,6 +2873,7 @@
+
Index: report_templates/HTML_REPORT.ftl
===================================================================
--- report_templates/HTML_REPORT.ftl (revision 0)
+++ report_templates/HTML_REPORT.ftl (revision 0)
@@ -0,0 +1,240 @@
+
+<#setting number_format="##0">
+
+
+
+
+
+
+
+
+
+
+ Dashboard
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
APDEX
+
+
+ ${APDEX}
+
+
+
+
+
+
+
Requests Summary
+
+
+
+
+
+
+
+
+
+
+ Label |
+ #Samples |
+ KO |
+ Error% |
+ 90% Line |
+ 95% Line |
+ 99% Line |
+ Throughput |
+ KB/sec |
+ Min |
+ Max |
+
+
+
+
+ ${TOTAL.label} |
+ ${TOTAL.count} |
+ ${TOTAL.errorCount} |
+ ${TOTAL.errorPercentage?string.percent} |
+ ${TOTAL.getPercentPoint(0.9)} |
+ ${TOTAL.getPercentPoint(0.95)} |
+ ${TOTAL.getPercentPoint(0.99)} |
+ ${TOTAL.rate} |
+ ${TOTAL.getKBPerSecond()} |
+ ${TOTAL.min} |
+ ${TOTAL.max} |
+
+
+
+<#list resultsPerSample?keys as key>
+<#assign prop = resultsPerSample[key]>
+
+ ${prop.label} |
+ ${prop.count} |
+ ${prop.errorCount} |
+ ${prop.errorPercentage?string.percent} |
+ ${prop.getPercentPoint(0.9)} |
+ ${prop.getPercentPoint(0.95)} |
+ ${prop.getPercentPoint(0.99)} |
+ ${prop.rate} |
+ ${prop.getKBPerSecond()} |
+ ${prop.min} |
+ ${prop.max} |
+
+#list>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: src/components/org/apache/jmeter/visualizers/backend/BackendListener.java
===================================================================
--- src/components/org/apache/jmeter/visualizers/backend/BackendListener.java (revision 1656228)
+++ src/components/org/apache/jmeter/visualizers/backend/BackendListener.java (working copy)
@@ -169,6 +169,9 @@
BackendListenerContext context = new BackendListenerContext(args);
SampleResult sr = listenerClientData.client.createSampleResult(context, event.getResult());
+ if(sr == null) {
+ return;
+ }
try {
if (!listenerClientData.queue.offer(sr)){ // we failed to add the element first time
listenerClientData.queueWaits.incrementAndGet();
Index: src/components/org/apache/jmeter/visualizers/backend/apdex/ApdexBackendListenerClient.java
===================================================================
--- src/components/org/apache/jmeter/visualizers/backend/apdex/ApdexBackendListenerClient.java (revision 0)
+++ src/components/org/apache/jmeter/visualizers/backend/apdex/ApdexBackendListenerClient.java (revision 0)
@@ -0,0 +1,221 @@
+/*
+ * 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.backend.apdex;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.jmeter.config.Arguments;
+import org.apache.jmeter.samplers.SampleResult;
+import org.apache.jmeter.util.JMeterUtils;
+import org.apache.jmeter.visualizers.SamplingStatCalculator;
+import org.apache.jmeter.visualizers.backend.AbstractBackendListenerClient;
+import org.apache.jmeter.visualizers.backend.BackendListenerContext;
+import org.apache.jorphan.logging.LoggingManager;
+import org.apache.jorphan.util.JOrphanUtils;
+import org.apache.log.Logger;
+
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateExceptionHandler;
+
+/**
+ * APDEX Computer
+ * @since 2.13
+ */
+public class ApdexBackendListenerClient extends AbstractBackendListenerClient {
+ private static final Logger LOGGER = LoggingManager.getLoggerForClass();
+ private static final String SEPARATOR = ";"; //$NON-NLS-1$
+
+ /**
+ * T : Threshold for satistied users.
+ * Response times <= satisfiedResponseTimeMS count towards satisfying
+ */
+ private long satisfiedResponseTimeMS;
+ /**
+ * F : Threshold for tolerated users.
+ * Response times > satisfiedResponseTimeMS and <= toleratedResponseTimeMS count towards tolerated response times
+ * If user does not provide it, it is equal to 4xT
+ */
+ private long toleratedResponseTimeMS;
+ /**
+ * Semicolon separated samplers to include in results
+ */
+ private String samplersList = ""; //$NON-NLS-1$
+ /**
+ * Set of samplers to filter, computed from samplersList
+ */
+ private Set
samplersToFilter;
+
+ /**
+ * Number of Successful Responses for samplers in samplersToFilter with Response Time <= T
+ */
+ private AtomicLong numberOfSatistiedResponses = new AtomicLong(0);
+ /**
+ * Number of Successful Responses for samplers in samplersToFilter with Response Time > T and <= F
+ */
+ private AtomicLong numberOfToleratedResponses = new AtomicLong(0);
+ /**
+ * Number of Responses for samplers in samplersToFilter (OK + KO)
+ */
+ private AtomicLong totalResponses = new AtomicLong(0);
+ private String resultsFile;
+ private String reportFile;
+ public ApdexBackendListenerClient() {
+ super();
+ }
+
+
+ /**
+ * @return the samplersList
+ */
+ public String getSamplersList() {
+ return samplersList;
+ }
+
+ /**
+ * @param samplersList the samplersList to set
+ */
+ public void setSamplersList(String samplersList) {
+ this.samplersList = samplersList;
+ }
+
+ @Override
+ public void handleSampleResults(List sampleResults,
+ BackendListenerContext context) {
+ // NOOP
+ }
+
+ @Override
+ public void setupTest(BackendListenerContext context) throws Exception {
+ String satisfiedResponseTimeAsString = context.getParameter("satisfiedResponseTimeMS", "");
+ if(!StringUtils.isEmpty(satisfiedResponseTimeAsString)) {
+ try {
+ satisfiedResponseTimeMS = Long.parseLong(satisfiedResponseTimeAsString);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Error parsing satisfiedResponseTimeMS '"
+ +satisfiedResponseTimeAsString+"', message:"+e.getMessage());
+ }
+ } else {
+ throw new IllegalArgumentException("satisfiedResponseTimeMS is null or empty");
+ }
+
+ String toleratedResponseTimeAsString = context.getParameter("toleratedResponseTimeMS", "");
+ if(!StringUtils.isEmpty(toleratedResponseTimeAsString)) {
+ try {
+ toleratedResponseTimeMS = Long.parseLong(toleratedResponseTimeAsString);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Error parsing toleratedResponseTimeMS '"
+ +satisfiedResponseTimeAsString+"', message:"+e.getMessage());
+ }
+ } else {
+ toleratedResponseTimeMS = 4*satisfiedResponseTimeMS;
+ }
+
+ samplersList = context.getParameter("samplersList", "");
+ String[] samplers = samplersList.split(SEPARATOR);
+ samplersToFilter = new HashSet();
+ for (String samplerName : samplers) {
+ samplersToFilter.add(samplerName.trim());
+ }
+
+ resultsFile = context.getParameter("resultsFile");
+ reportFile = context.getParameter("reportFile");
+
+ numberOfSatistiedResponses.set(0);
+ numberOfToleratedResponses.set(0);
+ totalResponses.set(0);
+ }
+
+ @Override
+ public void teardownTest(BackendListenerContext context) throws Exception {
+ ReportVisualizer reportVisualizer = new ReportVisualizer(samplersToFilter, satisfiedResponseTimeMS, toleratedResponseTimeMS);
+ ReportResultCollector collector = new ReportResultCollector();
+ collector.setFilename(resultsFile);
+ collector.setListener(reportVisualizer);
+ collector.loadExistingFile();
+ Map results = reportVisualizer.getResultsPerSampleLabel();
+ for (Map.Entry entry : results.entrySet()) {
+ System.out.println(entry.getKey()+":"+entry.getValue());
+ }
+ generateReport(reportFile, reportVisualizer);
+ samplersToFilter.clear();
+ super.teardownTest(context);
+ }
+
+ private void generateReport(String reportFile, ReportVisualizer reportVisualizer)
+ throws Exception {
+ Configuration cfg = new Configuration(Configuration.VERSION_2_3_21);
+ cfg.setDirectoryForTemplateLoading(new File(JMeterUtils.getJMeterHome(),"report_templates"));
+ cfg.setDefaultEncoding("UTF-8");
+ cfg.setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER);
+ Writer writer = null;
+ try {
+ //Load template from source folder
+ Template template = cfg.getTemplate("HTML_REPORT.ftl");
+
+ // Build the data-model
+ Map data = new HashMap();
+ data.put("APDEX", "APDEX:"+Float.toString(reportVisualizer.computeApdex()) + " for thresholds T:"+satisfiedResponseTimeMS+"ms, F:"+toleratedResponseTimeMS
+ + "ms on samplers:"+samplersToFilter);
+ Map map = reportVisualizer.getResultsPerSampleLabel();
+ SamplingStatCalculator total = map.get(ReportVisualizer.TOTAL_ROW_LABEL);
+ data.put("TOTAL", total);
+ map.remove(ReportVisualizer.TOTAL_ROW_LABEL);
+ data.put("resultsPerSample", map);
+ writer = new BufferedWriter(new OutputStreamWriter(
+ new FileOutputStream(reportFile), "UTF-8"));
+ // File output
+ template.process(data, writer);
+ } finally {
+ JOrphanUtils.closeQuietly(writer);
+ }
+ }
+
+ @Override
+ public Arguments getDefaultParameters() {
+ Arguments arguments = new Arguments();
+ arguments.addArgument("satisfiedResponseTimeMS", "");
+ arguments.addArgument("toleratedResponseTimeMS", "");
+ arguments.addArgument("samplersList", "");
+ arguments.addArgument("resultsFile", "");
+ arguments.addArgument("reportFile", "");
+ return arguments;
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.apache.jmeter.visualizers.backend.AbstractBackendListenerClient#createSampleResult(org.apache.jmeter.visualizers.backend.BackendListenerContext, org.apache.jmeter.samplers.SampleResult)
+ */
+ @Override
+ public SampleResult createSampleResult(BackendListenerContext context,
+ SampleResult result) {
+ return result;
+ }
+}
Index: src/components/org/apache/jmeter/visualizers/backend/apdex/ReportResultCollector.java
===================================================================
--- src/components/org/apache/jmeter/visualizers/backend/apdex/ReportResultCollector.java (revision 0)
+++ src/components/org/apache/jmeter/visualizers/backend/apdex/ReportResultCollector.java (revision 0)
@@ -0,0 +1,57 @@
+/*
+ * 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.backend.apdex;
+
+import org.apache.jmeter.reporters.ResultCollector;
+import org.apache.jmeter.reporters.Summariser;
+import org.apache.jmeter.samplers.SampleEvent;
+
+/**
+ *
+ */
+public class ReportResultCollector extends ResultCollector {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 4360593252987031579L;
+
+ /**
+ *
+ */
+ public ReportResultCollector() {
+ super();
+ }
+
+ /**
+ * @param summer
+ */
+ public ReportResultCollector(Summariser summer) {
+ super(summer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.jmeter.reporters.ResultCollector#sampleOccurred(org.apache.jmeter.samplers.SampleEvent)
+ */
+ @Override
+ public void sampleOccurred(SampleEvent event) {
+ super.sampleOccurred(event);
+ }
+
+}
Index: src/components/org/apache/jmeter/visualizers/backend/apdex/ReportVisualizer.java
===================================================================
--- src/components/org/apache/jmeter/visualizers/backend/apdex/ReportVisualizer.java (revision 0)
+++ src/components/org/apache/jmeter/visualizers/backend/apdex/ReportVisualizer.java (revision 0)
@@ -0,0 +1,139 @@
+/*
+ * 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.backend.apdex;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.jmeter.samplers.SampleResult;
+import org.apache.jmeter.util.JMeterUtils;
+import org.apache.jmeter.visualizers.SamplingStatCalculator;
+import org.apache.jmeter.visualizers.gui.AbstractVisualizer;
+
+/**
+ * @since 2.13
+ */
+public class ReportVisualizer extends AbstractVisualizer {
+ /**
+ *
+ */
+ private static final long serialVersionUID = -783284260364641662L;
+ private final Map tableRows =
+ new ConcurrentHashMap();
+ public final static String TOTAL_ROW_LABEL = JMeterUtils
+ .getResString("aggregate_report_total_label"); //$NON-NLS-1$
+ /**
+ * T : Threshold for satistied users.
+ * Response times <= satisfiedResponseTimeMS count towards satisfying
+ */
+ private long satisfiedResponseTimeMS;
+ /**
+ * F : Threshold for tolerated users.
+ * Response times > satisfiedResponseTimeMS and <= toleratedResponseTimeMS count towards tolerated response times
+ * If user does not provide it, it is equal to 4xT
+ */
+ private long toleratedResponseTimeMS;
+
+ /**
+ * Set of samplers to filter, computed from samplersList
+ */
+ private Set samplersToFilter;
+
+ /**
+ * Number of Successful Responses for samplers in samplersToFilter with Response Time <= T
+ */
+ private AtomicLong numberOfSatistiedResponses = new AtomicLong(0);
+ /**
+ * Number of Successful Responses for samplers in samplersToFilter with Response Time > T and <= F
+ */
+ private AtomicLong numberOfToleratedResponses = new AtomicLong(0);
+ /**
+ * Number of Responses for samplers in samplersToFilter (OK + KO)
+ */
+ private AtomicLong totalResponses = new AtomicLong(0);
+
+ public ReportVisualizer(Set samplersToFilter,
+ long satisfiedResponseTimeMS,
+ long toleratedResponseTimeMS) {
+ this.samplersToFilter = samplersToFilter;
+ this.satisfiedResponseTimeMS = satisfiedResponseTimeMS;
+ this.toleratedResponseTimeMS = toleratedResponseTimeMS;
+ }
+
+ @Override
+ public void add(SampleResult res) {
+ final String sampleLabel = res.getSampleLabel();
+ if(!samplersToFilter.contains(sampleLabel)) {
+ return;
+ }
+ SamplingStatCalculator row = null;
+ row = tableRows.get(sampleLabel);
+ if (row == null) {
+ row = new SamplingStatCalculator(sampleLabel);
+ tableRows.put(row.getLabel(), row);
+ }
+ row.addSample(res);
+
+ SamplingStatCalculator tot = tableRows.get(TOTAL_ROW_LABEL);
+ if (tot == null) {
+ tot = new SamplingStatCalculator(TOTAL_ROW_LABEL);
+ tableRows.put(tot.getLabel(), tot);
+ }
+ tot.addSample(res);
+
+ totalResponses.incrementAndGet();
+
+ if(res.isSuccessful()) {
+ if(res.getTime()<=satisfiedResponseTimeMS) {
+ numberOfSatistiedResponses.incrementAndGet();
+ } else if (res.getTime()<=toleratedResponseTimeMS) {
+ numberOfToleratedResponses.incrementAndGet();
+ }
+ }
+ }
+
+ public Map getResultsPerSampleLabel() {
+ return tableRows;
+ }
+
+ @Override
+ public void clearData() {
+ tableRows.clear();
+ tableRows.put(TOTAL_ROW_LABEL, new SamplingStatCalculator(TOTAL_ROW_LABEL));
+ numberOfSatistiedResponses.set(0);
+ numberOfToleratedResponses.set(0);
+ totalResponses.set(0);
+ }
+
+ @Override
+ public String getLabelResource() {
+ return "TEST";
+ }
+
+ /**
+ * @return float APDEX
+ * See http://www.apdex.org/overview.html
+ */
+ public float computeApdex() {
+ double apdex = ((double)(numberOfSatistiedResponses.get()+numberOfToleratedResponses.get()/2))/totalResponses.get();
+ return (float)apdex;
+ }
+}