Exception printout on fast changing series

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
brimborium
Posts: 23
Joined: Mon Dec 13, 2010 10:23 am
antibot: No, of course not.

Exception printout on fast changing series

Post by brimborium » Thu Jan 13, 2011 10:39 am

Hi there,

I use jfreechart-1.0.13.jar and jcommon-1.0.16.jar. I am working with a chart which has a single dataset with multiple series in it. The series are private fields that change over time (get cleared and filled up again with different data). If these changes come in at a low rate, everything is fine, but when I send in new data at a fast rate, the System.out prints out an IndexOutOfBoundsException (which is not thrown at me). It must be catched inside JFC somewhere and printed out with e.printStackTrace() or so.

Here is an example:

Code: Select all

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;

import javax.swing.JFrame;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

public class PlotDemo extends JFrame implements Runnable {
	private static final long serialVersionUID = 1L;
	private XYSeries plotSeries1, plotSeries2;

	public PlotDemo() {
		XYSeriesCollection data = new XYSeriesCollection();
		XYLineAndShapeRenderer r = new XYLineAndShapeRenderer();
		plotSeries1 = new XYSeries("demoSeries1");
		plotSeries2 = new XYSeries("demoSeries2");
		for(int i = 0; i < 500; i++) {
			plotSeries2.add(i, Math.sin(i / 50.0));
		}
		data.addSeries(plotSeries1);
		data.addSeries(plotSeries2);

		r.setSeriesStroke(0, new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
		r.setSeriesShape(0, new Rectangle(new Dimension(0, 0)));
		r.setSeriesPaint(0, Color.blue);
		r.setSeriesStroke(1, new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
		r.setSeriesShape(1, new Rectangle(new Dimension(0, 0)));
		r.setSeriesPaint(1, Color.red);

		JFreeChart chart = ChartFactory.createXYLineChart("", "", "",
						data, PlotOrientation.VERTICAL, false, true, false);
		XYPlot plot = chart.getXYPlot();
		plot.setRenderer(r);

		ValueAxis domain= new NumberAxis(), range = new NumberAxis();
		plot.setDomainAxis(domain);
		plot.setRangeAxis(range);

		ChartPanel phnPlotPanel = new ChartPanel(chart);
		add(phnPlotPanel, BorderLayout.CENTER);

		setVisible(true);
		setSize(new Dimension(400, 400));
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}

	public static void main(String[] args) {
		PlotDemo main = new PlotDemo();
		new Thread(main).start();
	}

	@Override
	public void run() {
		for(int t = 0; t < 1000; t++) {
			plotSeries1.clear();
			for(int i = 0; i < 500; i++) {
				plotSeries1.add(i, Math.random() * Math.sin(i / 30.0), false);
			}
			plotSeries1.fireSeriesChanged();
			sleepMs(10);                  // <------- change time here to see the difference
		}
	}

	private void sleepMs(int ms) {
		try {
			Thread.sleep(ms);
		} catch(InterruptedException e) {}
	}
}
If the sleepMs waits for 10ms, the exception is printed. If you insert for example 500ms, the exception is not printed anymore. Can anybody explain to me what I am doing wrong or if I should just ignore the exception? It is a little bit annoying, to be honest. ;)
Or is the whole concept I am using to present different datasets over time, totally wrong? Before, I just created a new plotseries every time and then removed the old one and added the new one from/to the plot. But I thought this was kind of senseless.

Cheers, Stefan

brimborium
Posts: 23
Joined: Mon Dec 13, 2010 10:23 am
antibot: No, of course not.

Re: Exception printout on fast changing series

Post by brimborium » Thu Oct 20, 2011 9:25 am

Hi there,

I am still getting the same exceptions and am still wondering what their all about. Everything seems to work just fine, but the exceptions are very annoying. Can somebody help me out here?

The most often occuring exceptions that are printed are:

Code: Select all

Exception in thread "AWT-EventQueue-0" java.lang.IndexOutOfBoundsException: Index: 2749, Size: 0
	at java.util.ArrayList.RangeCheck(ArrayList.java:547)
	at java.util.ArrayList.get(ArrayList.java:322)
	at org.jfree.data.xy.XYSeries.getDataItem(XYSeries.java:616)
	at org.jfree.data.xy.XYSeriesCollection.getX(XYSeriesCollection.java:320)
	at org.jfree.data.xy.AbstractXYDataset.getXValue(AbstractXYDataset.java:75)
	at org.jfree.chart.renderer.xy.XYLineAndShapeRenderer.drawPrimaryLine(XYLineAndShapeRenderer.java:985)
	at org.jfree.chart.renderer.xy.XYLineAndShapeRenderer.drawItem(XYLineAndShapeRenderer.java:911)
	at org.jfree.chart.plot.XYPlot.render(XYPlot.java:3738)
	at org.jfree.chart.plot.XYPlot.draw(XYPlot.java:3310)
	at org.jfree.chart.JFreeChart.draw(JFreeChart.java:1235)
	at org.jfree.chart.ChartPanel.paintComponent(ChartPanel.java:1668)
	at javax.swing.JComponent.paint(JComponent.java:1029)
	at javax.swing.JComponent.paintToOffscreen(JComponent.java:5124)
	at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:278)
	at javax.swing.RepaintManager.paint(RepaintManager.java:1224)
	at javax.swing.JComponent._paintImmediately(JComponent.java:5072)
	at javax.swing.JComponent.paintImmediately(JComponent.java:4882)
	at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:785)
	at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:713)
	at javax.swing.RepaintManager.seqPaintDirtyRegions(RepaintManager.java:693)
	at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(SystemEventQueueUtilities.java:125)
	at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
and

Code: Select all

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
	at org.jfree.data.xy.XYSeriesCollection.getY(XYSeriesCollection.java:359)
	at org.jfree.data.xy.AbstractXYDataset.getYValue(AbstractXYDataset.java:92)
	at org.jfree.chart.renderer.xy.XYLineAndShapeRenderer.drawPrimaryLine(XYLineAndShapeRenderer.java:992)
	at org.jfree.chart.renderer.xy.XYLineAndShapeRenderer.drawItem(XYLineAndShapeRenderer.java:911)
	at org.jfree.chart.plot.XYPlot.render(XYPlot.java:3738)
	at org.jfree.chart.plot.XYPlot.draw(XYPlot.java:3310)
	at org.jfree.chart.JFreeChart.draw(JFreeChart.java:1235)
	at org.jfree.chart.ChartPanel.paintComponent(ChartPanel.java:1668)
	at javax.swing.JComponent.paint(JComponent.java:1029)
	at javax.swing.JComponent.paintToOffscreen(JComponent.java:5124)
	at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:278)
	at javax.swing.RepaintManager.paint(RepaintManager.java:1224)
	at javax.swing.JComponent._paintImmediately(JComponent.java:5072)
	at javax.swing.JComponent.paintImmediately(JComponent.java:4882)
	at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:785)
	at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:713)
	at javax.swing.RepaintManager.seqPaintDirtyRegions(RepaintManager.java:693)
	at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(SystemEventQueueUtilities.java:125)
	at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
So they are completely out of my hand and don't occur on direct action from my classes (well, they are repaints that are triggered internally/automatically, but I think they are coming more often when I am displaying new data and when there are more datasets displayed).

Any help would be appreciated. Although everything works fine, its kind of annoying. ;)

Cheers,
Stefan

paradoxoff
Posts: 1634
Joined: Sat Feb 17, 2007 1:51 pm

Re: Exception printout on fast changing series

Post by paradoxoff » Thu Oct 20, 2011 4:35 pm

Hi.
first, IndexOutOfBoundsException extends RuntimException, and thus doesn´t need to be explicitly treated by catch/throws clauses. If an unhandled exception occurs, its stack trace will simply be printed out. The stack trace that you are seeing are thus created by the JVM and not somewhere in JFreeChart or somewhere else.
Second: when you are applying a fast update rate, it is quiet likely that you might be clearing plotSeries1 while the EDT is accessing the plotSeries1 to plot some data and then all of sudden, in the middle of the loop, finds an empty series.
Solution: make sure that you are clearing and filling the plotSeries in the EDT, so that any rendering is stopped until this task is complete.
The fact you haven´t noticed that visually when 10, 20, 50 ... of 100 redraw requests per second crash. Obviously there is still a sufficient amount of successful rendering requests to create the impression that everything is ok.

brimborium
Posts: 23
Joined: Mon Dec 13, 2010 10:23 am
antibot: No, of course not.

Re: Exception printout on fast changing series

Post by brimborium » Thu Oct 20, 2011 10:46 pm

Thanks for the answer. :)
Ok, so your proposed solution would be to extend the EventDispatchThread and overwrite the run() method, where I should then clear and fill in the XYSeries?
Can you give me some advice how this Thread has to be embedded into the programm? (Somehow it has to be started...)

Thanks alot for your help.
Cheers, Stefan

paradoxoff
Posts: 1634
Joined: Sat Feb 17, 2007 1:51 pm

Re: Exception printout on fast changing series

Post by paradoxoff » Fri Oct 21, 2011 11:31 am

brimborium wrote: Ok, so your proposed solution would be to extend the EventDispatchThread and overwrite the run() method, where I should then clear and fill in the XYSeries?
No :shock:
Make your program implements the ActionListener interface, then add an instance as ActionListener to an instance of javax.swing.Timer. Perform the clearing and other changes to the series in the actionPerformed(ActionEvent e) method. Doing so, it is guaranteed that he actions will happen in the EDT and not interfere with the rendering.

brimborium
Posts: 23
Joined: Mon Dec 13, 2010 10:23 am
antibot: No, of course not.

Re: Exception printout on fast changing series

Post by brimborium » Mon Oct 24, 2011 9:30 am

Ok, so I have to do something like this:

Code: Select all

import javax.swing.Timer;

public class plotThingamajig implements ActionListener {

  // constructor, init and other stuff

  // start at init
  public void startTimer(){
    Timer fTimer = new Timer(updateFrequency, this);
    fTimer.start();
  }

  public void actionPerformed(ActionEvent e) {
    // update the XY Series here
  }
}
Ah, and this works because the Timer is controlled within the EDT and therefore it is guaranteed that the ActionListener call will not be at the same time as the rendering? But what happens, if the Timer fires during the rendering routine? Will it be postponed until the rendering finished?

paradoxoff
Posts: 1634
Joined: Sat Feb 17, 2007 1:51 pm

Re: Exception printout on fast changing series

Post by paradoxoff » Mon Oct 24, 2011 6:29 pm

brimborium wrote:But what happens, if the Timer fires during the rendering routine? Will it be postponed until the rendering finished?
Though I am not the right person to ask if you want to know the details of the Java threading and event handling concept, I would say yes. It is not only the rendering that should be finished but all others event that have been placed in the EventQueue.

brimborium
Posts: 23
Joined: Mon Dec 13, 2010 10:23 am
antibot: No, of course not.

Re: Exception printout on fast changing series

Post by brimborium » Mon Oct 24, 2011 6:40 pm

Well, it seems I have a gap in eduction on that topic... I'll have to change that as soon as there is some time.
Anyway, thanks a lot for your patient replies. I will report the results as soon as possible (my application has kind of grown since I startet this topic, so it will take some time to implement the changes).

brimborium
Posts: 23
Joined: Mon Dec 13, 2010 10:23 am
antibot: No, of course not.

Re: Exception printout on fast changing series

Post by brimborium » Wed Nov 30, 2011 1:11 pm

Ok, finally I got the time to look into this problem again. For future references, I will post the solution here. Thanks a lot for helping me out!
I managed to get rid of the exception printouts (and therefore the collisions in the EDT), by running all changes of the plotSeries like this:

Code: Select all

SwingUtilities.invokeLater(new Runnable() {
    @Override
    public void run() {
        // change your plotSeries here
    }
}
So the example in the initial post will look like this:

Code: Select all

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;

import javax.swing.JFrame;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

public class PlotDemo extends JFrame implements Runnable {
    private static final long serialVersionUID = 1L;
    private XYSeries plotSeries1, plotSeries2;
    
    public PlotDemo() {
        XYSeriesCollection data = new XYSeriesCollection();
        XYLineAndShapeRenderer r = new XYLineAndShapeRenderer();
        plotSeries1 = new XYSeries("demoSeries1");
        plotSeries2 = new XYSeries("demoSeries2");
        for(int i = 0; i < 500; i++) {
            plotSeries2.add(i, Math.sin(i / 50.0));
        }
        data.addSeries(plotSeries1);
        data.addSeries(plotSeries2);
        
        r.setSeriesStroke(0, new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
        r.setSeriesShape(0, new Rectangle(new Dimension(0, 0)));
        r.setSeriesPaint(0, Color.blue);
        r.setSeriesStroke(1, new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
        r.setSeriesShape(1, new Rectangle(new Dimension(0, 0)));
        r.setSeriesPaint(1, Color.red);
        
        JFreeChart chart = ChartFactory.createXYLineChart("", "", "",
        data, PlotOrientation.VERTICAL, false, true, false);
        XYPlot plot = chart.getXYPlot();
        plot.setRenderer(r);
        
        ValueAxis domain= new NumberAxis(), range = new NumberAxis();
        plot.setDomainAxis(domain);
        plot.setRangeAxis(range);
        
        ChartPanel phnPlotPanel = new ChartPanel(chart);
        add(phnPlotPanel, BorderLayout.CENTER);
        
        setVisible(true);
        setSize(new Dimension(400, 400));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    
    public static void main(String[] args) {
        PlotDemo main = new PlotDemo();
        new Thread(main).start();
    }
    
    @Override
    public void run() {
        for(int t = 0; t < 1000; t++) {
            SwingUtilities.invokeLater(new Runnable() { // <<<<< THIS IS NEW
                @Override
                public void run() {
                    plotSeries1.clear();
                    for(int i = 0; i < 500; i++) {
                        plotSeries1.add(i, Math.random() * Math.sin(i / 30.0), false);
                    }
                    plotSeries1.fireSeriesChanged();
                    sleepMs(10); 
                }
            });
        }
    }
    
    private void sleepMs(int ms) {
        try {
            Thread.sleep(ms);
        } catch(InterruptedException e) {}
    }
}

Locked