Author: pmouawad
Date: Tue Jun 26 07:30:08 2018
New Revision: 1834399

URL: http://svn.apache.org/viewvc?rev=1834399&view=rev
Log:
Bug 62166 - Report/Dashboard: Provide ability to register custom graphs and 
metrics in the JMeter Dashboard

Contributed by UbikLoadPack
Bugzilla Id: 62166

Added:
    jmeter/trunk/bin/report-template/content/js/customGraph.js.fmkr
    jmeter/trunk/bin/report-template/content/js/dashboard-commons.js.fmkr
    jmeter/trunk/bin/report-template/content/pages/CustomsGraphs.html.fmkr
    
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/CustomGraphConsumer.java
   (with props)
    
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/ResponseCustomGraphGraphConsumer.java
   (with props)
    jmeter/trunk/test/src/org/apache/jmeter/report/processor/graph/
    jmeter/trunk/test/src/org/apache/jmeter/report/processor/graph/impl/
    
jmeter/trunk/test/src/org/apache/jmeter/report/processor/graph/impl/CustomGraphConsumerTest.java
   (with props)
Removed:
    jmeter/trunk/bin/report-template/content/js/dashboard-commons.js
Modified:
    jmeter/trunk/bin/report-template/content/js/graph.js.fmkr
    jmeter/trunk/bin/report-template/content/pages/OverTime.html.fmkr
    jmeter/trunk/bin/report-template/content/pages/ResponseTimes.html.fmkr
    jmeter/trunk/bin/report-template/content/pages/Throughput.html.fmkr
    jmeter/trunk/bin/report-template/index.html.fmkr
    jmeter/trunk/bin/user.properties
    
jmeter/trunk/src/core/org/apache/jmeter/report/dashboard/HtmlTemplateExporter.java
    
jmeter/trunk/src/core/org/apache/jmeter/report/dashboard/ReportGenerator.java
    
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/AbstractGraphConsumer.java
    
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/AbstractOverTimeGraphConsumer.java
    
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/AbstractVersusRequestsGraphConsumer.java
    
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/BytesThroughputGraphConsumer.java
    
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/CodesPerSecondGraphConsumer.java
    
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/HitsPerSecondGraphConsumer.java
    
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/ResponseTimePerSampleGraphConsumer.java
    
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/ResponseTimePercentilesGraphConsumer.java
    
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/TotalTPSGraphConsumer.java
    
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/TransactionsPerSecondGraphConsumer.java
    jmeter/trunk/xdocs/changes.xml
    jmeter/trunk/xdocs/usermanual/generating-dashboard.xml

Added: jmeter/trunk/bin/report-template/content/js/customGraph.js.fmkr
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/bin/report-template/content/js/customGraph.js.fmkr?rev=1834399&view=auto
==============================================================================
--- jmeter/trunk/bin/report-template/content/js/customGraph.js.fmkr (added)
+++ jmeter/trunk/bin/report-template/content/js/customGraph.js.fmkr Tue Jun 26 
07:30:08 2018
@@ -0,0 +1,160 @@
+/*
+   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.
+*/
+
+
+    
+$(document).ready(function() {
+    $(".click-title").mouseenter( function(    e){
+        e.preventDefault();
+        this.style.cursor="pointer";
+    });
+    $(".click-title").mousedown( function(event){
+        event.preventDefault();
+    });
+    
+    <#list customsGraphsData?keys as key> 
+    try{
+        refresh${key}(true);
+    } catch(e){
+        console.log(e);
+    }<#break> <#-- Stop after the first iteration in purpose to show the first 
graph only -->
+    </#list>
+    
+    $(".portlet-header").css("cursor", "auto");
+});
+
+<#list customsGraphsData?keys as key>
+var response${key}Infos = {
+    data: ${customsGraphsData[key]},
+    getOptions: function(){
+        return {
+            series: {
+                lines: {
+                    show: true
+                },
+                points: {
+                    show: true
+                }
+            },
+            xaxis: {
+                mode: "time",
+                timeformat: getTimeFormat(this.data.result.granularity),
+                axisLabel: 
'${graphConfigurations[key].getProperties()["set_X_Axis"]!"Default X Axis 
Title"}',
+                axisLabelUseCanvas: true,
+                axisLabelFontSizePixels: 12,
+                axisLabelFontFamily: 'Verdana, Arial',
+                axisLabelPadding: 20,
+            },
+            yaxis: {
+                axisLabel: 
'${graphConfigurations[key].getProperties()["set_Y_Axis"]!"Default Y Axis 
Title"}',
+                axisLabelUseCanvas: true,
+                axisLabelFontSizePixels: 12,
+                axisLabelFontFamily: 'Verdana, Arial',
+                axisLabelPadding: 20,
+            },
+            legend: {
+                noColumns: 2,
+                show: true,
+                container: '#legendResponse${key}'
+            },
+            selection: {
+                mode: 'xy'
+            },
+            grid: {
+                hoverable: true // IMPORTANT! this is needed for tooltip to
+                                // work
+            },
+            tooltip: true,
+            tooltipOpts: {
+                content: "%s : at %x 
${graphConfigurations[key].getProperties()["setContentMessage"]!"Default 
content message"} %y"
+            }
+        };
+    },
+    createGraph: function() {
+        var data = this.data;
+        var dataset = prepareData(data.result.series, 
$("#choicesResponse${key}"));
+        var options = this.getOptions();
+        prepareOptions(options, data);
+        $.plot($("#flotResponse${key}"), dataset, options);
+        // setup overview
+        $.plot($("#overviewResponse${key}"), dataset, 
prepareOverviewOptions(options));
+    }
+};
+
+// Response Custom Graph
+function refresh${key}(fixTimestamps) {
+    var infos = response${key}Infos;
+    prepareSeries(infos.data);
+    if(fixTimestamps) {
+        fixTimeStamps(infos.data.result.series, ${(timeZoneOffset?c)!0});
+    }
+    if(isGraph($("#flotResponse${key}"))){
+        infos.createGraph();
+    }else{
+        var choiceContainer = $("#choicesResponse${key}");
+        createLegend(choiceContainer, infos);
+        infos.createGraph();
+        setGraphZoomable("#flotResponse${key}", "#overviewResponse${key}");
+        $('#footerResponse${key} .legendColorBox > div').each(function(i){
+            $(this).clone().prependTo(choiceContainer.find("li").eq(i));
+        });
+    }
+};
+    </#list>
+
+function collapse(elem, collapsed){
+    if(collapsed){
+        
$(elem).parent().find(".fa-chevron-up").removeClass("fa-chevron-up").addClass("fa-chevron-down");
+    }else{
+        
$(elem).parent().find(".fa-chevron-down").removeClass("fa-chevron-down").addClass("fa-chevron-up");
+    
+    <#assign loopCount = 0>
+    <#list customsGraphsData?keys as key>
+    <#if loopCount == 0>
+    if(elem.id == "bodyResponse${key}"){
+    <#else>
+    else if(elem.id == "bodyResponse${key}"){
+    </#if>
+        if (isGraph($(elem).find('.flot-chart-content')) == false) {
+            refresh${key}(true);
+        }
+            document.location.href="#${key}";
+        }
+    </#list>
+    }
+}
+
+function toggleAll(id, checked){
+    var placeholder = document.getElementById(id);
+    var cases = $(placeholder).find(':checkbox');
+    cases.prop('checked', checked);
+    $(cases).parent().children().children().toggleClass("legend-disabled", 
!checked);
+    var choiceContainer;
+    
+    <#assign loopCount = 0>
+    <#list customsGraphsData?keys as key>
+    <#if loopCount == 0>
+    if(id == "choicesResponse${key}"){
+    <#else>
+    else if(id == "choicesResponse${key}"){
+    </#if>
+        choiceContainer = $("#choicesResponse${key}");
+        refresh${key}(false);
+    }
+    <#assign loopCount++>    
+    </#list>
+}
\ No newline at end of file

Added: jmeter/trunk/bin/report-template/content/js/dashboard-commons.js.fmkr
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/bin/report-template/content/js/dashboard-commons.js.fmkr?rev=1834399&view=auto
==============================================================================
--- jmeter/trunk/bin/report-template/content/js/dashboard-commons.js.fmkr 
(added)
+++ jmeter/trunk/bin/report-template/content/js/dashboard-commons.js.fmkr Tue 
Jun 26 07:30:08 2018
@@ -0,0 +1,461 @@
+/*
+   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.
+*/
+var DAY_MS   = 86400000;
+var HOUR_MS  =  3600000;
+var MINUTE_MS  =    60000;
+
+/**
+ * From 
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math/round
+ * Licensed under https://creativecommons.org/licenses/by-sa/2.5/
+ */
+// Closure
+(function() {
+  /**
+   * Decimal adjustment of a number.
+   *
+   * @param {String}  type  The type of adjustment.
+   * @param {Number}  value The number.
+   * @param {Integer} exp   The exponent (the 10 logarithm of the adjustment 
base).
+   * @returns {Number} The adjusted value.
+   */
+  function decimalAdjust(type, value, exp) {
+    // If the exp is undefined or zero...
+    if (typeof exp === 'undefined' || +exp === 0) {
+      return Math[type](value);
+    }
+    value = +value;
+    exp = +exp;
+    // If the value is not a number or the exp is not an integer...
+    if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
+      return NaN;
+    }
+    // Shift
+    value = value.toString().split('e');
+    value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : 
-exp)));
+    // Shift back
+    value = value.toString().split('e');
+    return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
+  }
+
+  // Decimal round
+  if (!Math.round10) {
+    Math.round10 = function(value, exp) {
+      return decimalAdjust('round', value, exp);
+    };
+  }
+  // Decimal floor
+  if (!Math.floor10) {
+    Math.floor10 = function(value, exp) {
+      return decimalAdjust('floor', value, exp);
+    };
+  }
+  // Decimal ceil
+  if (!Math.ceil10) {
+    Math.ceil10 = function(value, exp) {
+      return decimalAdjust('ceil', value, exp);
+    };
+  }
+})();
+
+/*
+ * Suffixes the specified value with a unit
+ * The spaced argument defines whether a space character is introduced.
+ */
+function formatUnit(value, unit, spaced){
+    return spaced ? value + " " + unit : value + unit;
+}
+
+/*
+ * Gets a string representing the specified duration in milliseconds.
+ * 
+ * E.g : duration = 20000100, returns "45 min 20 sec 100 ms"
+ */
+function formatDuration(duration, spaced) {
+    var type = $.type(duration);
+    if (type === "string")
+        return duration;
+
+    // Calculate each part of the string
+    var days = Math.floor(duration / 86400000); // 1000 * 60 * 60 * 24 = 1 day
+    duration %= 8640000;
+
+    var hours = Math.floor(duration / 3600000); // 1000 * 60 *60 = 1 hour
+    duration %= 3600000;
+
+    var minutes = Math.floor(duration / 60000); // 1000 * 60 = 1 minute
+    duration %= 60000;
+
+    var seconds = Math.floor(duration / 1000); // 1 second
+    duration %= 1000;
+
+    // Add non zero part.
+    var formatArray = [];
+    if (days > 0)
+        formatArray.push(formatUnit(days, " day(s)", spaced));
+
+    if (hours > 0)
+        formatArray.push(formatUnit(hours, " hour(s)", spaced));
+
+    if (minutes > 0)
+        formatArray.push(formatUnit(minutes," min", spaced));
+
+    if (seconds > 0)
+        formatArray.push(formatUnit(seconds, " sec", spaced));
+
+    if (duration > 0)
+        formatArray.push(formatUnit(duration, " ms", spaced));
+
+    // Build the string
+    return formatArray.join(" ");
+}
+
+/*
+ * Gets axis label for the specified granularity
+ */
+function getElapsedTimeLabel(granularity) {
+    return "Elapsed Time (granularity: " + formatDuration(granularity) + ")";
+}
+
+/*
+ * Gets time format based on granularity
+ */
+function getTimeFormat(granularity) {
+    if (granularity >= DAY_MS) {
+        return "%y/%m/%d"; 
+    } else if (granularity >= HOUR_MS) {
+        return "%m/%d %H"; 
+    } else if (granularity >= MINUTE_MS) {
+        return "%d %H:%M";
+    } else {
+        return "%H:%M:%S";
+    }
+}
+
+/*
+ * Gets axis label for the specified granularity
+ */
+function getConnectTimeLabel(granularity) {
+    return "Connect Time (granularity: " + formatDuration(granularity) + ")";
+}
+
+//Get the property value of an object using the specified key
+//Returns the property value if all properties in the key exist; undefined
+//otherwise.
+function getProperty(key, obj) {
+    return key.split('.').reduce(function(prop, subprop){
+        return prop && prop[subprop];
+    }, obj);
+}
+
+/*
+ * Removes quotes from the specified string 
+ */
+function unquote(str, quoteChar) {
+    quoteChar = quoteChar || '"';
+    if (str.length > 0 && str[0] === quoteChar && str[str.length - 1] === 
quoteChar)
+        return str.slice(1, str.length - 1);
+    else
+        return str;
+};
+
+/*
+ * This comparison function evaluates abscissas to sort array of coordinates.
+ */
+function compareByXCoordinate(coord1, coord2) {
+    return coord2[0] - coord1[0];
+}
+
+
+/*
+ * Followings functions and variables are used to generate statics graphs
+ * AND dynamics graphs (if you have the plugin)
+ */
+
+var showControllersOnly = ${showControllersOnly?c!"false"};
+var seriesFilter = ${seriesFilter!"undefined"};
+var filtersOnlySampleSeries = ${filtersOnlySampleSeries?c!"false"};
+
+// Fixes time stamps
+function fixTimeStamps(series, offset){
+    $.each(series, function(index, item) {
+        $.each(item.data, function(index, coord) {
+            coord[0] += offset;
+        });
+    });
+}
+
+// Check if the specified jquery object is a graph
+function isGraph(object){
+    return object.data('plot') !== undefined;
+}
+
+// Collapse
+$(function() {
+        $('.collapse').on('shown.bs.collapse', function(){
+            collapse(this, false);
+        }).on('hidden.bs.collapse', function(){
+            collapse(this, true);
+        });
+});
+
+$(function() {
+    $(".glyphicon").mousedown( function(event){
+        var tmp = $('.in:not(ul)');
+        
tmp.parent().parent().parent().find(".fa-chevron-up").removeClass("fa-chevron-down").addClass("fa-chevron-down");
+        tmp.removeClass("in");
+        tmp.addClass("out");
+    });
+});
+
+/**
+ * Export graph to a PNG
+ */
+function exportToPNG(graphName, target) {
+    var plot = $("#"+graphName).data('plot');
+    var flotCanvas = plot.getCanvas();
+    var image = flotCanvas.toDataURL();
+    image = image.replace("image/png", "image/octet-stream");
+    
+    var downloadAttrSupported = ("download" in document.createElement("a"));
+    if(downloadAttrSupported === true) {
+        target.download = graphName + ".png";
+        target.href = image;
+    }
+    else {
+        document.location.href = image;
+    }
+}
+
+// Override the specified graph options to fit the requirements of an overview
+function prepareOverviewOptions(graphOptions){
+    var overviewOptions = {
+        series: {
+            shadowSize: 0,
+            lines: {
+                lineWidth: 1
+            },
+            points: {
+                // Show points on overview only when linked graph does not show
+                // lines
+                show: getProperty('series.lines.show', graphOptions) == false,
+                radius : 1
+            }
+        },
+        xaxis: {
+            ticks: 2,
+            axisLabel: null
+        },
+        yaxis: {
+            ticks: 2,
+            axisLabel: null
+        },
+        legend: {
+            show: false,
+            container: null
+        },
+        grid: {
+            hoverable: false
+        },
+        tooltip: false
+    };
+    return $.extend(true, {}, graphOptions, overviewOptions);
+}
+
+function prepareOptions(options, data) {
+    options.canvas = true;
+    var extraOptions = data.extraOptions;
+    if(extraOptions !== undefined){
+        var xOffset = options.xaxis.mode === "time" ? ${(timeZoneOffset?c)!0} 
: 0;
+        var yOffset = options.yaxis.mode === "time" ? ${(timeZoneOffset?c)!0} 
: 0;
+
+        if(!isNaN(extraOptions.minX))
+               options.xaxis.min = parseFloat(extraOptions.minX) + xOffset;
+        
+        if(!isNaN(extraOptions.maxX))
+               options.xaxis.max = parseFloat(extraOptions.maxX) + xOffset;
+        
+        if(!isNaN(extraOptions.minY))
+               options.yaxis.min = parseFloat(extraOptions.minY) + yOffset;
+        
+        if(!isNaN(extraOptions.maxY))
+               options.yaxis.max = parseFloat(extraOptions.maxY) + yOffset;
+    }
+}
+
+// Filter, mark series and sort data
+/**
+ * @param data
+ * @param noMatchColor if defined and true, series.color are not matched with 
index
+ */
+function prepareSeries(data, noMatchColor){
+    var result = data.result;
+
+    // Keep only series when needed
+    if(seriesFilter && (!filtersOnlySampleSeries || 
result.supportsControllersDiscrimination)){
+        // Insensitive case matching
+        var regexp = new RegExp(seriesFilter, 'i');
+        result.series = $.grep(result.series, function(series, index){
+            return regexp.test(series.label);
+        });
+    }
+
+    // Keep only controllers series when supported and needed
+    if(result.supportsControllersDiscrimination && showControllersOnly){
+        result.series = $.grep(result.series, function(series, index){
+            return series.isController;
+        });
+    }
+
+    // Sort data and mark series
+    $.each(result.series, function(index, series) {
+        series.data.sort(compareByXCoordinate);
+        if(!(noMatchColor && noMatchColor===true)) {
+               series.color = index;
+           }
+    });
+}
+
+// Set the zoom on the specified plot object
+function zoomPlot(plot, xmin, xmax, ymin, ymax){
+    var axes = plot.getAxes();
+    // Override axes min and max options
+    $.extend(true, axes, {
+        xaxis: {
+            options : { min: xmin, max: xmax }
+        },
+        yaxis: {
+            options : { min: ymin, max: ymax }
+        }
+    });
+
+    // Redraw the plot
+    plot.setupGrid();
+    plot.draw();
+}
+
+// Prepares DOM items to add zoom function on the specified graph
+function setGraphZoomable(graphSelector, overviewSelector){
+    var graph = $(graphSelector);
+    var overview = $(overviewSelector);
+
+    // Ignore mouse down event
+    graph.bind("mousedown", function() { return false; });
+    overview.bind("mousedown", function() { return false; });
+
+    // Zoom on selection
+    graph.bind("plotselected", function (event, ranges) {
+        // clamp the zooming to prevent infinite zoom
+        if (ranges.xaxis.to - ranges.xaxis.from < 0.00001) {
+            ranges.xaxis.to = ranges.xaxis.from + 0.00001;
+        }
+        if (ranges.yaxis.to - ranges.yaxis.from < 0.00001) {
+            ranges.yaxis.to = ranges.yaxis.from + 0.00001;
+        }
+
+        // Do the zooming
+        var plot = graph.data('plot');
+        zoomPlot(plot, ranges.xaxis.from, ranges.xaxis.to, ranges.yaxis.from, 
ranges.yaxis.to);
+        plot.clearSelection();
+
+        // Synchronize overview selection
+        overview.data('plot').setSelection(ranges, true);
+    });
+
+    // Zoom linked graph on overview selection
+    overview.bind("plotselected", function (event, ranges) {
+        graph.data('plot').setSelection(ranges);
+    });
+
+    // Reset linked graph zoom when reseting overview selection
+    overview.bind("plotunselected", function () {
+        var overviewAxes = overview.data('plot').getAxes();
+        zoomPlot(graph.data('plot'), overviewAxes.xaxis.min, 
overviewAxes.xaxis.max, overviewAxes.yaxis.min, overviewAxes.yaxis.max);
+    });
+}
+
+// Prepares data to be consumed by plot plugins
+function prepareData(series, choiceContainer, customizeSeries){
+    var datasets = [];
+
+    // Add only selected series to the data set
+    choiceContainer.find("input:checked").each(function (index, item) {
+        var key = $(item).attr("name");
+        var i = 0;
+        var size = series.length;
+        while(i < size && series[i].label != key)
+            i++;
+        if(i < size){
+            var currentSeries = series[i];
+            datasets.push(currentSeries);
+            if(customizeSeries)
+                customizeSeries(currentSeries);
+        }
+    });
+    return datasets;
+}
+
+/*
+ * Ignore case comparator
+ */
+function sortAlphaCaseless(a,b){
+    return a.toLowerCase() > b.toLowerCase() ? 1 : -1;
+};
+
+function createLegend(choiceContainer, infos) {
+    // Sort series by name
+    var keys = [];
+    $.each(infos.data.result.series, function(index, series){
+        keys.push(series.label);
+    });
+    keys.sort(sortAlphaCaseless);
+
+    // Create list of series with support of activation/deactivation
+    $.each(keys, function(index, key) {
+        var id = choiceContainer.attr('id') + index;
+        $('<li />')
+            .append($('<input id="' + id + '" name="' + key + '" 
type="checkbox" checked="checked" hidden />'))
+            .append($('<label />', { 'text': key , 'for': id }))
+            .appendTo(choiceContainer);
+    });
+    choiceContainer.find("label").click( function(){
+        if (this.style.color !== "rgb(129, 129, 129)" ){
+            this.style.color="#818181";
+        }else {
+            this.style.color="black";
+        }
+        $(this).parent().children().children().toggleClass("legend-disabled");
+    });
+    choiceContainer.find("label").mousedown( function(event){
+        event.preventDefault();
+    });
+    choiceContainer.find("label").mouseenter(function(){
+        this.style.cursor="pointer";
+    });
+
+    // Recreate graphe on series activation toggle
+    choiceContainer.find("input").click(function(){
+        infos.createGraph();
+    });
+}
+
+// Unchecks all boxes for "Hide all samples" functionality
+function uncheckAll(id){
+    toggleAll(id, false);
+}
+
+// Checks all boxes for "Show all samples" functionality
+function checkAll(id){
+    toggleAll(id, true);
+}
\ No newline at end of file

Modified: jmeter/trunk/bin/report-template/content/js/graph.js.fmkr
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/bin/report-template/content/js/graph.js.fmkr?rev=1834399&r1=1834398&r2=1834399&view=diff
==============================================================================
--- jmeter/trunk/bin/report-template/content/js/graph.js.fmkr (original)
+++ jmeter/trunk/bin/report-template/content/js/graph.js.fmkr Tue Jun 26 
07:30:08 2018
@@ -34,194 +34,8 @@ $(document).ready(function() {
     try{
         refreshResponseTimePercentiles();
     } catch(e){}
-    $(".portlet-header").css("cursor", "auto");
 });
 
-var showControllersOnly = ${showControllersOnly?c!"false"};
-var seriesFilter = ${seriesFilter!"undefined"};
-var filtersOnlySampleSeries = ${filtersOnlySampleSeries?c!"false"};
-
-// Fixes time stamps
-function fixTimeStamps(series, offset){
-    $.each(series, function(index, item) {
-        $.each(item.data, function(index, coord) {
-            coord[0] += offset;
-        });
-    });
-}
-
-// Check if the specified jquery object is a graph
-function isGraph(object){
-    return object.data('plot') !== undefined;
-}
-
-/**
- * Export graph to a PNG
- */
-function exportToPNG(graphName, target) {
-    var plot = $("#"+graphName).data('plot');
-    var flotCanvas = plot.getCanvas();
-    var image = flotCanvas.toDataURL();
-    image = image.replace("image/png", "image/octet-stream");
-    
-    var downloadAttrSupported = ("download" in document.createElement("a"));
-    if(downloadAttrSupported === true) {
-        target.download = graphName + ".png";
-        target.href = image;
-    }
-    else {
-        document.location.href = image;
-    }
-    
-}
-
-// Override the specified graph options to fit the requirements of an overview
-function prepareOverviewOptions(graphOptions){
-    var overviewOptions = {
-        series: {
-            shadowSize: 0,
-            lines: {
-                lineWidth: 1
-            },
-            points: {
-                // Show points on overview only when linked graph does not show
-                // lines
-                show: getProperty('series.lines.show', graphOptions) == false,
-                radius : 1
-            }
-        },
-        xaxis: {
-            ticks: 2,
-            axisLabel: null
-        },
-        yaxis: {
-            ticks: 2,
-            axisLabel: null
-        },
-        legend: {
-            show: false,
-            container: null
-        },
-        grid: {
-            hoverable: false
-        },
-        tooltip: false
-    };
-    return $.extend(true, {}, graphOptions, overviewOptions);
-}
-
-// Force axes boundaries using graph extra options
-function prepareOptions(options, data) {
-    options.canvas = true;
-    var extraOptions = data.extraOptions;
-    if(extraOptions !== undefined){
-        var xOffset = options.xaxis.mode === "time" ? ${(timeZoneOffset?c)!0} 
: 0;
-        var yOffset = options.yaxis.mode === "time" ? ${(timeZoneOffset?c)!0} 
: 0;
-
-        if(!isNaN(extraOptions.minX))
-               options.xaxis.min = parseFloat(extraOptions.minX) + xOffset;
-        
-        if(!isNaN(extraOptions.maxX))
-               options.xaxis.max = parseFloat(extraOptions.maxX) + xOffset;
-        
-        if(!isNaN(extraOptions.minY))
-               options.yaxis.min = parseFloat(extraOptions.minY) + yOffset;
-        
-        if(!isNaN(extraOptions.maxY))
-               options.yaxis.max = parseFloat(extraOptions.maxY) + yOffset;
-    }
-}
-
-// Filter, mark series and sort data
-/**
- * @param data
- * @param noMatchColor if defined and true, series.color are not matched with 
index
- */
-function prepareSeries(data, noMatchColor){
-    var result = data.result;
-
-    // Keep only series when needed
-    if(seriesFilter && (!filtersOnlySampleSeries || 
result.supportsControllersDiscrimination)){
-        // Insensitive case matching
-        var regexp = new RegExp(seriesFilter, 'i');
-        result.series = $.grep(result.series, function(series, index){
-            return regexp.test(series.label);
-        });
-    }
-
-    // Keep only controllers series when supported and needed
-    if(result.supportsControllersDiscrimination && showControllersOnly){
-        result.series = $.grep(result.series, function(series, index){
-            return series.isController;
-        });
-    }
-
-    // Sort data and mark series
-    $.each(result.series, function(index, series) {
-        series.data.sort(compareByXCoordinate);
-        if(!(noMatchColor && noMatchColor===true)) {
-               series.color = index;
-           }
-    });
-}
-
-// Set the zoom on the specified plot object
-function zoomPlot(plot, xmin, xmax, ymin, ymax){
-    var axes = plot.getAxes();
-    // Override axes min and max options
-    $.extend(true, axes, {
-        xaxis: {
-            options : { min: xmin, max: xmax }
-        },
-        yaxis: {
-            options : { min: ymin, max: ymax }
-        }
-    });
-
-    // Redraw the plot
-    plot.setupGrid();
-    plot.draw();
-}
-
-// Prepares DOM items to add zoom function on the specified graph
-function setGraphZoomable(graphSelector, overviewSelector){
-    var graph = $(graphSelector);
-    var overview = $(overviewSelector);
-
-    // Ignore mouse down event
-    graph.bind("mousedown", function() { return false; });
-    overview.bind("mousedown", function() { return false; });
-
-    // Zoom on selection
-    graph.bind("plotselected", function (event, ranges) {
-        // clamp the zooming to prevent infinite zoom
-        if (ranges.xaxis.to - ranges.xaxis.from < 0.00001) {
-            ranges.xaxis.to = ranges.xaxis.from + 0.00001;
-        }
-        if (ranges.yaxis.to - ranges.yaxis.from < 0.00001) {
-            ranges.yaxis.to = ranges.yaxis.from + 0.00001;
-        }
-
-        // Do the zooming
-        var plot = graph.data('plot');
-        zoomPlot(plot, ranges.xaxis.from, ranges.xaxis.to, ranges.yaxis.from, 
ranges.yaxis.to);
-        plot.clearSelection();
-
-        // Synchronize overview selection
-        overview.data('plot').setSelection(ranges, true);
-    });
-
-    // Zoom linked graph on overview selection
-    overview.bind("plotselected", function (event, ranges) {
-        graph.data('plot').setSelection(ranges);
-    });
-
-    // Reset linked graph zoom when reseting overview selection
-    overview.bind("plotunselected", function () {
-        var overviewAxes = overview.data('plot').getAxes();
-        zoomPlot(graph.data('plot'), overviewAxes.xaxis.min, 
overviewAxes.xaxis.max, overviewAxes.yaxis.min, overviewAxes.yaxis.max);
-    });
-}
 
 var responseTimePercentilesInfos = {
         data: ${responseTimePercentiles!"{}"},
@@ -1426,6 +1240,11 @@ function collapse(elem, collapsed){
                 refreshLatenciesOverTime(true);
             }
             document.location.href="#latenciesOverTime";
+        } else if (elem.id == "bodyCustomGraph") {
+            if (isGraph($(elem).find('.flot-chart-content')) == false) {
+                refreshCustomGraph(true);
+            }
+            document.location.href="#responseCustomGraph";
         } else if (elem.id == "bodyConnectTimeOverTime") {
             if (isGraph($(elem).find('.flot-chart-content')) == false) {
                 refreshConnectTimeOverTime(true);
@@ -1485,24 +1304,6 @@ function collapse(elem, collapsed){
     }
 }
 
-// Collapse
-$(function() {
-        $('.collapse').on('shown.bs.collapse', function(){
-            collapse(this, false);
-        }).on('hidden.bs.collapse', function(){
-            collapse(this, true);
-        });
-});
-
-$(function() {
-    $(".glyphicon").mousedown( function(event){
-        var tmp = $('.in:not(ul)');
-        
tmp.parent().parent().parent().find(".fa-chevron-up").removeClass("fa-chevron-down").addClass("fa-chevron-down");
-        tmp.removeClass("in");
-        tmp.addClass("out");
-    });
-});
-
 /*
  * Activates or deactivates all series of the specified graph (represented by 
id parameter)
  * depending on checked argument.
@@ -1521,6 +1322,9 @@ function toggleAll(id, checked){
     } else if(id == "choicesResponseTimesOverTime"){
         choiceContainer = $("#choicesResponseTimesOverTime");
         refreshResponseTimeOverTime(false);
+    }else if(id == "choicesResponseCustomGraph"){
+        choiceContainer = $("#choicesResponseCustomGraph");
+        refreshCustomGraph(false);
     } else if ( id == "choicesLatenciesOverTime"){
         choiceContainer = $("#choicesLatenciesOverTime");
         refreshLatenciesOverTime(false);
@@ -1570,80 +1374,3 @@ function toggleAll(id, checked){
     });
 }
 
-// Unchecks all boxes for "Hide all samples" functionality
-function uncheckAll(id){
-    toggleAll(id, false);
-}
-
-// Checks all boxes for "Show all samples" functionality
-function checkAll(id){
-    toggleAll(id, true);
-}
-
-// Prepares data to be consumed by plot plugins
-function prepareData(series, choiceContainer, customizeSeries){
-    var datasets = [];
-
-    // Add only selected series to the data set
-    choiceContainer.find("input:checked").each(function (index, item) {
-        var key = $(item).attr("name");
-        var i = 0;
-        var size = series.length;
-        while(i < size && series[i].label != key)
-            i++;
-        if(i < size){
-            var currentSeries = series[i];
-            datasets.push(currentSeries);
-            if(customizeSeries)
-                customizeSeries(currentSeries);
-        }
-    });
-    return datasets;
-}
-
-/*
- * Ignore case comparator
- */
-function sortAlphaCaseless(a,b){
-    return a.toLowerCase() > b.toLowerCase() ? 1 : -1;
-};
-
-/*
- * Creates a legend in the specified element with graph information
- */
-function createLegend(choiceContainer, infos) {
-    // Sort series by name
-    var keys = [];
-    $.each(infos.data.result.series, function(index, series){
-        keys.push(series.label);
-    });
-    keys.sort(sortAlphaCaseless);
-
-    // Create list of series with support of activation/deactivation
-    $.each(keys, function(index, key) {
-        var id = choiceContainer.attr('id') + index;
-        $('<li />')
-            .append($('<input id="' + id + '" name="' + key + '" 
type="checkbox" checked="checked" hidden />'))
-            .append($('<label />', { 'text': key , 'for': id }))
-            .appendTo(choiceContainer);
-    });
-    choiceContainer.find("label").click( function(){
-        if (this.style.color !== "rgb(129, 129, 129)" ){
-            this.style.color="#818181";
-        }else {
-            this.style.color="black";
-        }
-        $(this).parent().children().children().toggleClass("legend-disabled");
-    });
-    choiceContainer.find("label").mousedown( function(event){
-        event.preventDefault();
-    });
-    choiceContainer.find("label").mouseenter(function(){
-        this.style.cursor="pointer";
-    });
-
-    // Recreate graphe on series activation toggle
-    choiceContainer.find("input").click(function(){
-        infos.createGraph();
-    });
-}

Added: jmeter/trunk/bin/report-template/content/pages/CustomsGraphs.html.fmkr
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/bin/report-template/content/pages/CustomsGraphs.html.fmkr?rev=1834399&view=auto
==============================================================================
--- jmeter/trunk/bin/report-template/content/pages/CustomsGraphs.html.fmkr 
(added)
+++ jmeter/trunk/bin/report-template/content/pages/CustomsGraphs.html.fmkr Tue 
Jun 26 07:30:08 2018
@@ -0,0 +1,248 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta name="description" content="">
+    <meta name="author" content="">
+
+    <title>${reportTitle!"Apache JMeter Dashboard"}</title>
+
+    <!-- Bootstrap Core CSS -->
+    <link 
href="../../sbadmin2-1.0.7/bower_components/bootstrap/dist/css/bootstrap.min.css"
 rel="stylesheet">
+
+    <!-- icone onglet -->
+    <link rel="icon" type="image/png" href="icon-apache.png" />
+
+    <!-- MetisMenu CSS -->
+    <link 
href="../../sbadmin2-1.0.7/bower_components/metisMenu/dist/metisMenu.min.css" 
rel="stylesheet">
+
+    <!-- Legends CSS -->
+    <link href="../css/legends.css" rel="stylesheet">
+
+
+    <!-- Custom CSS -->
+    <link href="../../sbadmin2-1.0.7/dist/css/sb-admin-2.css" rel="stylesheet">
+
+    <!-- Custom Fonts -->
+    <link 
href="../../sbadmin2-1.0.7/bower_components/font-awesome/css/font-awesome.min.css"
 rel="stylesheet" type="text/css">
+
+   <!-- JQuery UI style -->
+    <link href="../css/jquery-ui.css" rel="stylesheet">
+    <link href="../css/jquery-ui.structure.css" rel="stylesheet">
+    <link href="../css/jquery-ui.theme.css" rel="stylesheet">
+</head>
+
+<body>
+
+    <div id="wrapper">
+
+        <!-- Navigation -->
+        <nav class="navbar navbar-default navbar-static-top" role="navigation" 
style="margin-bottom: 0">
+            <div class="navbar-header">
+                <button type="button" class="navbar-toggle" 
data-toggle="collapse" data-target=".navbar-collapse">
+                    <span class="sr-only">Toggle navigation</span>
+                    <span class="icon-bar"></span>
+                    <span class="icon-bar"></span>
+                    <span class="icon-bar"></span>
+                </button>
+                <a class="navbar-brand" 
href="../../index.html">${reportTitle!"Apache JMeter Dashboard"}</a>
+            </div>
+            <!-- /.navbar-header -->
+
+
+            <div class="navbar-default sidebar" role="navigation">
+                <div class="sidebar-nav navbar-collapse">
+                    <ul class="nav" id="side-menu">
+
+                        <li>
+                            <a href="../../index.html"><i class="fa 
fa-dashboard fa-fw"></i> Dashboard</a>
+                        </li>
+                        <li>
+                            <a href="#"><i class="fa fa-bar-chart-o 
fa-fw"></i> Charts<span class="fa arrow"></span></a>
+                            <ul class="nav nav-second-level">
+                                <li>
+                                    <a href="OverTime.html">Over Time</a>
+
+                                </li>
+                                <li>
+                                    <a href="Throughput.html">Throughput</a>
+                                </li>
+                                <li>
+                                    <a href="ResponseTimes.html">Response 
Times</a>
+                                </li>
+                
+                            </ul>
+                            <!-- /.nav-second-level -->
+                        </li>
+                        
+                        <li>
+                            <a href="#"><i class="fa fa-bar-chart-o 
fa-fw"></i> Customs Graphs<span class="fa arrow"></span></a>
+                            <ul class="nav nav-second-level">
+                                <li>
+                                    <a href="CustomsGraphs.html">Over 
Time<span class="fa arrow"></span></a>
+                                    <ul class="nav nav-third-level in" 
id="submenu">
+                                        <#list customsGraphsData?keys as key>
+                                            <li>
+                                                <a 
href="CustomsGraphs.html#${key}" 
onclick="$('#bodyResponse${key}').collapse('show');">
+                                                <#assign title = 
""+graphConfigurations[key].getTitle()>
+                                                <#if title == "">Default Graph 
Title
+                                                <#else>${title}
+                                                </#if>
+                                                </a>
+                                            </li>
+                                        </#list>
+                                    </ul>
+                                <li>
+                            </ul>
+                        </li>
+
+                    </ul>
+                </div>
+                <!-- /.sidebar-collapse -->
+            </div>
+            <!-- /.navbar-static-side -->
+        </nav>
+
+        <div id="page-wrapper">
+            <div class="row">
+                <div class="col-lg-12">
+                     <div class="panel panel-default" >
+                        <div class="panel-heading" style="text-align:center;">
+                           <p class="dashboard-title">Test and Report 
informations</p>
+                        </div>
+                        <div class="panel-body">
+                            <table id="generalInfos" class="table 
table-bordered table-condensed " >
+                                <tr>
+                                    <td>File:</td>
+                                    <td>${testFile!""}</td>
+                                </tr>
+                                <tr>
+                                    <td>Start Time:</td>
+                                    <td>${beginDate!""}</td>
+                                </tr>
+                                <tr>
+                                    <td>End Time:</td>
+                                    <td>${endDate!""}</td>
+                                </tr>
+                                <#if overallFilter?has_content>
+                                    <tr>
+                                        <td>Filter for computing:</td>
+                                        <td>${overallFilter}</td>
+                                    </tr>
+                                </#if>
+                                <#if seriesFilter?has_content>
+                                    <tr>
+                                        <td>Filter for display:</td>
+                                        <td>${seriesFilter}</td>
+                                    </tr>
+                                </#if>
+                            </table>
+                        </div>
+                    </div>
+                </div>
+            </div>    
+            <!-- /.row -->
+                <div class="row" id="graphContainer">
+                
+                <#assign loopCount = 0>
+                <#list customsGraphsData?keys as key> 
+                    <div class="col-lg-12 portlet" id="${key!"Clé pas 
trouvé"}">
+                        <div class="panel panel-default">
+                            <div class="panel-heading portlet-header">
+                                 <i  class="fa fa-bar-chart-o fa-fw" ></i> 
+                                <span type="button" class="dropdown-toggle 
click-title span-title" data-toggle="collapse" href="#bodyResponse${key}" 
aria-expanded="true" aria-controls="body${key}"><#assign title = 
""+graphConfigurations[key].getTitle()><#if title == "">Default Graph 
Title<#else>${title}</#if></span>
+                                 <div class="pull-right">
+                                    <div class="btn-group">
+                                        <a class="btn btn-link btn-xs">
+                                            <i class="glyphicon 
glyphicon-resize-vertical"></i>
+                                        </a>
+                                        <button type="button" class="btn 
btn-link btn-xs dropdown-toggle" data-toggle="dropdown">
+                                            <i class="fa fa-wrench"></i>
+                                        </button>
+                                        <ul class="dropdown-menu 
dropdown-user">
+                                            <li><a href="#bodyResponse${key}" 
onClick="checkAll('choicesResponse${key}');">Display all samples</a>
+                                            </li>
+                                            <li><a href="#bodyResponse${key}" 
onClick="uncheckAll('choicesResponse${key}');">Hide all samples</a>
+                                            </li>
+                                            <li><a href="#bodyResponse${key}" 
onclick="exportToPNG('flotResponse${key}', this);">Save as PNG</a></li>
+                                        </ul>
+                                        <button type="button" class="btn 
btn-link btn-xs dropdown-toggle" data-toggle="collapse" 
href="#bodyResponse${key}" aria-expanded="true" 
aria-controls="bodyResponse${key}">
+                                            <i class="fa fa-chevron-up"></i>
+                                        </button>
+                                    </div>
+                                </div>
+                            </div>
+                            <!-- /.panel-heading -->
+                            <#if loopCount == 0>
+                            <div class="collapse in portlet-content" 
id="bodyResponse${key}">
+                            <#else>
+                            <div class="collapse out portlet-content" 
id="bodyResponse${key}">
+                            </#if>
+                                <div class="panel-body" 
id="collapseResponse${key}">
+                                    <div class="flot-chart">
+                                        <div class="flot-chart-content" 
id="flotResponse${key}" style="float: left; width:80%;"></div>
+                                        <div 
style="float:left;margin-left:5px">
+                                            <p>Zoom :</p>
+                                            <div id="overviewResponse${key}" 
style="width:190px;height:100px;"></div>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="panel-footer" 
id="footerResponse${key}">
+                                    <p id="legendResponse${key}" hidden></p>
+                                    <ul id="choicesResponse${key}" 
class="legend"></ul>
+                                </div>
+                            </div>    
+                        </div>
+                        <!-- /.panel-body -->
+                    </div>
+                    <!-- /.panel -->
+                    <#assign loopCount++>
+                </#list>
+                </div>
+            <!-- /.row -->
+        </div>
+        <!-- /#page-wrapper -->
+
+    </div>
+    <!-- /#wrapper -->
+
+     <!-- jQuery -->
+    <script 
src="../../sbadmin2-1.0.7/bower_components/jquery/dist/jquery.min.js"></script>
+
+    <!-- Bootstrap Core JavaScript -->
+    <script 
src="../../sbadmin2-1.0.7/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
+
+    <!-- Metis Menu Plugin JavaScript -->
+    <script 
src="../../sbadmin2-1.0.7/bower_components/metisMenu/dist/metisMenu.min.js"></script>
+
+    <!-- Flot Charts JavaScript -->
+    <script 
src="../../sbadmin2-1.0.7/bower_components/flot/excanvas.min.js"></script>
+    <script 
src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.js"></script>
+    <script 
src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.pie.js"></script>
+    <script 
src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.resize.js"></script>
+    <script 
src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.canvas.js"></script>
+    <script 
src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.navigate.js"></script>
+    <script 
src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.time.js"></script>
+    <script 
src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.selection.js"></script>
+    <script 
src="../../sbadmin2-1.0.7/bower_components/flot.tooltip/js/jquery.flot.tooltip.min.js"></script>
+    <script 
src="../../sbadmin2-1.0.7/bower_components/flot-axislabels/jquery.flot.axislabels.js"></script>
+    <script src="../js/hashtable.js"></script>
+    <script src="../js/jquery.numberformatter-1.2.3.min.js"></script>
+    <script src="../js/curvedLines.js"></script>
+    <script src="../js/dashboard-commons.js"></script>
+    <script src="../js/customGraph.js"></script>
+    <script src="../js/jquery-ui.js"></script>
+    <script 
src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.threshold.js"></script>
+    <!-- Custom Theme JavaScript -->
+    <script src="../../sbadmin2-1.0.7/dist/js/sb-admin-2.js"></script>
+    <script src="../js/jquery.cookie.js"></script>
+
+    <script 
src="../../sbadmin2-1.0.7/bower_components/flot/jquery.flot.symbol.js"></script>
+
+</body>
+
+</html>

Modified: jmeter/trunk/bin/report-template/content/pages/OverTime.html.fmkr
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/bin/report-template/content/pages/OverTime.html.fmkr?rev=1834399&r1=1834398&r2=1834399&view=diff
==============================================================================
--- jmeter/trunk/bin/report-template/content/pages/OverTime.html.fmkr (original)
+++ jmeter/trunk/bin/report-template/content/pages/OverTime.html.fmkr Tue Jun 
26 07:30:08 2018
@@ -109,6 +109,15 @@
                             <!-- /.nav-second-level -->
                         </li>
 
+                        <li>
+                            <a href="#"><i class="fa fa-bar-chart-o 
fa-fw"></i>Customs Graphs<span class="fa arrow"></span></a>
+                            <ul class="nav nav-second-level">
+                                <li>
+                                    <a href="CustomsGraphs.html">Over Time</a>
+                                </li>
+                            </ul>
+                        </li>
+
                     </ul>
                 </div>
                 <!-- /.sidebar-collapse -->

Modified: jmeter/trunk/bin/report-template/content/pages/ResponseTimes.html.fmkr
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/bin/report-template/content/pages/ResponseTimes.html.fmkr?rev=1834399&r1=1834398&r2=1834399&view=diff
==============================================================================
--- jmeter/trunk/bin/report-template/content/pages/ResponseTimes.html.fmkr 
(original)
+++ jmeter/trunk/bin/report-template/content/pages/ResponseTimes.html.fmkr Tue 
Jun 26 07:30:08 2018
@@ -99,7 +99,14 @@
                             </ul>
                             <!-- /.nav-second-level -->
                         </li>
-
+                        <li>
+                            <a href="#"><i class="fa fa-bar-chart-o 
fa-fw"></i> Customs Graphs<span class="fa arrow"></span></a>
+                            <ul class="nav nav-second-level">
+                                <li>
+                                    <a href="CustomsGraphs.html">Over Time</a>
+                                </li>
+                            </ul>
+                        </li>
                     </ul>
                 </div>
                 <!-- /.sidebar-collapse -->

Modified: jmeter/trunk/bin/report-template/content/pages/Throughput.html.fmkr
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/bin/report-template/content/pages/Throughput.html.fmkr?rev=1834399&r1=1834398&r2=1834399&view=diff
==============================================================================
--- jmeter/trunk/bin/report-template/content/pages/Throughput.html.fmkr 
(original)
+++ jmeter/trunk/bin/report-template/content/pages/Throughput.html.fmkr Tue Jun 
26 07:30:08 2018
@@ -98,6 +98,15 @@
                             <!-- /.nav-second-level -->
                         </li>
 
+                        <li>
+                            <a href="#"><i class="fa fa-bar-chart-o 
fa-fw"></i> Customs Graphs<span class="fa arrow"></span></a>
+                            <ul class="nav nav-second-level">
+                                <li>
+                                    <a href="CustomsGraphs.html">Over Time</a>
+                                </li>
+                            </ul>
+                        </li>
+
                     </ul>
                 </div>
                 <!-- /.sidebar-collapse -->

Modified: jmeter/trunk/bin/report-template/index.html.fmkr
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/bin/report-template/index.html.fmkr?rev=1834399&r1=1834398&r2=1834399&view=diff
==============================================================================
--- jmeter/trunk/bin/report-template/index.html.fmkr (original)
+++ jmeter/trunk/bin/report-template/index.html.fmkr Tue Jun 26 07:30:08 2018
@@ -66,6 +66,15 @@
                             </ul>
                             <!-- /.nav-second-level -->
                         </li>
+                        
+                        <li>
+                            <a href="#"><i class="fa fa-bar-chart-o 
fa-fw"></i> Customs Graphs<span class="fa arrow"></span></a>
+                            <ul class="nav nav-second-level">
+                                <li>
+                                    <a 
href="content/pages/CustomsGraphs.html">Over Time</a>
+                                </li>
+                            </ul>
+                        </li>
                     </ul>
                 </div>
             </div>

Modified: jmeter/trunk/bin/user.properties
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/bin/user.properties?rev=1834399&r1=1834398&r2=1834399&view=diff
==============================================================================
--- jmeter/trunk/bin/user.properties (original)
+++ jmeter/trunk/bin/user.properties Tue Jun 26 07:30:08 2018
@@ -102,6 +102,15 @@
 
#jmeter.reportgenerator.exported_transactions_pattern=[a-zA-Z0-9_\\-{}\\$\\.]*[-_][0-9]*
 
 
+## Custom graph definition
+#jmeter.reportgenerator.graph.custom_mm_hit.classname=org.apache.jmeter.report.processor.graph.impl.CustomGraphConsumer
+#jmeter.reportgenerator.graph.custom_mm_hit.title=Graph Title
+#jmeter.reportgenerator.graph.custom_mm_hit.property.set_Y_Axis=Response Time 
(ms)
+#jmeter.reportgenerator.graph.custom_mm_hit.property.set_X_Axis=Over Time
+#jmeter.reportgenerator.graph.custom_mm_hit.property.set_granularity=${jmeter.reportgenerator.overall_granularity}
+#jmeter.reportgenerator.graph.custom_mm_hit.property.setSampleVariableName=VarName
+#jmeter.reportgenerator.graph.custom_mm_hit.property.setContentMessage=Message 
for graph point label
+
 ########################################################################
 ################## DISTRIBUTED TESTING CONFIGURATION  ##################
 ########################################################################

Modified: 
jmeter/trunk/src/core/org/apache/jmeter/report/dashboard/HtmlTemplateExporter.java
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/report/dashboard/HtmlTemplateExporter.java?rev=1834399&r1=1834398&r2=1834399&view=diff
==============================================================================
--- 
jmeter/trunk/src/core/org/apache/jmeter/report/dashboard/HtmlTemplateExporter.java
 (original)
+++ 
jmeter/trunk/src/core/org/apache/jmeter/report/dashboard/HtmlTemplateExporter.java
 Tue Jun 26 07:30:08 2018
@@ -20,6 +20,7 @@ package org.apache.jmeter.report.dashboa
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.TimeZone;
 import java.util.regex.Pattern;
@@ -444,23 +445,31 @@ public class HtmlTemplateExporter extend
         EmptyGraphChecker checker = new EmptyGraphChecker(
                 filtersOnlySampleSeries, showControllerSeriesOnly,
                 filterPattern);
+        DataContext customGraphs = new DataContext();
+        Map<String, GraphConfiguration> mapConfiguration = new HashMap<>();
         for (Map.Entry<String, GraphConfiguration> graphEntry : configuration
                 .getGraphConfigurations().entrySet()) {
             final String graphId = graphEntry.getKey();
             final GraphConfiguration graphConfiguration = 
graphEntry.getValue();
-            final SubConfiguration extraOptions = exportCfg
-                    .getGraphExtraConfigurations().get(graphId);
+            final SubConfiguration extraOptions = 
exportCfg.getGraphExtraConfigurations().get(graphId);
 
             // Initialize customizer and checker
             customizer.setExtraOptions(extraOptions);
             checker.setExcludesControllers(
                     graphConfiguration.excludesControllers());
             checker.setGraphId(graphId);
-
-            // Export graph data
-            addResultToContext(graphId, storedData, dataContext, jsonizer,
+            mapConfiguration.put(graphId, graphConfiguration);
+            if(graphId.substring(0,7).equals("custom_")) {
+                addResultToContext(graphId, storedData, customGraphs, jsonizer,
+                        customizer, checker);
+            } else {
+                // Export graph data
+                addResultToContext(graphId, storedData, dataContext, jsonizer,
                     customizer, checker);
+            }
         }
+        dataContext.put("graphConfigurations", mapConfiguration);
+        dataContext.put("customsGraphsData", customGraphs);
 
         // Replace the begin date with its formatted string and store the old
         // timestamp

Modified: 
jmeter/trunk/src/core/org/apache/jmeter/report/dashboard/ReportGenerator.java
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/report/dashboard/ReportGenerator.java?rev=1834399&r1=1834398&r2=1834399&view=diff
==============================================================================
--- 
jmeter/trunk/src/core/org/apache/jmeter/report/dashboard/ReportGenerator.java 
(original)
+++ 
jmeter/trunk/src/core/org/apache/jmeter/report/dashboard/ReportGenerator.java 
Tue Jun 26 07:30:08 2018
@@ -235,10 +235,8 @@ public class ReportGenerator {
                 .getGraphConfigurations();
 
         // Process configuration to build graph consumers
-        for (Map.Entry<String, GraphConfiguration> entryGraphCfg : 
graphConfigurations
-                .entrySet()) {
-            addGraphConsumer(nameFilter, excludeControllerFilter,
-                    entryGraphCfg);
+        for (Map.Entry<String, GraphConfiguration> entryGraphCfg : 
graphConfigurations.entrySet()) {
+            addGraphConsumer(nameFilter, excludeControllerFilter, 
entryGraphCfg);
         }
 
         // Generate data
@@ -353,6 +351,7 @@ public class ReportGenerator {
                 setProperty(className, obj, methods, propertyName,
                         propertyValue, setterName);
             }
+            graph.initialize(); 
 
             // Choose which entry point to use to plug the graph
             AbstractSampleConsumer entryPoint = graphConfiguration
@@ -383,11 +382,9 @@ public class ReportGenerator {
         } catch (ClassNotFoundException | IllegalAccessException
                 | InstantiationException | ClassCastException ex) {
             String error = String.format(INVALID_CLASS_FMT, className);
-            log.error(error, ex);
             throw new GenerationException(error, ex);
         } catch (ExportException ex) {
             String error = String.format(INVALID_EXPORT_FMT, exporterName);
-            log.error(error, ex);
             throw new GenerationException(error, ex);
         }
     }
@@ -554,8 +551,7 @@ public class ReportGenerator {
                                                 parameterType
                                                         .getName()));
                             }
-                            method.invoke(obj, converter
-                                    .convert(propertyValue));
+                            method.invoke(obj, 
converter.convert(propertyValue));
                         }
                         return;
                     }

Modified: 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/AbstractGraphConsumer.java
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/AbstractGraphConsumer.java?rev=1834399&r1=1834398&r2=1834399&view=diff
==============================================================================
--- 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/AbstractGraphConsumer.java
 (original)
+++ 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/AbstractGraphConsumer.java
 Tue Jun 26 07:30:08 2018
@@ -90,10 +90,10 @@ public abstract class AbstractGraphConsu
     public static final String DEFAULT_AGGREGATED_KEYS_SERIES_FORMAT = 
"%s-Aggregated";
 
     /** The map used to store group information. */
-    private final HashMap<String, GroupInfo> groupInfos;
+    private HashMap<String, GroupInfo> groupInfos;
 
     /** The keys selector. */
-    private final GraphKeysSelector keysSelector;
+    private GraphKeysSelector keysSelector;
 
     /** The overall seriesData name. */
     private String overallSeriesFormat = DEFAULT_OVERALL_SERIES_FORMAT;
@@ -228,8 +228,6 @@ public abstract class AbstractGraphConsu
      * Instantiates a new abstract graph consumer.
      */
     protected AbstractGraphConsumer() {
-        keysSelector = createKeysSelector();
-        groupInfos = new HashMap<>(createGroupInfos());
     }
 
     protected abstract GraphKeysSelector createKeysSelector();
@@ -591,4 +589,8 @@ public abstract class AbstractGraphConsu
         }
     }
 
+    public void initialize() {
+        keysSelector = createKeysSelector();
+        groupInfos = new HashMap<>(createGroupInfos());
+    }
 }

Modified: 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/AbstractOverTimeGraphConsumer.java
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/AbstractOverTimeGraphConsumer.java?rev=1834399&r1=1834398&r2=1834399&view=diff
==============================================================================
--- 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/AbstractOverTimeGraphConsumer.java
 (original)
+++ 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/AbstractOverTimeGraphConsumer.java
 Tue Jun 26 07:30:08 2018
@@ -51,20 +51,13 @@ public abstract class AbstractOverTimeGr
      *            the granularity to set
      */
     public void setGranularity(long granularity) {
-        _setGranularity(granularity);
-    }
-
-    // Called from ctor
-    private final void _setGranularity(long granularity) {
         this.granularity = granularity;
-        ((TimeStampKeysSelector) 
getKeysSelector()).setGranularity(granularity);
     }
 
     /**
      * Instantiates a new abstract over time graph consumer.
      */
     protected AbstractOverTimeGraphConsumer() {
-        _setGranularity(1L);
     }
 
     /**
@@ -107,4 +100,10 @@ public abstract class AbstractOverTimeGr
         parentResult.setResult(RESULT_CTX_GRANULARITY, new ValueResultData(
                 Long.valueOf(granularity)));
     }
+    
+    @Override
+    public void initialize() {
+        super.initialize();
+        ((TimeStampKeysSelector) 
getKeysSelector()).setGranularity(granularity);
+    }
 }

Modified: 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/AbstractVersusRequestsGraphConsumer.java
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/AbstractVersusRequestsGraphConsumer.java?rev=1834399&r1=1834398&r2=1834399&view=diff
==============================================================================
--- 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/AbstractVersusRequestsGraphConsumer.java
 (original)
+++ 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/AbstractVersusRequestsGraphConsumer.java
 Tue Jun 26 07:30:08 2018
@@ -53,7 +53,7 @@ public abstract class AbstractVersusRequ
      * The embedded time count consumer is used to buffer (disk storage) and 
tag
      * samples with the number of samples in the same interval.
      */
-    private final TimeCountConsumer embeddedConsumer;
+    private TimeCountConsumer embeddedConsumer;
 
     /**
      * Gets the granularity.
@@ -79,8 +79,6 @@ public abstract class AbstractVersusRequ
      * Instantiates a new abstract over time graph consumer.
      */
     protected AbstractVersusRequestsGraphConsumer() {
-        embeddedConsumer = new TimeCountConsumer(this);
-        setGranularity(1L);
     }
 
     /*
@@ -95,6 +93,13 @@ public abstract class AbstractVersusRequ
         embeddedConsumer.startConsuming();
     }
 
+    @Override
+    public void initialize() {
+        super.initialize();
+        embeddedConsumer = new TimeCountConsumer(this);
+        setGranularity(1L);
+    }
+
     private void startConsumingBase() {
         super.startConsuming();
     }

Modified: 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/BytesThroughputGraphConsumer.java
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/BytesThroughputGraphConsumer.java?rev=1834399&r1=1834398&r2=1834399&view=diff
==============================================================================
--- 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/BytesThroughputGraphConsumer.java
 (original)
+++ 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/BytesThroughputGraphConsumer.java
 Tue Jun 26 07:30:08 2018
@@ -101,9 +101,14 @@ public class BytesThroughputGraphConsume
     @Override
     public void setGranularity(long granularity) {
         super.setGranularity(granularity);
+    }
+
+    @Override
+    public void initialize() {
+        super.initialize();
         // Override the granularity of the aggregators factory
         ((TimeRateAggregatorFactory) getGroupInfos().get(
                 AbstractGraphConsumer.DEFAULT_GROUP).getAggregatorFactory())
-                .setGranularity(granularity);
+                .setGranularity(getGranularity());
     }
 }

Modified: 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/CodesPerSecondGraphConsumer.java
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/CodesPerSecondGraphConsumer.java?rev=1834399&r1=1834398&r2=1834399&view=diff
==============================================================================
--- 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/CodesPerSecondGraphConsumer.java
 (original)
+++ 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/CodesPerSecondGraphConsumer.java
 Tue Jun 26 07:30:08 2018
@@ -66,6 +66,15 @@ public class CodesPerSecondGraphConsumer
         return groupInfos;
     }
 
+    @Override
+    public void initialize() {
+        super.initialize();
+        // Override the granularity of the aggregators factory
+        ((TimeRateAggregatorFactory) getGroupInfos().get(
+                AbstractGraphConsumer.DEFAULT_GROUP).getAggregatorFactory())
+                .setGranularity(getGranularity());
+    }
+
     /*
      * (non-Javadoc)
      * 
@@ -76,9 +85,5 @@ public class CodesPerSecondGraphConsumer
     @Override
     public void setGranularity(long granularity) {
         super.setGranularity(granularity);
-        // Override the granularity of the aggregators factory
-        ((TimeRateAggregatorFactory) getGroupInfos().get(
-                AbstractGraphConsumer.DEFAULT_GROUP).getAggregatorFactory())
-                .setGranularity(granularity);
     }
 }

Added: 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/CustomGraphConsumer.java
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/CustomGraphConsumer.java?rev=1834399&view=auto
==============================================================================
--- 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/CustomGraphConsumer.java
 (added)
+++ 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/CustomGraphConsumer.java
 Tue Jun 26 07:30:08 2018
@@ -0,0 +1,238 @@
+/*
+ * 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.report.processor.graph.impl;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.jmeter.report.core.ConvertException;
+import org.apache.jmeter.report.core.Converters;
+import org.apache.jmeter.report.core.Sample;
+import org.apache.jmeter.report.processor.MapResultData;
+import org.apache.jmeter.report.processor.SampleConsumer;
+import org.apache.jmeter.report.processor.TimeRateAggregatorFactory;
+import org.apache.jmeter.report.processor.ValueResultData;
+import org.apache.jmeter.report.processor.graph.AbstractGraphConsumer;
+import org.apache.jmeter.report.processor.graph.AbstractOverTimeGraphConsumer;
+import org.apache.jmeter.report.processor.graph.AbstractSeriesSelector;
+import org.apache.jmeter.report.processor.graph.GraphValueSelector;
+import org.apache.jmeter.report.processor.graph.GroupInfo;
+import org.apache.jmeter.report.processor.graph.TimeStampKeysSelector;
+
+/**
+ * The class CustomGraphConsumer is added by the custom Graphs plugin.
+ * It provides all the graphs the user defined in user.properties.
+ *
+ * @since 4.1
+ */
+
+public class CustomGraphConsumer extends AbstractOverTimeGraphConsumer 
implements SampleConsumer{
+    
+    public static final String RESULT_Y_AXIS = "Y_Axis"; //$NON-NLS-1$
+    public static final String RESULT_X_AXIS = "X_Axis"; //$NON-NLS-1$
+    public static final String RESULT_SAMPLE_VARIABLE_NAME = 
"sample_Metric_Name"; //$NON-NLS-1$
+    public static final String RESULT_CONTENT_MESSAGE = "content_Message"; 
//$NON-NLS-1$
+    public static final String REPORT_GENERATOR_PROPERTIES = 
"jmeter.reportgenerator.graph.customGraph.property"; //$NON-NLS-1$
+
+    private String yAxis;
+    private String xAxis;
+    private String contentMessage;
+    private String sampleVariableName;
+    private boolean isNativeSampleVariableName = false;
+    
+    /**
+     * Only used for junit tests.
+     * Indicates if the sampleVariableName 
+     * is native
+     * 
+     * @return the nativeSampleVariableName
+     */
+    public boolean getIsNativeSampleVariableName() {
+        return isNativeSampleVariableName;
+    }
+
+    /**
+     * Gets the Y Axis.
+     *
+     * @return the yAxis
+     */
+    public String getYAxis() {
+        return yAxis;
+    }
+    
+    /**
+     * Gets the X Axis.
+     *
+     * @return the xAxis
+     */
+    public String getXAxis() {
+        return xAxis;
+    }
+
+    /**
+     * Sets the yAxis.
+     *
+     * @param axis
+     * the yAxis to set
+     */
+    public void setYAxis(String axis) {
+        yAxis=axis;
+    }
+    
+    /**
+     * Sets the xAxis.
+     *
+     * @param axis
+     * the xAxis to set
+     */
+    public void setXAxis(String axis) {
+        xAxis=axis;
+    }
+    
+    /**
+     * Sets the contentMessage.
+     *
+     * @param message
+     * the message to set
+     */
+    public void setContentMessage(String message) {
+        contentMessage=message;
+    }
+    
+    /**
+     * Gets the content message.
+     *
+     * @return the contentMessage
+     */
+    public String getContentMessage() {
+        return contentMessage;
+    }
+    
+    /**
+     * Gets the sampleVariableName.
+     *
+     * @return the sampleVariableName
+     */
+    public String getSampleVariableName() {
+        return sampleVariableName;
+    }
+    
+    /**
+     * Sets the sampleVariableName.
+     * Sets the boolean isNativesSampleVariableName
+     *
+     * @param sampleVarName
+     * the sampleVariableName to set
+     */
+    public void setSampleVariableName(String sampleVarName) {
+        sampleVariableName = sampleVarName;
+        // this if contains every native sample variables names
+        if(sampleVarName.equals("timeStamp") || 
sampleVarName.equals("elapsed") 
+                || sampleVarName.equals("label") || 
sampleVarName.equals("responseCode") 
+                || sampleVarName.equals("threadName") || 
sampleVarName.equals("success") 
+                || sampleVarName.equals("failureMessage") || 
sampleVarName.equals("bytes") 
+                || sampleVarName.equals("sentBytes") || 
sampleVarName.equals("grpThreads") 
+                || sampleVarName.equals("allThreads") || 
sampleVarName.equals("URL") 
+                || sampleVarName.equals("Latency") || 
sampleVarName.equals("IdleTime") 
+                || sampleVarName.equals("Connect")) {
+            isNativeSampleVariableName = true;
+        }else {
+            isNativeSampleVariableName = false;
+        }
+    }
+    
+        
+    @Override
+    protected void initializeExtraResults(MapResultData parentResult) {
+        parentResult.setResult(RESULT_CTX_GRANULARITY, new 
ValueResultData(Long.valueOf(getGranularity())));
+        parentResult.setResult(RESULT_Y_AXIS, new ValueResultData(getYAxis()));
+        parentResult.setResult(RESULT_X_AXIS, new ValueResultData(getXAxis()));
+        parentResult.setResult(RESULT_SAMPLE_VARIABLE_NAME, new 
ValueResultData(getSampleVariableName()));
+        parentResult.setResult(RESULT_CONTENT_MESSAGE, new 
ValueResultData(getContentMessage()));
+    }
+    
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * 
org.apache.jmeter.report.csv.processor.impl.AbstractOverTimeGraphConsumer
+     * #createTimeStampKeysSelector()
+     */
+    @Override
+    protected TimeStampKeysSelector createTimeStampKeysSelector() {
+        TimeStampKeysSelector keysSelector = new TimeStampKeysSelector();
+        keysSelector.setSelectBeginTime(false);
+        return keysSelector;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.jmeter.report.csv.processor.impl.AbstractGraphConsumer#
+     * createGroupInfos()
+     */
+    @Override
+    protected Map<String, GroupInfo> createGroupInfos() {
+        
+        HashMap<String, GroupInfo> groupInfos = new HashMap<>(); 
+        groupInfos.put(AbstractGraphConsumer.DEFAULT_GROUP,
+                new GroupInfo(
+                new TimeRateAggregatorFactory(), 
+                new AbstractSeriesSelector() {
+                    private final Iterable<String> values = 
Arrays.asList(sampleVariableName);
+
+                  @Override
+                  public Iterable<String> select(Sample sample) {
+                      return values;
+                  }
+                },
+                // We ignore Transaction Controller results
+                new GraphValueSelector() {
+                  @Override
+                  public Double select(String series, Sample sample) {
+                      String value="";
+                      if(isNativeSampleVariableName) {
+                          value = sample.getData(sampleVariableName);
+                      }else {
+                          value = sample.getData("\""+sampleVariableName+"\"");
+                      }
+                      if(StringUtils.isEmpty(value) || value.equals("null")) {
+                          return null;
+                      }
+                      else {
+                          try {
+                            return Converters.convert(Double.class, value);
+                        } catch (ConvertException e) {
+                            throw new IllegalArgumentException("Double 
converter failed : {}",e);
+                        }
+                      }
+              }}, false, false));
+        return groupInfos;
+    }
+    
+    @Override
+    public void initialize() {
+        super.initialize();
+        // Override the granularity of the aggregators factory
+        ((TimeRateAggregatorFactory) getGroupInfos().get(
+                AbstractGraphConsumer.DEFAULT_GROUP).getAggregatorFactory())
+                .setGranularity(getGranularity());
+    }
+}

Propchange: 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/CustomGraphConsumer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/CustomGraphConsumer.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/HitsPerSecondGraphConsumer.java
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/HitsPerSecondGraphConsumer.java?rev=1834399&r1=1834398&r2=1834399&view=diff
==============================================================================
--- 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/HitsPerSecondGraphConsumer.java
 (original)
+++ 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/HitsPerSecondGraphConsumer.java
 Tue Jun 26 07:30:08 2018
@@ -76,10 +76,20 @@ public class HitsPerSecondGraphConsumer
     @Override
     public void setGranularity(long granularity) {
         super.setGranularity(granularity);
+        
+    }
+
+    @Override
+    public void initialize() {
+        super.initialize();
         // Override the granularity of the aggregators factory
         ((TimeRateAggregatorFactory) getGroupInfos().get(
                 AbstractGraphConsumer.DEFAULT_GROUP).getAggregatorFactory())
-                .setGranularity(granularity);
+                .setGranularity(getGranularity());
+        // Override the series name with the name of the graph
+        ((StaticSeriesSelector) getGroupInfos().get(
+                AbstractGraphConsumer.DEFAULT_GROUP).getSeriesSelector())
+                .setSeriesName(getName());
     }
 
     /*
@@ -92,9 +102,5 @@ public class HitsPerSecondGraphConsumer
     @Override
     public void setName(String name) {
         super.setName(name);
-        // Override the series name with the name of the graph
-        ((StaticSeriesSelector) getGroupInfos().get(
-                AbstractGraphConsumer.DEFAULT_GROUP).getSeriesSelector())
-                .setSeriesName(name);
     }
 }

Added: 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/ResponseCustomGraphGraphConsumer.java
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/ResponseCustomGraphGraphConsumer.java?rev=1834399&view=auto
==============================================================================
--- 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/ResponseCustomGraphGraphConsumer.java
 (added)
+++ 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/ResponseCustomGraphGraphConsumer.java
 Tue Jun 26 07:30:08 2018
@@ -0,0 +1,69 @@
+/*
+ * 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.report.processor.graph.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.jmeter.report.processor.MeanAggregatorFactory;
+import org.apache.jmeter.report.processor.graph.AbstractGraphConsumer;
+import org.apache.jmeter.report.processor.graph.AbstractOverTimeGraphConsumer;
+import org.apache.jmeter.report.processor.graph.ElapsedTimeValueSelector;
+import org.apache.jmeter.report.processor.graph.GroupInfo;
+import org.apache.jmeter.report.processor.graph.NameSeriesSelector;
+import org.apache.jmeter.report.processor.graph.TimeStampKeysSelector;
+
+/**
+ * The class ResponseCustomGraphGraphConsumer provides a graph to visualize 
mean 
+ * custom value per time period (defined by granularity)
+ *
+ * @since 4.1
+ */
+public class ResponseCustomGraphGraphConsumer extends
+        AbstractOverTimeGraphConsumer {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * 
org.apache.jmeter.report.csv.processor.impl.AbstractOverTimeGraphConsumer
+     * #createTimeStampKeysSelector()
+     */
+    @Override
+    protected TimeStampKeysSelector createTimeStampKeysSelector() {
+        TimeStampKeysSelector keysSelector = new TimeStampKeysSelector();
+        keysSelector.setSelectBeginTime(false);
+        return keysSelector;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.jmeter.report.csv.processor.impl.AbstractGraphConsumer#
+     * createGroupInfos()
+     */
+    @Override
+    protected Map<String, GroupInfo> createGroupInfos() {
+        HashMap<String, GroupInfo> groupInfos = new HashMap<>(1);
+        groupInfos.put(AbstractGraphConsumer.DEFAULT_GROUP, new GroupInfo(
+                new MeanAggregatorFactory(), new NameSeriesSelector(),
+                // We include Transaction Controller results
+                new ElapsedTimeValueSelector(false), false, false));
+        return groupInfos;
+    }
+}

Propchange: 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/ResponseCustomGraphGraphConsumer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/ResponseCustomGraphGraphConsumer.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/ResponseTimePerSampleGraphConsumer.java
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/ResponseTimePerSampleGraphConsumer.java?rev=1834399&r1=1834398&r2=1834399&view=diff
==============================================================================
--- 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/ResponseTimePerSampleGraphConsumer.java
 (original)
+++ 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/ResponseTimePerSampleGraphConsumer.java
 Tue Jun 26 07:30:08 2018
@@ -47,6 +47,11 @@ public class ResponseTimePerSampleGraphC
      * Instantiates a new response time per sample graph consumer.
      */
     public ResponseTimePerSampleGraphConsumer() {
+    }
+
+    @Override
+    public void initialize() {
+        super.initialize();
         setRevertKeysAndValues(true);
     }
 

Modified: 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/ResponseTimePercentilesGraphConsumer.java
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/ResponseTimePercentilesGraphConsumer.java?rev=1834399&r1=1834398&r2=1834399&view=diff
==============================================================================
--- 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/ResponseTimePercentilesGraphConsumer.java
 (original)
+++ 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/ResponseTimePercentilesGraphConsumer.java
 Tue Jun 26 07:30:08 2018
@@ -41,6 +41,11 @@ public class ResponseTimePercentilesGrap
      * Instantiates a new response time percentiles graph consumer.
      */
     public ResponseTimePercentilesGraphConsumer() {
+    }
+
+    @Override
+    public void initialize() {
+        super.initialize();
         setRenderPercentiles(true);
     }
 

Modified: 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/TotalTPSGraphConsumer.java
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/TotalTPSGraphConsumer.java?rev=1834399&r1=1834398&r2=1834399&view=diff
==============================================================================
--- 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/TotalTPSGraphConsumer.java
 (original)
+++ 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/TotalTPSGraphConsumer.java
 Tue Jun 26 07:30:08 2018
@@ -91,9 +91,6 @@ public class TotalTPSGraphConsumer exten
     @Override
     public void setGranularity(long granularity) {
         super.setGranularity(granularity);
-        // Override the granularity of the aggregators factory
-        ((TimeRateAggregatorFactory) 
getGroupInfos().get(AbstractGraphConsumer.DEFAULT_GROUP).getAggregatorFactory())
-                .setGranularity(granularity);
     }
     
     @Override
@@ -105,6 +102,14 @@ public class TotalTPSGraphConsumer exten
         initializeSeries(parentResult, seriesLabels);
     }
     
+    @Override
+    public void initialize() {
+        super.initialize();
+        // Override the granularity of the aggregators factory
+        ((TimeRateAggregatorFactory) 
getGroupInfos().get(AbstractGraphConsumer.DEFAULT_GROUP).getAggregatorFactory())
+        .setGranularity(getGranularity());
+    }
+    
 
     private void initializeSeries(MapResultData parentResult, String[] series) 
{
         ListResultData listResultData = (ListResultData) 
parentResult.getResult("series");

Modified: 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/TransactionsPerSecondGraphConsumer.java
URL: 
http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/TransactionsPerSecondGraphConsumer.java?rev=1834399&r1=1834398&r2=1834399&view=diff
==============================================================================
--- 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/TransactionsPerSecondGraphConsumer.java
 (original)
+++ 
jmeter/trunk/src/core/org/apache/jmeter/report/processor/graph/impl/TransactionsPerSecondGraphConsumer.java
 Tue Jun 26 07:30:08 2018
@@ -84,6 +84,15 @@ public class TransactionsPerSecondGraphC
         return groupInfos;
     }
 
+    @Override
+    public void initialize() {
+        super.initialize();
+        // Override the granularity of the aggregators factory
+        ((TimeRateAggregatorFactory) getGroupInfos().get(
+                AbstractGraphConsumer.DEFAULT_GROUP).getAggregatorFactory())
+                .setGranularity(getGranularity());
+    }
+
     /*
      * (non-Javadoc)
      * 
@@ -94,10 +103,6 @@ public class TransactionsPerSecondGraphC
     @Override
     public void setGranularity(long granularity) {
         super.setGranularity(granularity);
-        // Override the granularity of the aggregators factory
-        ((TimeRateAggregatorFactory) getGroupInfos().get(
-                AbstractGraphConsumer.DEFAULT_GROUP).getAggregatorFactory())
-                .setGranularity(granularity);
     }
 
 }


Reply via email to