tools.descartes.bungee.chart.ChartGenerator.java Source code

Java tutorial

Introduction

Here is the source code for tools.descartes.bungee.chart.ChartGenerator.java

Source

/*******************************************************************************
* Copyright (c) 2014 Andreas Weber, Nikolas Herbst
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/

package tools.descartes.bungee.chart;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.SeriesRenderingOrder;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StackedXYBarRenderer;
import org.jfree.chart.renderer.xy.StandardXYBarPainter;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.renderer.xy.XYStepRenderer;
import org.jfree.chart.util.RelativeDateFormat;
import org.jfree.data.time.FixedMillisecond;
import org.jfree.data.time.SimpleTimePeriod;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.time.TimeTableXYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

import tools.descartes.bungee.allocation.AllocationSeries;
import tools.descartes.bungee.allocation.DemandSupplyContainer;
import tools.descartes.bungee.allocation.ResourceAllocation;
import tools.descartes.bungee.allocation.SupplySeries;
import tools.descartes.bungee.analysis.IntensityDemandMapping;
import tools.descartes.bungee.analysis.IntensityDemandMapping.IntensityResourcePair;
import tools.descartes.bungee.loadgeneration.AbstractResponse;
import tools.descartes.bungee.loadgeneration.JMeterResponse;
import tools.descartes.bungee.loadgeneration.RunResult;
import tools.descartes.bungee.utils.DateUtility;
import tools.descartes.dlim.generator.ArrivalRateTuple;

public class ChartGenerator {

    private static final Color VARIANT_A_COLOR = new Color(34, 139, 34);
    private static final Color VARIANT_B_COLOR = new Color(255, 140, 0);
    private static final Color VARIANT_C_COLOR = Color.RED;
    private static final Color VARIANT_D_COLOR = Color.MAGENTA;
    private static final int STROKE_WIDTH = 1;
    private static Date referenceDate = new Date(0);

    public static void showChart(JFreeChart chart, String title) {
        final ChartFrame frame = new ChartFrame(title, chart);
        frame.setVisible(true);
    }

    public static JFreeChart allocationChart(DemandSupplyContainer container, boolean splitUp) {
        if (splitUp) {
            List<XYPlot> plots = allocationPlots(container);
            return createTimeSeriesChart(plots);
        } else {
            return createTimeSeriesChart(allocationPlot(container));
        }
    }

    public static List<XYPlot> allocationPlots(DemandSupplyContainer container) {
        List<XYPlot> plots = new LinkedList<XYPlot>();
        plots.add(allocationPlot(container.getDemand()));
        for (SupplySeries supply : container.getAllSupplies()) {
            plots.add(allocationPlot(supply));
        }
        return plots;
    }

    public static JFreeChart allocationChart(DemandSupplyContainer container,
            final List<ArrivalRateTuple> intensities, boolean splitUp) {
        List<XYPlot> plots = new LinkedList<XYPlot>();
        plots.add(createIntensityPlot(intensities));
        if (splitUp) {
            plots.add(allocationPlot(container.getDemand()));
            for (SupplySeries supply : container.getAllSupplies()) {
                plots.add(allocationPlot(supply));
            }
        } else {
            plots.add(allocationPlot(container));
        }
        return createTimeSeriesChart(plots);
    }

    public static XYPlot allocationPlot(AllocationSeries series) {
        final TimeSeriesCollection dataset = new TimeSeriesCollection();
        dataset.addSeries(createTimeSeries(series));
        return createAllocationPlot(dataset);
    }

    public static XYPlot allocationPlot(SupplySeries series) {
        final TimeSeriesCollection dataset = new TimeSeriesCollection();
        dataset.addSeries(createTimeSeries(series));
        return createAllocationPlot(dataset);
    }

    public static XYPlot allocationPlot(DemandSupplyContainer container) {
        final TimeSeriesCollection dataset = new TimeSeriesCollection();
        dataset.addSeries(createTimeSeries(container.getDemand()));
        for (SupplySeries supplySeries : container.getAllSupplies()) {
            if (container.getAllSupplies().size() == 1 || (supplySeries.getType() != SupplySeries.TYPE.VM_COMPLETED
                    && supplySeries.getType() != SupplySeries.TYPE.VM_SCHEDULED
                    && supplySeries.getType() != SupplySeries.TYPE.MONITORED)) {
                dataset.addSeries(createTimeSeries(supplySeries));
            }

        }
        return createAllocationPlot(dataset);
    }

    public static JFreeChart allocationChart(AllocationSeries allocations) {
        final TimeSeriesCollection dataset = new TimeSeriesCollection();
        final TimeSeries series = createTimeSeries(allocations);
        dataset.addSeries(series);
        return createTimeSeriesChart(createAllocationPlot(dataset));
    }

    public static JFreeChart allocationChart(List<ResourceAllocation> allocations) {
        final TimeSeriesCollection dataset = new TimeSeriesCollection();
        final TimeSeries series = createTimeSeries(allocations, "Resource Allocation");
        dataset.addSeries(series);
        return createTimeSeriesChart(createAllocationPlot(dataset));
    }

    private static void addResponseTimeToDataset(TimeTableXYDataset dataset, double granularityInSeconds,
            long windowStart, long summedResponseTimes, long summedWorkTimes, long numberOfElements, String name) {
        SimpleTimePeriod timePeriod = new SimpleTimePeriod(new Date(windowStart),
                new Date((long) (windowStart + granularityInSeconds * 1000)));
        long overhead = (summedResponseTimes - summedWorkTimes) / numberOfElements;
        long workTime = summedWorkTimes / numberOfElements;
        dataset.add(timePeriod, overhead, "waiting time");
        dataset.add(timePeriod, workTime, "service time");
    }

    public static JFreeChart responseTimeChart(RunResult result) {
        XYPlot plot = createResponseTimePlot(result, "");
        return createTimeSeriesChart(plot);
    }

    public static JFreeChart responseTimeChart(RunResult result, double granulatrityInSeconds) {
        XYPlot plot = createResponseTimePlot(result, "", granulatrityInSeconds);
        return createTimeSeriesChart(plot);
    }

    public static XYPlot createResponseTimePlot(RunResult result, String name, double granularityInSeconds) {
        TimeTableXYDataset dataset = new TimeTableXYDataset();

        if (granularityInSeconds < 0.001) {
            granularityInSeconds = 0;
        }

        long delta = result.getResponses().get(0).getRequestSubmitTime();
        long firstRequest = 0;
        double windowStart = firstRequest;
        long summedResponseTimes = 0;
        long summedWorkTimes = 0;
        long numberOfElements = 0;

        for (JMeterResponse response : result.getResponses()) {
            long requestSubmitTime = response.getRequestSubmitTime() - delta;
            if (requestSubmitTime - windowStart > granularityInSeconds * 1000) {
                if (numberOfElements > 0) {
                    addResponseTimeToDataset(dataset, granularityInSeconds, (long) windowStart, summedResponseTimes,
                            summedWorkTimes, numberOfElements, name);
                    numberOfElements = 0;
                    summedResponseTimes = 0;
                    summedWorkTimes = 0;
                    firstRequest = requestSubmitTime;
                }
                if (granularityInSeconds > 0) {
                    while (requestSubmitTime - windowStart > granularityInSeconds * 1000) {
                        windowStart += granularityInSeconds * 1000;
                    }
                } else {
                    windowStart = requestSubmitTime;
                }
            }
            numberOfElements++;
            summedResponseTimes += response.getResponseTime();
            summedWorkTimes += response.getRequestServiceTime();
        }
        if (numberOfElements > 0) {
            addResponseTimeToDataset(dataset, granularityInSeconds, (long) windowStart, summedResponseTimes,
                    summedWorkTimes, numberOfElements, name);
        }

        NumberAxis rangeAxis = new NumberAxis("Resp.Time [ms]");
        rangeAxis.setRange(0, 1100);
        StackedXYBarRenderer renderer = new StackedXYBarRenderer(0.10);
        renderer.setShadowVisible(false);
        renderer.setBarPainter(new StandardXYBarPainter());
        renderer.setSeriesPaint(0, Color.GRAY);
        renderer.setSeriesPaint(1, colorForConfig(name));
        XYPlot plot = new XYPlot(dataset, null, rangeAxis, renderer);
        return plot;
    }

    public static XYPlot createResponseTimePlot(RunResult result, String name) {
        return createResponseTimePlot(result, name, 1);
    }

    public static JFreeChart scheduleChart(RunResult result) {
        return scheduleChart(result, 1);
    }

    public static JFreeChart scheduleChart(RunResult result, double granularityInSeconds) {
        XYPlot plot = createSchedulePlot(result, granularityInSeconds);
        return createTimeSeriesChart(plot);
    }

    private static XYPlot createSchedulePlot(RunResult result, double granularityInSeconds) {
        TimeTableXYDataset dataset = new TimeTableXYDataset();

        if (granularityInSeconds < 0.001) {
            granularityInSeconds = 0;
        }

        long delta = result.getResponses().get(0).getRequestSubmitTime();
        long firstRequest = 0;
        double windowStart = firstRequest;
        long summedDiffs = 0;
        long numberOfElements = 0;
        Iterator<JMeterResponse> responseIterator = result.getResponses().iterator();
        Iterator<Long> diffIterator = result.getTimestampDiffs().iterator();
        while (responseIterator.hasNext() && diffIterator.hasNext()) {
            AbstractResponse response = responseIterator.next();
            Long diff = diffIterator.next();
            long requestSubmitTime = response.getRequestSubmitTime() - delta;
            if (requestSubmitTime - windowStart > granularityInSeconds * 1000) {
                if (numberOfElements > 0) {
                    addDiffToDataset(granularityInSeconds, dataset, windowStart, summedDiffs, numberOfElements);
                    numberOfElements = 0;
                    summedDiffs = 0;
                    firstRequest = requestSubmitTime;
                }
                if (granularityInSeconds > 0) {
                    while (requestSubmitTime - windowStart > granularityInSeconds * 1000) {
                        windowStart += granularityInSeconds * 1000;
                    }
                } else {
                    windowStart = requestSubmitTime;
                }
            }
            numberOfElements++;
            summedDiffs += diff;
        }
        if (numberOfElements > 0) {
            addDiffToDataset(granularityInSeconds, dataset, windowStart, summedDiffs, numberOfElements);
        }
        NumberAxis rangeAxis = new NumberAxis("Request Delay [ms]");
        StackedXYBarRenderer renderer = new StackedXYBarRenderer(0.10);
        renderer.setShadowVisible(false);
        renderer.setBarPainter(new StandardXYBarPainter());
        XYPlot plot = new XYPlot(dataset, null, rangeAxis, renderer);
        return plot;
    }

    private static void addDiffToDataset(double granularityInSeconds, TimeTableXYDataset dataset,
            double windowStart, long summedDiffs, long numberOfElements) {
        long diffAverage = summedDiffs / numberOfElements;
        SimpleTimePeriod timePeriod = new SimpleTimePeriod(new Date((long) windowStart),
                new Date((long) (windowStart + granularityInSeconds * 1000)));
        dataset.add(timePeriod, diffAverage, "Request Submission Delay");
    }

    private static TimeSeries createTimeSeries(AllocationSeries series) {
        return createTimeSeries(series.getAllocations(), series.getCategory().toString());
    }

    private static TimeSeries createTimeSeries(SupplySeries series) {
        String name = series.getName();
        return createTimeSeries(series.getAllocations(), name);
    }

    private static TimeSeries createTimeSeries(List<ResourceAllocation> allocations, String label) {
        Integer lastAmount = null;
        final TimeSeries series = new TimeSeries(label);
        for (ResourceAllocation allocation : allocations) {
            if (lastAmount != null) {
                series.addOrUpdate(
                        new FixedMillisecond(
                                new Date(DateUtility.toSecondPrecision(allocation.getDate()).getTime() - 1)),
                        lastAmount);
            }
            series.addOrUpdate(new FixedMillisecond(DateUtility.toSecondPrecision(allocation.getDate())),
                    allocation.getCurrentAmount());
            lastAmount = allocation.getCurrentAmount();
        }
        return series;
    }

    public static JFreeChart mappingChart(final IntensityDemandMapping mapping) {
        final XYSeriesCollection dataset = new XYSeriesCollection();
        final XYSeries mappingSeries = new XYSeries("mapping function");
        double lastIntentsity = 0;
        for (IntensityResourcePair pair : mapping.getMappingList()) {
            mappingSeries.add(lastIntentsity, pair.resourceAmount);
            mappingSeries.add(pair.maxIntensity, pair.resourceAmount);
            lastIntentsity = pair.maxIntensity;
        }
        dataset.addSeries(mappingSeries);

        final JFreeChart chart = ChartFactory.createXYLineChart("", "Load Intensity", "Resource Amount", dataset);

        chartCustomization(chart);
        chart.getXYPlot().getRenderer().setSeriesPaint(0, Color.BLUE);
        chart.getXYPlot().getRenderer().setSeriesStroke(0, new BasicStroke(STROKE_WIDTH));
        NumberAxis rangeAxis = (NumberAxis) chart.getXYPlot().getRangeAxis();
        rangeAxis.setTickUnit(new NumberTickUnit(1));
        return chart;
    }

    public static JFreeChart intensityChart(final List<ArrivalRateTuple> intensities) {
        final CombinedDomainXYPlot plot = createRelativeTimeSeriesPlot();
        JFreeChart chart = new JFreeChart("", JFreeChart.DEFAULT_TITLE_FONT, plot, true);
        chartCustomization(chart);

        final XYPlot intensityPlot = createIntensityPlot(intensities);
        plot.add(intensityPlot, 1);

        return chart;
    }

    public static XYPlot createIntensityPlot(final List<ArrivalRateTuple> intensities) {
        final TimeSeriesCollection dataset = new TimeSeriesCollection();
        final TimeSeries series = new TimeSeries("load intensity");
        for (ArrivalRateTuple intensity : intensities) {
            long milliseconds = (referenceDate.getTime() / 1000 + (long) Math.floor(intensity.getTimeStamp()))
                    * 1000;
            series.add(new FixedMillisecond(milliseconds), intensity.getArrivalRate());
        }
        dataset.addSeries(series);

        XYStepRenderer renderer = new XYStepRenderer();
        //renderer.setSeriesStroke(0, (new BasicStroke(2.0F)));   
        renderer.setSeriesPaint(0, Color.GRAY);
        final NumberAxis rangeAxis = new NumberAxis("Arrival Rate [1/s]");
        //rangeAxis.setRange(0, 220);
        final XYPlot intensityPlot = new XYPlot(dataset, null, rangeAxis, renderer);
        return intensityPlot;
    }

    public static JFreeChart demandChart(final List<ArrivalRateTuple> intensities,
            final IntensityDemandMapping mapping, boolean showIntensities) {
        List<XYPlot> plots = new LinkedList<XYPlot>();
        if (showIntensities) {
            plots.add(createIntensityPlot(intensities));
        }
        plots.add(createDemandPlot(intensities, mapping));
        return createTimeSeriesChart(plots);
    }

    private static CombinedDomainXYPlot createRelativeTimeSeriesPlot() {
        DateAxis timeAxis = new DateAxis("Time");
        RelativeDateFormat relativeDateFormat = new RelativeDateFormat(referenceDate);
        relativeDateFormat.setSecondFormatter(new DecimalFormat("0"));
        relativeDateFormat.setShowZeroHours(false);
        timeAxis.setDateFormatOverride(relativeDateFormat);
        final CombinedDomainXYPlot plot = new CombinedDomainXYPlot(timeAxis);
        return plot;
    }

    public static XYPlot createDemandPlot(final List<ArrivalRateTuple> intensities,
            final IntensityDemandMapping mapping) {
        final TimeSeriesCollection dataset = new TimeSeriesCollection();
        final TimeSeries series = new TimeSeries("resource demand");
        for (ArrivalRateTuple intensity : intensities) {
            long milliseconds = (referenceDate.getTime() / 1000 + (long) Math.floor(intensity.getTimeStamp()))
                    * 1000;
            series.add(new FixedMillisecond(milliseconds), mapping.getResourceDemand(intensity.getArrivalRate()));
        }
        dataset.addSeries(series);

        XYStepRenderer renderer = new XYStepRenderer();
        renderer.setSeriesPaint(0, Color.RED);
        renderer.setSeriesStroke(0, new BasicStroke(STROKE_WIDTH));
        final NumberAxis rangeAxis = new NumberAxis("Resource Amount");
        rangeAxis.setAutoRangeIncludesZero(true);
        //rangeAxis.setRange(0,2.2);
        rangeAxis.setTickUnit(new NumberTickUnit(2));
        final XYPlot amountPlot = new XYPlot(dataset, null, rangeAxis, renderer);
        return amountPlot;
    }

    private static void chartCustomization(final JFreeChart chart) {
        chart.setBackgroundPaint(Color.white);
        final XYPlot plot = chart.getXYPlot();
        customizePlot(plot);
    }

    private static void customizePlot(final XYPlot plot) {
        plot.setOutlinePaint(null);
        plot.setBackgroundPaint(Color.white);
        plot.setDomainGridlinePaint(Color.LIGHT_GRAY);
        plot.setRangeGridlinePaint(Color.LIGHT_GRAY);
        plot.setDomainCrosshairVisible(false);
        plot.setRangeCrosshairVisible(false);
        //plot.setShadowGenerator(new DefaultShadowGenerator());
        Font font = new Font("Dialog", Font.PLAIN, 20);
        Font labelFont = new Font("Dialog", Font.PLAIN, 16);
        plot.getDomainAxis().setLabelFont(labelFont);
        plot.getRangeAxis().setLabelFont(labelFont);
        plot.getDomainAxis().setTickLabelFont(font);
        plot.getRangeAxis().setTickLabelFont(font);
    }

    private static XYPlot createAllocationPlot(final TimeSeriesCollection dataset) {

        XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
        renderer.setBaseShapesVisible(true);
        renderer.setBaseShapesFilled(false);
        final NumberAxis rangeAxis = new NumberAxis("Resource Amount");
        rangeAxis.setAutoRangeIncludesZero(true);
        //rangeAxis.setTickUnit(new NumberTickUnit(1));
        double lower = Math.min(1, dataset.getRangeBounds(false).getLowerBound()) - 0.2;
        double upper = Math.max(2, dataset.getRangeBounds(false).getUpperBound()) + 0.2;
        rangeAxis.setRange(lower, upper);
        final XYPlot allocationPlot = new XYPlot(dataset, null, rangeAxis, renderer);
        allocationPlot.setSeriesRenderingOrder(SeriesRenderingOrder.REVERSE);

        for (int i = 0; i < dataset.getSeriesCount(); i++) {
            TimeSeries series = dataset.getSeries(i);
            //addEndSeriesItem(lastDate, series);
            String description = series.getKey().toString();
            renderer.setSeriesStroke(i, new BasicStroke(STROKE_WIDTH));
            if (description.equals(AllocationSeries.CATEGORY.DEMAND.toString())) {
                renderer.setSeriesShapesVisible(i, false);
                renderer.setSeriesPaint(i, Color.RED);
            } else if (description.equals(SupplySeries.TYPE.VM_SCHEDULED.toString())) {
                renderer.setSeriesPaint(i, VARIANT_C_COLOR);
            } else if (description.equals(SupplySeries.TYPE.VM_COMPLETED.toString())) {
                renderer.setSeriesPaint(i, VARIANT_B_COLOR);
            } else if (description.equals(SupplySeries.TYPE.LB_RULE_ADAPTION.toString())) {
                if (dataset.getSeriesCount() != 2) {
                    renderer.setSeriesPaint(i, VARIANT_A_COLOR);
                } else {
                    renderer.setSeriesPaint(i, Color.BLUE);
                }
            } else if (description.equals(SupplySeries.TYPE.MONITORED.toString())) {
                if (dataset.getSeriesCount() != 2) {
                    renderer.setSeriesPaint(i, VARIANT_D_COLOR);
                } else {
                    renderer.setSeriesPaint(i, Color.BLUE);
                }
            } else {
                renderer.setSeriesPaint(i, colorForConfig(description));
            }
        }

        return allocationPlot;
    }

    private static Color colorForConfig(String configName) {
        if (configName.contains("config-a")) {
            return VARIANT_A_COLOR;
        } else if (configName.contains("config-b")) {
            return VARIANT_B_COLOR;
        } else if (configName.contains("config-c")) {
            return VARIANT_C_COLOR;
        } else if (configName.contains("config-d")) {
            return VARIANT_D_COLOR;
        } else {
            return Color.BLUE;
        }
    }

    public static JFreeChart createTimeSeriesChart(final XYPlot plot) {
        List<XYPlot> plots = new LinkedList<XYPlot>();
        plots.add(plot);
        return createTimeSeriesChart(plots);
    }

    public static JFreeChart createTimeSeriesChart(final List<XYPlot> plots) {
        final CombinedDomainXYPlot plot = createRelativeTimeSeriesPlot();
        plot.setGap(20);
        JFreeChart chart = new JFreeChart("", JFreeChart.DEFAULT_TITLE_FONT, plot, true);
        boolean manyCharts = plots.size() > 3;
        int num = 0;
        for (XYPlot xyPlot : plots) {
            num++;
            if (manyCharts && xyPlot.getRangeAxis().getLabel() == "Arrival Rate [1/s]") {
                plot.add(xyPlot, 3);
            } else {
                plot.add(xyPlot, 1);
            }

            chart.setBackgroundPaint(Color.white);
            customizePlot(xyPlot);
            if (manyCharts && xyPlot.getRangeAxis().getLabel() != "Arrival Rate [1/s]" && num != 4 && num != 7) {
                xyPlot.getRangeAxis().setLabel("");
            }
        }
        return chart;
    }
}