de.tsystems.mms.apm.performancesignature.ui.PerfSigProjectAction.java Source code

Java tutorial

Introduction

Here is the source code for de.tsystems.mms.apm.performancesignature.ui.PerfSigProjectAction.java

Source

/*
 * Copyright (c) 2014 T-Systems Multimedia Solutions GmbH
 *
 * 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 de.tsystems.mms.apm.performancesignature.ui;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.thoughtworks.xstream.XStream;
import de.tsystems.mms.apm.performancesignature.dynatrace.model.ChartDashlet;
import de.tsystems.mms.apm.performancesignature.dynatrace.model.DashboardReport;
import de.tsystems.mms.apm.performancesignature.dynatrace.model.Measure;
import de.tsystems.mms.apm.performancesignature.dynatrace.model.TestRun;
import de.tsystems.mms.apm.performancesignature.model.JSONDashlet;
import de.tsystems.mms.apm.performancesignature.model.PerfSigTestDataWrapper;
import de.tsystems.mms.apm.performancesignature.util.PerfSigUIUtils;
import hudson.XmlFile;
import hudson.model.Job;
import hudson.model.ProminentProjectAction;
import hudson.model.Run;
import hudson.tasks.junit.TestResult;
import hudson.tasks.junit.TestResultAction;
import hudson.tasks.test.TestResultProjectAction;
import hudson.util.*;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.StackedBarRenderer;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.bind.JavaScriptMethod;

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

public class PerfSigProjectAction extends PerfSigBaseAction implements ProminentProjectAction {
    static final String UNITTEST_DASHLETNAME = "unittest_overview";
    private static final String JSON_FILENAME = "gridconfig.xml";
    private static final XStream XSTREAM = new XStream2();
    private static final Logger logger = Logger.getLogger(PerfSigProjectAction.class.getName());
    private final Job<?, ?> job;
    private transient Map<String, JSONDashlet> jsonDashletMap;

    public PerfSigProjectAction(final Job<?, ?> job) {
        this.job = job;
    }

    @Override
    protected String getTitle() {
        return job.getDisplayName() + " PerfSig";
    }

    public Job<?, ?> getJob() {
        return job;
    }

    public TestResultProjectAction getTestResultProjectAction() {
        return job.getAction(TestResultProjectAction.class);
    }

    public Class<PerfSigUIUtils> getPerfSigUIUtils() {
        return PerfSigUIUtils.class;
    }

    private synchronized Map<String, JSONDashlet> getJsonDashletMap() throws InterruptedException {
        if (this.jsonDashletMap == null) {
            this.jsonDashletMap = new ConcurrentHashMap<>();
            this.jsonDashletMap.putAll(readConfiguration());
        }
        return this.jsonDashletMap;
    }

    public void doSummarizerGraph(final StaplerRequest request, final StaplerResponse response)
            throws IOException, InterruptedException {
        String id = request.getParameter("id");
        JSONDashlet knownJsonDashlet = getJsonDashletMap().get(id);
        final JSONDashlet jsonDashletToRender;

        if (knownJsonDashlet != null) { //dashlet from stored configuration
            jsonDashletToRender = knownJsonDashlet;
        } else {
            JSONDashlet newJsonDashlet = createJSONConfiguration(false).get(id);
            if (newJsonDashlet != null) { //new dashlet
                if (StringUtils.isNotBlank(request.getParameter("aggregation"))) {
                    newJsonDashlet.setAggregation(request.getParameter("aggregation"));
                }
                newJsonDashlet.setCustomName(request.getParameter("customName"));
                newJsonDashlet.setCustomBuildCount(request.getParameter("customBuildCount"));

                jsonDashletToRender = newJsonDashlet;
            } else {
                response.sendError(404, "no chartdashlet found for id " + id);
                return;
            }
        }

        final Graph graph = new PerfGraphImpl(jsonDashletToRender) {
            @Override
            protected DataSetBuilder<String, ChartUtil.NumberOnlyBuildLabel> createDataSet() {
                String dashboard = jsonDashletToRender.getDashboard();
                String chartDashlet = jsonDashletToRender.getChartDashlet();
                String measure = jsonDashletToRender.getMeasure();
                String buildCount = jsonDashletToRender.getCustomBuildCount();
                String aggregation = jsonDashletToRender.getAggregation();
                int customBuildCount = 0, i = 0;

                if (StringUtils.isNotBlank(buildCount)) {
                    customBuildCount = Integer.parseInt(buildCount);
                }

                Map<Run<?, ?>, DashboardReport> dashboardReports = getDashboardReports(dashboard);
                DataSetBuilder<String, ChartUtil.NumberOnlyBuildLabel> dsb = new DataSetBuilder<>();

                for (Map.Entry<Run<?, ?>, DashboardReport> dashboardReport : dashboardReports.entrySet()) {
                    double metricValue = 0;
                    if (dashboardReport.getValue().getChartDashlets() != null) {
                        Measure m = dashboardReport.getValue().getMeasure(chartDashlet, measure);
                        if (m != null) {
                            metricValue = StringUtils.isBlank(aggregation) ? m.getMetricValue()
                                    : m.getMetricValue(aggregation);
                        }
                    }
                    i++;
                    dsb.add(metricValue, chartDashlet,
                            new ChartUtil.NumberOnlyBuildLabel(dashboardReport.getKey()));
                    if (customBuildCount != 0 && i == customBuildCount) {
                        break;
                    }
                }
                return dsb;
            }
        };
        graph.doPng(request, response);
    }

    public void doTestRunGraph(final StaplerRequest request, final StaplerResponse response)
            throws InterruptedException, IOException {
        final String customName, customBuildCount;

        JSONDashlet jsonDashlet = getJsonDashletMap().get(UNITTEST_DASHLETNAME);
        if (jsonDashlet != null) {
            customName = jsonDashlet.getCustomName();
            customBuildCount = String.valueOf(jsonDashlet.getCustomBuildCount());
        } else { //generate test run graph with GET parameters
            customName = request.getParameter("customName");
            customBuildCount = request.getParameter("customBuildCount");
        }

        final Graph graph = new TestRunGraphImpl(customName) {
            @Override
            protected DataSetBuilder<String, ChartUtil.NumberOnlyBuildLabel> createDataSet() {
                final DataSetBuilder<String, ChartUtil.NumberOnlyBuildLabel> dsb = new DataSetBuilder<>();
                int buildCount = 0, i = 0;
                if (StringUtils.isNotBlank(customBuildCount)) {
                    buildCount = Integer.parseInt(customBuildCount);
                }

                for (Run<?, ?> run : job.getBuilds()) {
                    PerfSigTestDataWrapper testDataWrapper = run.getAction(PerfSigTestDataWrapper.class);
                    if (testDataWrapper != null && testDataWrapper.getTestRuns() != null) {
                        TestRun testRun = TestRun.mergeTestRuns(testDataWrapper.getTestRuns());
                        if (testRun != null) {
                            dsb.add(testRun.getNumFailed(), "failed", new ChartUtil.NumberOnlyBuildLabel(run));
                            dsb.add(testRun.getNumDegraded(), "degraded", new ChartUtil.NumberOnlyBuildLabel(run));
                            dsb.add(testRun.getNumImproved(), "improved", new ChartUtil.NumberOnlyBuildLabel(run));
                            dsb.add(testRun.getNumPassed(), "passed", new ChartUtil.NumberOnlyBuildLabel(run));
                            dsb.add(testRun.getNumVolatile(), "volatile", new ChartUtil.NumberOnlyBuildLabel(run));
                            dsb.add(testRun.getNumInvalidated(), "invalidated",
                                    new ChartUtil.NumberOnlyBuildLabel(run));
                        }
                    }
                    i++;
                    if (buildCount != 0 && i == buildCount) {
                        break;
                    }
                }
                return dsb;
            }
        };
        graph.doPng(request, response);
    }

    public List<DashboardReport> getLastDashboardReports() {
        final Run<?, ?> tb = job.getLastSuccessfulBuild();

        Run<?, ?> b = job.getLastBuild();
        while (b != null) {
            PerfSigBuildAction a = b.getAction(PerfSigBuildAction.class);
            if (a != null && (!b.isBuilding())) {
                return a.getDashboardReports();
            }
            if (b == tb) {
                return new ArrayList<>();
            }
            b = b.getPreviousBuild();
        }
        return new ArrayList<>();
    }

    public TestRun getTestRun(final Run<?, ?> run) {
        if (run != null) {
            PerfSigTestDataWrapper testDataWrapper = run.getAction(PerfSigTestDataWrapper.class);
            if (testDataWrapper != null) {
                return TestRun.mergeTestRuns(testDataWrapper.getTestRuns());
            }
        }
        return null;
    }

    public TestResult getTestAction(final Run<?, ?> run) {
        if (run != null) {
            TestResultAction testResultAction = run.getAction(TestResultAction.class);
            if (testResultAction != null) {
                return testResultAction.getResult();
            }
        }
        return null;
    }

    public Map<Run<?, ?>, DashboardReport> getDashboardReports(final String name) {
        final Map<Run<?, ?>, DashboardReport> dashboardReports = new HashMap<>();
        if (job == null) {
            return dashboardReports;
        }
        for (Run<?, ?> currentRun : job.getBuilds()) {
            final PerfSigBuildAction perfSigBuildAction = currentRun.getAction(PerfSigBuildAction.class);
            if (perfSigBuildAction != null) {
                DashboardReport dashboardReport = perfSigBuildAction.getBuildActionResultsDisplay()
                        .getDashBoardReport(name);
                if (dashboardReport == null) {
                    dashboardReport = new DashboardReport(name);
                }
                dashboardReports.put(currentRun, dashboardReport);
            }
        }
        return dashboardReports;
    }

    @JavaScriptMethod
    public String getDashboardConfiguration(final String dashboard) throws IOException, InterruptedException {
        List<JSONDashlet> jsonDashletList = new ArrayList<>();
        for (JSONDashlet jsonDashlet : getJsonDashletMap().values()) {
            if (jsonDashlet.getDashboard().equals(dashboard)) {
                jsonDashletList.add(jsonDashlet);
            }
        }

        return new Gson().toJson(jsonDashletList);
    }

    private Map<String, JSONDashlet> createJSONConfiguration(final boolean useRandomId) {
        int col = 1, row = 1;

        logger.fine(addTimeStampToLog("grid configuration generation started"));
        Map<String, JSONDashlet> newJsonDashletMap = new HashMap<>();
        for (DashboardReport dashboardReport : getLastDashboardReports()) {
            if (dashboardReport.isUnitTest()) {
                JSONDashlet dashlet = new JSONDashlet(col++, row, UNITTEST_DASHLETNAME, dashboardReport.getName());
                newJsonDashletMap.put(UNITTEST_DASHLETNAME, dashlet);
            }
            for (ChartDashlet chartDashlet : dashboardReport.getChartDashlets()) {
                for (Measure measure : chartDashlet.getMeasures()) {
                    JSONDashlet dashlet = new JSONDashlet(col++, row, dashboardReport.getName(),
                            chartDashlet.getName(), measure.getName(), measure.getAggregation(),
                            chartDashlet.getDescription());
                    if (useRandomId) {
                        dashlet.setId(dashlet.generateID());
                    }

                    newJsonDashletMap.put(dashlet.getId(), dashlet);

                    if (col > 3) {
                        col = 1;
                        row++;
                    }
                }
            }
        }
        logger.fine(addTimeStampToLog("grid configuration generation finished"));
        return newJsonDashletMap;
    }

    /*
    only for debug purposes
     */
    private String addTimeStampToLog(final String message) {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
        return sdf.format(new Date()) + ": " + this.getClass().getSimpleName() + "@"
                + Integer.toHexString(System.identityHashCode(this)) + ", threadId:"
                + Thread.currentThread().getId() + " " + message;
    }

    @JavaScriptMethod
    public void setDashboardConfiguration(final String dashboard, final String data) throws InterruptedException {
        Map<String, JSONDashlet> defaultConfiguration = createJSONConfiguration(false);
        HashSet<String> idsFromJson = new HashSet<>();

        String json = StringEscapeUtils.unescapeJava(data);
        if (!json.startsWith("[")) {
            json = json.substring(1, json.length() - 1);
        }

        List<JSONDashlet> jsonDashletList = new Gson().fromJson(json, new TypeToken<List<JSONDashlet>>() {
        }.getType());
        for (JSONDashlet jsonDashlet : jsonDashletList) {
            idsFromJson.add(jsonDashlet.getId());
        }

        try {
            for (JSONDashlet jsonDashlet : getJsonDashletMap().values()) {
                if (!jsonDashlet.getDashboard().equals(dashboard)) { //filter out dashlets from other dashboards
                    continue;
                }
                if (!idsFromJson.contains(jsonDashlet.getId())) { //remove dashlet, if it's not present in gridConfiguration
                    getJsonDashletMap().remove(jsonDashlet.getId());
                }
            }

            for (JSONDashlet modifiedDashlet : jsonDashletList) {
                JSONDashlet unmodifiedDashlet = defaultConfiguration.get(modifiedDashlet.getId());
                JSONDashlet originalDashlet = getJsonDashletMap().get(modifiedDashlet.getId());
                if (modifiedDashlet.getId().equals(UNITTEST_DASHLETNAME)) {
                    if (originalDashlet != null) {
                        modifiedDashlet.setCustomBuildCount(originalDashlet.getCustomBuildCount());
                        modifiedDashlet.setCustomName(originalDashlet.getCustomName());
                    }
                    getJsonDashletMap().put(modifiedDashlet.getId(), modifiedDashlet);
                } else if (unmodifiedDashlet != null) { //newly added dashlets
                    modifiedDashlet.setDashboard(unmodifiedDashlet.getDashboard());
                    modifiedDashlet.setChartDashlet(unmodifiedDashlet.getChartDashlet());
                    modifiedDashlet.setMeasure(unmodifiedDashlet.getMeasure());
                    modifiedDashlet.setDescription(unmodifiedDashlet.getDescription());
                    modifiedDashlet.setId(modifiedDashlet.generateID());

                    getJsonDashletMap().put(modifiedDashlet.getId(), modifiedDashlet);
                } else if (originalDashlet != null) { //old dashlets
                    modifiedDashlet.setDashboard(originalDashlet.getDashboard());
                    modifiedDashlet.setChartDashlet(originalDashlet.getChartDashlet());
                    modifiedDashlet.setMeasure(originalDashlet.getMeasure());
                    modifiedDashlet.setDescription(originalDashlet.getDescription());
                    modifiedDashlet.setAggregation(originalDashlet.getAggregation());
                    modifiedDashlet.setCustomBuildCount(originalDashlet.getCustomBuildCount());
                    modifiedDashlet.setCustomName(originalDashlet.getCustomName());

                    modifiedDashlet.setId(modifiedDashlet.generateID());
                    getJsonDashletMap().remove(originalDashlet.getId());
                    getJsonDashletMap().put(modifiedDashlet.getId(), modifiedDashlet);
                }
            }
            writeConfiguration(getJsonDashletMap());
        } catch (InterruptedException e) {
            logger.log(Level.SEVERE, Messages.PerfSigProjectAction_FailedToSaveGrid(), e);
            throw e;
        } catch (IOException e) {
            logger.log(Level.SEVERE, Messages.PerfSigProjectAction_FailedToSaveGrid(), e);
        }
    }

    @JavaScriptMethod
    public Map<String, String> getAvailableMeasures(final String dashboard, final String dashlet)
            throws IOException {
        Map<String, String> availableMeasures = new LinkedHashMap<>();
        List<JSONDashlet> jsonDashlets = new ArrayList<>(createJSONConfiguration(false).values());
        Collections.sort(jsonDashlets);
        for (JSONDashlet jsonDashlet : jsonDashlets) {
            if (jsonDashlet.getDashboard().equals(dashboard) && jsonDashlet.getChartDashlet().equals(dashlet)) {
                availableMeasures.put(jsonDashlet.getId(), jsonDashlet.getMeasure());
            }
        }
        return availableMeasures;
    }

    @JavaScriptMethod
    public String getAggregationFromMeasure(final String dashboard, final String dashlet, final String measure)
            throws IOException {
        for (DashboardReport dashboardReport : getLastDashboardReports()) {
            if (dashboardReport.getName().equals(dashboard)) {
                Measure m = dashboardReport.getMeasure(dashlet, measure);
                if (m != null) {
                    return m.getAggregation();
                }
            }
        }
        return "";
    }

    public Map<JSONDashlet, Measure> getFilteredChartDashlets(final DashboardReport dashboardReport)
            throws IOException, InterruptedException {
        Map<JSONDashlet, Measure> filteredChartDashlets = new TreeMap<>(new Comparator<JSONDashlet>() {
            @Override
            public int compare(final JSONDashlet o1, final JSONDashlet o2) {
                if (o1.getRow() > o2.getRow() || o1.getRow() == o2.getRow() && o1.getCol() > o2.getCol()) {
                    return 1;
                }
                return -1;
            }
        });

        if (dashboardReport.getChartDashlets() == null) {
            return filteredChartDashlets;
        }

        for (JSONDashlet jsonDashlet : getJsonDashletMap().values()) {
            if (!jsonDashlet.getDashboard().equals(dashboardReport.getName())) {
                continue;
            }
            boolean chartDashletFound = false;

            for (ChartDashlet dashlet : dashboardReport.getChartDashlets()) {
                if (dashlet.getName().equals(jsonDashlet.getChartDashlet())) {
                    for (Measure m : dashlet.getMeasures()) {
                        if (m.getName().equals(jsonDashlet.getMeasure())) {
                            filteredChartDashlets.put(jsonDashlet, m);
                            chartDashletFound = true;
                            break;
                        }
                    }
                }
            }
            if (!chartDashletFound && !jsonDashlet.getId().equals(UNITTEST_DASHLETNAME)) {
                filteredChartDashlets.put(jsonDashlet, new Measure(null));
            }
        }
        return filteredChartDashlets;
    }

    private synchronized XmlFile getConfigFile() {
        return new XmlFile(XSTREAM, new File(job.getConfigFile().getFile().getParent(), JSON_FILENAME));
    }

    @SuppressWarnings("unchecked")
    private Map<String, JSONDashlet> readConfiguration() throws InterruptedException {
        logger.fine(addTimeStampToLog("grid configuration read started"));
        try {
            if (getConfigFile().exists()) {
                Map<String, JSONDashlet> configuration = (Map<String, JSONDashlet>) getConfigFile().read();
                logger.fine(addTimeStampToLog("grid configuration read finished (config file exists)"));
                return configuration;
            } else {
                Map<String, JSONDashlet> newConfiguration = createJSONConfiguration(true);
                writeConfiguration(newConfiguration);
                logger.fine(addTimeStampToLog("grid configuration read finished (config file created)"));
                return newConfiguration;
            }
        } catch (IOException e) {
            logger.log(Level.SEVERE, Messages.PerfSigProjectAction_FailedToLoadConfigFile(getConfigFile()), e);
        } catch (InterruptedException e) {
            logger.log(Level.SEVERE, Messages.PerfSigProjectAction_FailedToLoadConfigFile(getConfigFile()), e);
            throw e;
        }
        return new HashMap<>();
    }

    private void writeConfiguration(final Map<String, JSONDashlet> jsonDashletMap)
            throws IOException, InterruptedException {
        try {
            logger.fine(addTimeStampToLog("grid configuration write started"));
            getConfigFile().write(jsonDashletMap);
            logger.fine(addTimeStampToLog("grid configuration write finished"));
        } catch (IOException e) {
            logger.log(Level.SEVERE, Messages.PerfSigProjectAction_FailedToSaveGrid(), e);
        }
    }

    private abstract class PerfGraphImpl extends Graph {
        private final JSONDashlet jsonDashlet;

        protected PerfGraphImpl(final JSONDashlet jsonDashlet) {
            super(-1, 600, 300);
            this.jsonDashlet = jsonDashlet;
        }

        protected abstract DataSetBuilder<String, ChartUtil.NumberOnlyBuildLabel> createDataSet();

        protected JFreeChart createGraph() {
            final String measure = jsonDashlet.getMeasure();
            final String chartDashlet = jsonDashlet.getChartDashlet();
            final String dashboard = jsonDashlet.getDashboard();
            final String customMeasureName = jsonDashlet.getCustomName();
            final String aggregation = jsonDashlet.getAggregation();

            String unit = "", color = "#FF5555";

            for (DashboardReport dr : getLastDashboardReports()) {
                if (dr.getName().equals(dashboard)) {
                    final Measure m = dr.getMeasure(chartDashlet, measure);
                    if (m != null) {
                        unit = "Count".equalsIgnoreCase(aggregation) ? "num" : m.getUnit();
                        color = m.getColor();
                    }
                    break;
                }
            }

            String title = StringUtils.isBlank(customMeasureName)
                    ? PerfSigUIUtils.generateTitle(measure, chartDashlet, aggregation)
                    : customMeasureName;

            final JFreeChart chart = ChartFactory.createBarChart(title, // title
                    Messages.PerfSigProjectAction_Build(), // category axis label
                    unit, // value axis label
                    createDataSet().build(), // data
                    PlotOrientation.VERTICAL, // orientation
                    false, // include legend
                    false, // tooltips
                    false // urls
            );

            chart.setBackgroundPaint(Color.white);

            final CategoryPlot plot = chart.getCategoryPlot();

            plot.setBackgroundPaint(Color.WHITE);
            plot.setOutlinePaint(null);
            plot.setForegroundAlpha(0.8f);
            plot.setRangeGridlinesVisible(true);
            plot.setRangeGridlinePaint(Color.black);

            final CategoryAxis domainAxis = new ShiftedCategoryAxis(null);
            plot.setDomainAxis(domainAxis);
            domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_90);
            //domainAxis.setLowerMargin(0.0);
            //domainAxis.setUpperMargin(0.0);
            //domainAxis.setCategoryMargin(0.0);

            final NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
            rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());

            final BarRenderer renderer = (BarRenderer) chart.getCategoryPlot().getRenderer();
            renderer.setSeriesPaint(0, Color.decode(color));

            return chart;
        }
    }

    private abstract class TestRunGraphImpl extends Graph {
        private final String customName;

        protected TestRunGraphImpl(final String customName) {
            super(-1, 600, 300);
            this.customName = customName;
        }

        protected abstract DataSetBuilder<String, ChartUtil.NumberOnlyBuildLabel> createDataSet();

        protected JFreeChart createGraph() {
            String title = StringUtils.isNotBlank(customName) ? customName : "UnitTest overview";

            final JFreeChart chart = ChartFactory.createBarChart(title, // title
                    "build", // category axis label
                    "num", // value axis label
                    createDataSet().build(), // data
                    PlotOrientation.VERTICAL, // orientation
                    true, // include legend
                    true, // tooltips
                    false // urls
            );

            chart.setBackgroundPaint(Color.white);

            final CategoryPlot plot = chart.getCategoryPlot();

            plot.setBackgroundPaint(Color.WHITE);
            plot.setOutlinePaint(null);
            plot.setForegroundAlpha(0.8f);
            plot.setRangeGridlinesVisible(true);
            plot.setRangeGridlinePaint(Color.black);

            final CategoryAxis domainAxis = new ShiftedCategoryAxis(null);
            plot.setDomainAxis(domainAxis);
            domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_90);
            //domainAxis.setLowerMargin(0.0);
            //domainAxis.setUpperMargin(0.0);
            //domainAxis.setCategoryMargin(0.0);

            final NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
            rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());

            final StackedBarRenderer br = new StackedBarRenderer();
            plot.setRenderer(br);
            br.setSeriesPaint(0, new Color(0xFF, 0x99, 0x99)); // degraded
            br.setSeriesPaint(1, ColorPalette.RED); // failed
            br.setSeriesPaint(2, new Color(0x00, 0xFF, 0x00)); // improved
            br.setSeriesPaint(3, ColorPalette.GREY); // invalidated
            br.setSeriesPaint(4, ColorPalette.BLUE); // passed
            br.setSeriesPaint(5, ColorPalette.YELLOW); // volatile

            return chart;
        }
    }
}