diff --git a/src/main/java/com/tagtraum/perf/gcviewer/SimpleChartRenderer.java b/src/main/java/com/tagtraum/perf/gcviewer/SimpleChartRenderer.java index 3254f5c8..ad8b7c7c 100644 --- a/src/main/java/com/tagtraum/perf/gcviewer/SimpleChartRenderer.java +++ b/src/main/java/com/tagtraum/perf/gcviewer/SimpleChartRenderer.java @@ -2,10 +2,13 @@ import java.awt.Color; import java.awt.Dimension; +import java.awt.EventQueue; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; import java.util.logging.Level; import java.util.logging.Logger; import javax.imageio.ImageIO; @@ -13,6 +16,7 @@ import javax.swing.SwingUtilities; import com.tagtraum.perf.gcviewer.model.GCModel; +import javafx.scene.chart.Chart; public class SimpleChartRenderer { private static final Logger LOGGER = Logger.getLogger(SimpleChartRenderer.class.getName()); @@ -22,37 +26,96 @@ public void render(GCModel model, FileOutputStream outputStream) throws IOExcept gcPreferences.load(); Dimension d = new Dimension(gcPreferences.getWindowWidth(), gcPreferences.getWindowHeight()); - BufferedImage image = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_RGB); Graphics2D graphics = image.createGraphics(); graphics.setBackground(Color.WHITE); graphics.clearRect(0, 0, image.getWidth(), image.getHeight()); - try { - SwingUtilities.invokeAndWait(new Runnable() { - @Override - public void run() { - ModelChartImpl pane = new ModelChartImpl(); - pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); - - pane.setModel(model, gcPreferences); - pane.setFootprint(model.getFootprint()); - pane.setMaxPause(model.getPause().getMax()); - pane.setRunningTime(model.getRunningTime()); - - pane.setSize(d); - pane.addNotify(); - pane.validate(); - - pane.autoSetScaleFactor(); - pane.paint(graphics); - } - }); + + ChartDrawingParameters params + = new ChartDrawingParameters(model, gcPreferences, d, graphics, image, outputStream); + + if (EventQueue.isDispatchThread()) { + drawAndSaveToStream(params); } - catch (Exception e) { - LOGGER.log(Level.SEVERE, "Failed to paint graphic", e); + else { + new SwingChartToStreamHelper().execute(params); + } + } + + private void drawAndSaveToStream(ChartDrawingParameters params) throws IOException { + ModelChartImpl pane = new ModelChartImpl(); + pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + + pane.setModel(params.model, params.gcPreferences); + pane.setFootprint(params.model.getFootprint()); + pane.setMaxPause(params.model.getPause().getMax()); + pane.setRunningTime(params.model.getRunningTime()); + + pane.setSize(params.dimension); + pane.addNotify(); + pane.validate(); + + pane.autoSetScaleFactor(); + pane.paint(params.graphics); + + ImageIO.write(params.image, "png", params.outputStream); + params.outputStream.close(); + } + + /** + * Helperclass that saves the chart to a file making sure, it happens on the EventDispathThread. + */ + private class SwingChartToStreamHelper { + + private IOException ioException; + + public void execute(ChartDrawingParameters params) throws IOException { + try { + SwingUtilities.invokeAndWait(() -> { + try { + drawAndSaveToStream(params); + } + catch (IOException e) { + this.ioException = e; + } + }); + } + catch (InterruptedException | InvocationTargetException e) { + // may look a bit strange, but allows to defer exception handling to a place, where it makes sense. + ioException = new IOException(e); + } + + if (ioException != null) { + throw ioException; + } } + } + + /** + * Holder class wrapping all parameters needed to draw the chart and save it to an OutputStream. + */ + private static class ChartDrawingParameters { + + GCModel model; + GCPreferences gcPreferences; + Dimension dimension; + Graphics2D graphics; + BufferedImage image; + OutputStream outputStream; - ImageIO.write(image, "png", outputStream); - outputStream.close(); + public ChartDrawingParameters(GCModel model, + GCPreferences gcPreferences, + Dimension dimension, + Graphics2D graphics, + BufferedImage image, + OutputStream outputStream) { + + this.model = model; + this.gcPreferences = gcPreferences; + this.dimension = dimension; + this.graphics = graphics; + this.image = image; + this.outputStream = outputStream; + } } }