Java tutorial
/******************************************************************************* * Copyright (c) 2007, 2010 The Planets Project Partners. * * All rights reserved. This program and the accompanying * materials are made available under the terms of the * Apache License, Version 2.0 which accompanies * this distribution, and is available at * http://www.apache.org/licenses/LICENSE-2.0 * *******************************************************************************/ /** * */ package eu.planets_project.tb.impl.chart; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Paint; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.batik.dom.GenericDOMImplementation; import org.apache.batik.svggen.SVGGraphics2D; import org.apache.batik.svggen.SVGGraphics2DIOException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartUtilities; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.CategoryLabelPositions; import org.jfree.chart.axis.LogAxis; import org.jfree.chart.labels.StandardCategoryToolTipGenerator; import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.category.BarRenderer; import org.jfree.chart.renderer.category.StandardBarPainter; import org.jfree.chart.util.LogFormat; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.data.general.DefaultPieDataset; import org.jfree.data.time.Day; import org.jfree.data.time.TimeSeries; import org.jfree.data.time.TimeSeriesCollection; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.jfree.date.MonthConstants; import org.jfree.ui.RectangleInsets; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import eu.planets_project.tb.api.data.util.DigitalObjectRefBean; import eu.planets_project.tb.api.model.Experiment; import eu.planets_project.tb.api.persistency.ExperimentPersistencyRemote; import eu.planets_project.tb.gui.backing.exp.ResultsForDigitalObjectBean; import eu.planets_project.tb.impl.data.util.DataHandlerImpl; import eu.planets_project.tb.impl.model.eval.mockup.TecRegMockup; import eu.planets_project.tb.impl.model.exec.BatchExecutionRecordImpl; import eu.planets_project.tb.impl.model.exec.ExecutionRecordImpl; import eu.planets_project.tb.impl.model.exec.ExecutionStageRecordImpl; import eu.planets_project.tb.impl.persistency.ExperimentPersistencyImpl; /** * @author <a href="mailto:Andrew.Jackson@bl.uk">Andy Jackson</a> * */ public class ExperimentChartServlet extends HttpServlet { /** * @author anj */ public class RunComparator implements Comparator<ExecutionRecordImpl> { /* (non-Javadoc) * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */ public int compare(ExecutionRecordImpl arg0, ExecutionRecordImpl arg1) { if (arg0 != null && arg1 != null && arg0.getStartDate() != null) { return arg0.getStartDate().compareTo(arg1.getStartDate()); } return 0; } } /** */ private static Log log = LogFactory.getLog(ExperimentChartServlet.class); /** * */ private static final long serialVersionUID = -417368403030414811L; /** * Default constructor. */ public ExperimentChartServlet() { // nothing required } /** * Process a GET request. * * @param request the request. * @param response the response. * * @throws ServletException if there is a servlet related problem. * @throws IOException if there is an I/O problem. */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { OutputStream out = response.getOutputStream(); try { String type = request.getParameter("type"); String format = request.getParameter("format"); String eid = request.getParameter("eid"); JFreeChart chart = null; if ("pie".equalsIgnoreCase(type)) { chart = createPieChart(); } else if ("bar".equalsIgnoreCase(type)) { chart = createBarChart(); } else if ("time".equalsIgnoreCase(type)) { chart = createTimeSeriesChart(); } else if ("exp".equalsIgnoreCase(type)) { chart = createXYChart(eid); } else if ("wall".equalsIgnoreCase(type)) { chart = createWallclockChart(eid); } else { chart = null; } // Render if (chart != null) { if ("svg".equalsIgnoreCase(format)) { response.setContentType("image/svg+xml"); writeChartAsSVG(out, chart, 600, 500); } else { response.setContentType("image/png"); // force aliasing of the rendered content.. chart.getRenderingHints().put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); ChartUtilities.writeChartAsPNG(out, chart, 600, 500); } } } catch (Exception e) { System.err.println(e.toString()); e.printStackTrace(); } finally { out.close(); } } /** * Creates a sample pie chart. * * @return a pie chart. */ private JFreeChart createPieChart() { // create a dataset... DefaultPieDataset data = new DefaultPieDataset(); data.setValue("One", new Double(43.2)); data.setValue("Two", new Double(10.0)); data.setValue("Three", new Double(27.5)); data.setValue("Four", new Double(17.5)); data.setValue("Five", new Double(11.0)); data.setValue("Six", new Double(19.4)); JFreeChart chart = ChartFactory.createPieChart("Pie Chart", data, true, true, false); return chart; } /** * Creates a sample bar chart. * * @return a bar chart. */ private JFreeChart createBarChart() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(10.0, "S1", "C1"); dataset.addValue(4.0, "S1", "C2"); dataset.addValue(15.0, "S1", "C3"); dataset.addValue(14.0, "S1", "C4"); dataset.addValue(-5.0, "S2", "C1"); dataset.addValue(-7.0, "S2", "C2"); dataset.addValue(14.0, "S2", "C3"); dataset.addValue(-3.0, "S2", "C4"); dataset.addValue(6.0, "S3", "C1"); dataset.addValue(17.0, "S3", "C2"); dataset.addValue(-12.0, "S3", "C3"); dataset.addValue(7.0, "S3", "C4"); dataset.addValue(7.0, "S4", "C1"); dataset.addValue(15.0, "S4", "C2"); dataset.addValue(11.0, "S4", "C3"); dataset.addValue(0.0, "S4", "C4"); dataset.addValue(-8.0, "S5", "C1"); dataset.addValue(-6.0, "S5", "C2"); dataset.addValue(10.0, "S5", "C3"); dataset.addValue(-9.0, "S5", "C4"); dataset.addValue(9.0, "S6", "C1"); dataset.addValue(8.0, "S6", "C2"); dataset.addValue(null, "S6", "C3"); dataset.addValue(6.0, "S6", "C4"); dataset.addValue(-10.0, "S7", "C1"); dataset.addValue(9.0, "S7", "C2"); dataset.addValue(7.0, "S7", "C3"); dataset.addValue(7.0, "S7", "C4"); dataset.addValue(11.0, "S8", "C1"); dataset.addValue(13.0, "S8", "C2"); dataset.addValue(9.0, "S8", "C3"); dataset.addValue(9.0, "S8", "C4"); dataset.addValue(-3.0, "S9", "C1"); dataset.addValue(7.0, "S9", "C2"); dataset.addValue(11.0, "S9", "C3"); dataset.addValue(-10.0, "S9", "C4"); JFreeChart chart = ChartFactory.createBarChart3D("Bar Chart", "Category", "Value", dataset, PlotOrientation.VERTICAL, true, true, false); return chart; } /** * Creates a sample time series chart. * * @return a time series chart. */ private JFreeChart createTimeSeriesChart() { // here we just populate a series with random data... TimeSeries series = new TimeSeries("Random Data"); Day current = new Day(1, MonthConstants.JANUARY, 2001); for (int i = 0; i < 100; i++) { series.add(current, Math.random() * 100); current = (Day) current.next(); } XYDataset data = new TimeSeriesCollection(series); JFreeChart chart = ChartFactory.createTimeSeriesChart("Time Series Chart", "Date", "Rate", data, true, true, false); return chart; } /** * Creates a sample dataset. * * @return A sample dataset. */ private static XYDataset createDataset() { XYSeries series = new XYSeries("Random Data"); series.add(1.0, 500.2); series.add(5.0, 694.1); series.add(4.0, 100.0); series.add(12.5, 734.4); series.add(17.3, 453.2); series.add(21.2, 500.2); series.add(21.9, null); series.add(25.6, 734.4); series.add(30.0, 453.2); return new XYSeriesCollection(series); } public JFreeChart createXYChart(String expId) { ExperimentPersistencyRemote edao = ExperimentPersistencyImpl.getInstance(); long eid = Long.parseLong(expId); log.info("Building experiment chart for eid = " + eid); Experiment exp = edao.findExperiment(eid); final String expName = exp.getExperimentSetup().getBasicProperties().getExperimentName(); final XYSeries series = new XYSeries(expName); for (BatchExecutionRecordImpl batch : exp.getExperimentExecutable().getBatchExecutionRecords()) { int i = 1; for (ExecutionRecordImpl exr : batch.getRuns()) { //log.info("Found Record... "+exr+" stages: "+exr.getStages()); if (exr != null && exr.getStages() != null) { // Look up the object, so we can get the name. DigitalObjectRefBean dh = new DataHandlerImpl().get(exr.getDigitalObjectReferenceCopy()); String dobName = "Object " + i; if (dh != null) dobName = dh.getName(); for (ExecutionStageRecordImpl exsr : exr.getStages()) { Double time = exsr.getDoubleMeasurement(TecRegMockup.PROP_SERVICE_TIME); Double size = exsr.getDoubleMeasurement(TecRegMockup.PROP_DO_SIZE); // Look for timing: if (time != null && size != null && size.doubleValue() > 0.0 && time.doubleValue() > 0.0) { series.add(size, time); /* if( exsr.isMarkedAsSuccessful() ) { dataset.addValue( time, "Succeded", dobName); } else { dataset.addValue( time, "Failed", dobName); } */ } } } // Increment, for the next run. i++; } } // Create the chart. JFreeChart chart = ChartFactory.createScatterPlot(null, "Size [bytes]", "Time [s]", new XYSeriesCollection(series), PlotOrientation.VERTICAL, true, true, false); XYPlot plot = (XYPlot) chart.getPlot(); LogAxis xAxis = new LogAxis("Size [bytes]"); // Set the base appropriately: xAxis.setBase(1024.0); // xAxis.setTickUnit( new NumberTickUnit(128.0) ); // xAxis.getTickUnit().getMinorTickCount(); // FIXME This should really be a KB/MB/etc number formatter... xAxis.setNumberFormatOverride(new LogFormat(1024.0, "1024", true)); // LogAxis yAxis = new LogAxis("Y"); // yAxis.setNumberFormatOverride(new LogFormat(10.0, "10", true)); plot.setDomainAxis(xAxis); // plot.setRangeAxis(yAxis); // Add some tool-tips plot.getRenderer().setBaseToolTipGenerator(new StandardXYToolTipGenerator()); return chart; } public JFreeChart createWallclockChart(String expId) { ExperimentPersistencyRemote edao = ExperimentPersistencyImpl.getInstance(); long eid = Long.parseLong(expId); log.info("Building experiment chart for eid = " + eid); Experiment exp = edao.findExperiment(eid); final DefaultCategoryDataset dataset = new DefaultCategoryDataset(); final String expName = exp.getExperimentSetup().getBasicProperties().getExperimentName(); boolean hasSuccesses = false; boolean hasFails = false; for (BatchExecutionRecordImpl batch : exp.getExperimentExecutable().getBatchExecutionRecords()) { int i = 1; List<ExecutionRecordImpl> runs = new ArrayList<ExecutionRecordImpl>(batch.getRuns()); Collections.sort(runs, new RunComparator()); for (ExecutionRecordImpl exr : runs) { //log.info("Found Record... "+exr+" stages: "+exr.getStages()); if (exr != null && exr.getStages() != null) { // Look up the object, so we can get the name. DigitalObjectRefBean dh = new DataHandlerImpl().get(exr.getDigitalObjectReferenceCopy()); String dobName = "Object " + i; if (dh != null) dobName = dh.getName(); ResultsForDigitalObjectBean res = new ResultsForDigitalObjectBean( exr.getDigitalObjectReferenceCopy()); Double time = null; boolean success = false; // First, attempt to pull from stage records: // FIXME: Note that this record is really at the wrong level. /* if( exr.getStages().size() == 1 ) { for( ExecutionStageRecordImpl exsr : exr.getStages() ) { Double stageTime = exsr.getDoubleMeasurement( TecRegMockup.PROP_SERVICE_TIME ); if( stageTime != null ) { time = stageTime; success = exsr.isMarkedAsSuccessful(); } } } */ // Pick up from record duration: if (time == null && res.getExecutionDuration() != null) { //convert from milli seconds to seconds time = (double) res.getExecutionDuration() / 1000.0; success = res.getHasExecutionSucceededOK(); } log.info("Found DOB: {" + exr.getDigitalObjectReferenceCopy() + "} {" + dobName + "} w/ time " + time); if (res.getExecutionRecord() != null) log.info("Timing: " + res.getExecutionRecord().getStartDate() + " " + res.getExecutionRecord().getEndDate()); if (time != null) { if (success) { dataset.addValue(time, "Succeeded", dobName); hasSuccesses = true; } else { dataset.addValue(time, "Failed", dobName); hasFails = true; } } } // Increment, for the next run. i++; } } int si = dataset.getRowIndex("Succeeded"); int ri = dataset.getRowIndex("Failed"); // Create the chart. JFreeChart chart = ChartFactory.createStackedBarChart(null, "Digital Object", "Time [s]", dataset, PlotOrientation.VERTICAL, true, true, false); // set the background color for the chart... chart.setBackgroundPaint(Color.white); // get a reference to the plot for further customisation... final CategoryPlot plot = chart.getCategoryPlot(); plot.setBackgroundPaint(Color.lightGray); plot.setDomainGridlinePaint(Color.gray); plot.setRangeGridlinePaint(Color.gray); // set the range axis to display integers only... //final NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis(); //rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); // disable bar outlines... final BarRenderer renderer = (BarRenderer) plot.getRenderer(); renderer.setDrawBarOutline(true); renderer.setShadowVisible(false); renderer.setBarPainter(new StandardBarPainter()); // set up gradient paints for series... final GradientPaint gp0 = new GradientPaint(0.0f, 0.0f, Color.green, 0.0f, 0.0f, new Color(0.0f, 0.9f, 0.0f)); final GradientPaint gp1 = new GradientPaint(0.0f, 0.0f, Color.red, 0.0f, 0.0f, new Color(0.9f, 0.0f, 0.0f)); if (hasSuccesses) renderer.setSeriesPaint(si, gp0); if (hasFails) renderer.setSeriesPaint(ri, gp1); // Set the tooltips... //renderer.setBaseItemURLGenerator(new StandardCategoryURLGenerator("xy_chart.jsp","series","section")); renderer.setBaseToolTipGenerator(new MeasurementToolTipGenerator()); final CategoryAxis domainAxis = plot.getDomainAxis(); domainAxis.setCategoryLabelPositions(CategoryLabelPositions.createUpRotationLabelPositions(Math.PI / 4.0)); // More settings chart.getRenderingHints().put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // Remove the border, as the SVG renderer has problems with the text overflowing. chart.getLegend().setBorder(0.0, 0.0, 0.0, 0.0); // Remove the padding between the axes and the plot: chart.getPlot().setInsets(new RectangleInsets(0.0, 0.0, 0.0, 0.0)); // Set a gradient fill, fading towards the top: final GradientPaint gpb0 = new GradientPaint(0.0f, 0.0f, new Color(245, 245, 245), 0.0f, 0.0f, Color.white); chart.getPlot().setBackgroundPaint(gpb0); return chart; } static class MeasurementToolTipGenerator extends StandardCategoryToolTipGenerator { /** * */ private static final long serialVersionUID = 5318030018610824973L; /* (non-Javadoc) * @see org.jfree.chart.labels.StandardCategoryToolTipGenerator#generateToolTip(org.jfree.data.category.CategoryDataset, int, int) */ @Override public String generateToolTip(CategoryDataset dataset, int row, int column) { String toolTip = super.generateToolTip(dataset, row, column); toolTip = super.generateColumnLabel(dataset, column) + " - " + super.generateRowLabel(dataset, row) + " in " + dataset.getValue(row, column) + "s"; return toolTip; } } /** * A custom renderer that returns a different color for each item in a * single series. */ static class CustomRenderer extends BarRenderer { /** * */ private static final long serialVersionUID = 1404034457254212917L; /** The colors. */ private Paint[] colors; /** * Creates a new renderer. * * @param colors the colors. */ public CustomRenderer(Paint[] colors) { this.colors = colors; } /** * Returns the paint for an item. Overrides the default behaviour * inherited from AbstractSeriesRenderer. * * @param row the series. * @param column the category. * * @return The item color. */ public Paint getItemPaint(int row, int column) { return this.colors[column % this.colors.length]; } } public static void writeChartAsSVG(OutputStream outstream, JFreeChart chart, int width, int height) throws UnsupportedEncodingException, SVGGraphics2DIOException { Writer out = new OutputStreamWriter(outstream, "UTF-8"); // THE FOLLOWING CODE BASED ON THE EXAMPLE IN THE BATIK DOCUMENTATION... // Get a DOMImplementation DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation(); // Create an instance of org.w3c.dom.Document Document document = domImpl.createDocument(null, "svg", null); // Create an instance of the SVG Generator SVGGraphics2D svgGenerator = new SVGGraphics2D(document); // set the precision to avoid a null pointer exception in Batik 1.5 svgGenerator.getGeneratorContext().setPrecision(3); // Other opts: svgGenerator.getGeneratorContext().setEmbeddedFontsOn(true); // svgGenerator.setFont( new Font("SansSerif", Font.PLAIN, 8) ); // Ask the chart to render into the SVG Graphics2D implementation chart.draw(svgGenerator, new Rectangle2D.Double(0, 0, width, height), null); // Finally, stream out SVG to a file using UTF-8 character to // byte encoding boolean useCSS = true; svgGenerator.stream(out, useCSS); } }