com.artnaseef.jmeter.report.ResultCodesPerSecondReport.java Source code

Java tutorial

Introduction

Here is the source code for com.artnaseef.jmeter.report.ResultCodesPerSecondReport.java

Source

/*
 * Licensed 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 com.artnaseef.jmeter.report;

import com.artnaseef.jmeter.report.cli.ReportLauncher;
import com.artnaseef.jmeter.report.jtl.model.Sample;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.util.ExportUtils;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

import java.io.File;
import java.io.PrintStream;
import java.util.*;

import joptsimple.OptionParser;

/**
 * Created by art on 4/7/15.
 */
public class ResultCodesPerSecondReport implements FeedableReport {

    private OptionParser optionParser;

    private String outputFile = "resultCodesPerSecond.png";
    private String detailOutputFile;

    private int reportWidth = 1000;
    private int reportHeight = 750;

    private XYSeriesCollection dataset;
    private JFreeChart chart;
    private List<XYSeries> chartSeries;
    private Map<Integer, Map<Long, Long>> samplesByReportCode;

    private double secPerSample;
    private String yAxisLabel = "Seconds";

    private long timeSlotSize = 1000; // In milliseconds

    private long startTimestampSlot = -1;
    private long endTimestampSlot = -1;

    private PrintStream detailFileWriter;

    private String feedUri;

    public static void main(String[] args) {
        ResultCodesPerSecondReport mainObj = new ResultCodesPerSecondReport();

        try {
            ReportLauncher launcher = new ReportLauncher();
            launcher.launchReport(mainObj, args);
        } catch (Exception exc) {
            exc.printStackTrace();
        }
    }

    @Override
    public void onSample(Sample topLevelSample) throws Exception {
        List<Sample> subSamples = topLevelSample.getSubSamples();
        if ((subSamples != null) && (!subSamples.isEmpty())) {
            for (Sample oneSub : subSamples) {
                this.onSample(oneSub);
            }
        } else {
            this.addConcreteSample(topLevelSample);
        }
    }

    @Override
    public void onFeedStart(String uri, Properties reportProperties) throws Exception {
        this.feedUri = uri;

        this.extractReportProperties(reportProperties);

        this.chartSeries = new LinkedList<>();
        this.samplesByReportCode = new TreeMap<>();
        this.dataset = new XYSeriesCollection();

        if (this.detailOutputFile != null) {
            this.detailFileWriter = new PrintStream(this.detailOutputFile);
        }
    }

    @Override
    public void onFeedComplete() throws Exception {
        this.calculateTimeAdjustments();

        this.populateSeries(this.feedUri);

        for (XYSeries oneSeries : this.chartSeries) {
            this.dataset.addSeries(oneSeries);
        }

        this.createChart();

        ExportUtils.writeAsPNG(this.chart, this.reportWidth, this.reportHeight, new File(this.outputFile));
    }

    protected void extractReportProperties(Properties prop) {
        this.detailOutputFile = prop.getProperty(ReportLauncher.PROPERTY_DETAIL_FILE_NAME);

        String out = prop.getProperty(ReportLauncher.PROPERTY_OUTPUT_FILENAME);
        if (out != null) {
            this.outputFile = out;
        }

        Integer size;
        size = (Integer) prop.get(ReportLauncher.PROPERTY_CHART_HEIGHT);
        if (size != null) {
            this.reportHeight = size;
        }
        size = (Integer) prop.get(ReportLauncher.PROPERTY_CHART_WIDTH);
        if (size != null) {
            this.reportWidth = size;
        }

        Long slotSize = (Long) prop.get(ReportLauncher.PROPERTY_TIME_SLOT_SIZE);
        if (slotSize != null) {
            this.timeSlotSize = slotSize;
        }
    }

    protected void calculateTimeAdjustments() {
        this.secPerSample = (double) this.timeSlotSize / 1000.0;

        if (Math.abs(this.secPerSample - 1.0) < 0.1) {
            this.yAxisLabel = "Second";
        } else {
            this.yAxisLabel = String.format("%01.1f Second", secPerSample);
        }
    }

    protected void populateSeries(String sourceUri) {
        for (Map.Entry<Integer, Map<Long, Long>> entry : this.samplesByReportCode.entrySet()) {
            XYSeries rcSeries = new XYSeries(Integer.toString(entry.getKey()));
            this.chartSeries.add(rcSeries);

            for (Map.Entry<Long, Long> hitCountSeconds : entry.getValue().entrySet()) {
                long xPoint = this.calculateXAxisOffset(hitCountSeconds.getKey());
                double yPoint = (double) hitCountSeconds.getValue() / this.secPerSample;

                rcSeries.add(xPoint, yPoint);

                if (this.detailFileWriter != null) {
                    this.detailFileWriter.println(String.format("%s|%d|%d|%d|%f", sourceUri,
                            hitCountSeconds.getKey(), hitCountSeconds.getValue(), xPoint, yPoint));
                }
            }
        }
    }

    protected void createChart() {
        // create the chart...
        this.chart = ChartFactory.createXYLineChart("Result Codes per " + this.yAxisLabel, // chart title
                this.yAxisLabel, // x axis label
                "Hits", // y axis label
                dataset, // data
                PlotOrientation.VERTICAL, true, // include legend
                true, // tooltips
                false // urls
        );
    }

    protected void addConcreteSample(Sample oneSample) {
        Map<Long, Long> slotSamples = this.samplesByReportCode.get(oneSample.getResultCode());

        if (slotSamples == null) {
            slotSamples = new TreeMap<>();
            this.samplesByReportCode.put(oneSample.getResultCode(), slotSamples);
        }

        long newCount = 1;
        long timeStampSlot = normalizeTimestamp(oneSample.getTimestamp());
        Long existingCount = slotSamples.get(timeStampSlot);

        if (existingCount != null) {
            newCount += existingCount;
        }

        slotSamples.put(timeStampSlot, newCount);

        if ((this.startTimestampSlot == -1) || (timeStampSlot < this.startTimestampSlot)) {
            this.startTimestampSlot = timeStampSlot;
        }

        if ((this.endTimestampSlot == -1) || (timeStampSlot > this.endTimestampSlot)) {
            this.endTimestampSlot = timeStampSlot;
        }
    }

    protected long normalizeTimestamp(long timestamp) {
        return timestamp / this.timeSlotSize;
    }

    protected long calculateXAxisOffset(long timestampSlot) {
        long result = timestampSlot - this.startTimestampSlot;

        return result;
    }
}