0

I am writing a program that checks prices and if it increases past a set amount then lets the user know by putting into a JavaFX text area line by line with a time stamp, appendText() spits out 2 errors. The error codes don't point to anything specific which is causing me to go in circles.

Most of the code works and if I use System.out.print() instead the code runs perfectly, but if I use txtContent.appendText() then at a random time in the first five minutes I will get one of the following errors.

Exception in thread "JavaFX Application Thread" java.lang.NullPointerException: Cannot read the array length because "this.lines" is null
    at javafx.graphics@19/com.sun.javafx.text.PrismTextLayout.layout(PrismTextLayout.java:1222)
    at javafx.graphics@19/com.sun.javafx.text.PrismTextLayout.ensureLayout(PrismTextLayout.java:223)
    at javafx.graphics@19/com.sun.javafx.text.PrismTextLayout.getBounds(PrismTextLayout.java:246)
    at javafx.graphics@19/javafx.scene.text.Text.getLogicalBounds(Text.java:432)
    at javafx.graphics@19/javafx.scene.text.Text.doComputeGeomBounds(Text.java:1187)
    at javafx.graphics@19/javafx.scene.text.Text$1.doComputeGeomBounds(Text.java:149)
    at javafx.graphics@19/com.sun.javafx.scene.shape.TextHelper.computeGeomBoundsImpl(TextHelper.java:90)
    at javafx.graphics@19/com.sun.javafx.scene.NodeHelper.computeGeomBounds(NodeHelper.java:117)
    at javafx.graphics@19/javafx.scene.Node.updateGeomBounds(Node.java:3825)
    at javafx.graphics@19/javafx.scene.Node.getGeomBounds(Node.java:3787)
    at javafx.graphics@19/javafx.scene.Node.getLocalBounds(Node.java:3735)
    at javafx.graphics@19/javafx.scene.Node$MiscProperties$3.computeBounds(Node.java:6825)
    at javafx.graphics@19/javafx.scene.Node$LazyBoundsProperty.get(Node.java:9840)
    at javafx.graphics@19/javafx.scene.Node$LazyBoundsProperty.get(Node.java:9810)
    at javafx.graphics@19/javafx.scene.Node.getBoundsInLocal(Node.java:3415)
    at javafx.controls@19/javafx.scene.control.skin.TextAreaSkin$ContentView.layoutChildren(TextAreaSkin.java:1324)
    at javafx.graphics@19/javafx.scene.Parent.layout(Parent.java:1207)
    at javafx.graphics@19/javafx.scene.Parent.layout(Parent.java:1214)
    at javafx.graphics@19/javafx.scene.Parent.layout(Parent.java:1214)
    at javafx.graphics@19/javafx.scene.Parent.layout(Parent.java:1214)
    at javafx.graphics@19/javafx.scene.Parent.layout(Parent.java:1214)
    at javafx.graphics@19/javafx.scene.Parent.layout(Parent.java:1214)
    at javafx.graphics@19/javafx.scene.Parent.layout(Parent.java:1214)
    at javafx.graphics@19/javafx.scene.Scene.doLayoutPass(Scene.java:592)
    at javafx.graphics@19/javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2512)
    at javafx.graphics@19/com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:407)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
    at javafx.graphics@19/com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:406)
    at javafx.graphics@19/com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:436)
    at javafx.graphics@19/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:575)
    at javafx.graphics@19/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:555)
    at javafx.graphics@19/com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:548)
    at javafx.graphics@19/com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:352)
    at javafx.graphics@19/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
    at javafx.graphics@19/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics@19/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)
    at java.base/java.lang.Thread.run(Thread.java:1589)

Exception in thread "JavaFX Application Thread" java.lang.IndexOutOfBoundsException: Index -1 out of bounds for length 2
    at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:100)
    at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:106)
    at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:302)
    at java.base/java.util.Objects.checkIndex(Objects.java:385)
    at java.base/java.util.ArrayList.get(ArrayList.java:427)
    at javafx.base@19/com.sun.javafx.collections.ObservableListWrapper.get(ObservableListWrapper.java:89)
    at javafx.base@19/com.sun.javafx.collections.VetoableListDecorator.get(VetoableListDecorator.java:305)
    at javafx.graphics@19/javafx.scene.Parent.updateCachedBounds(Parent.java:1704)
    at javafx.graphics@19/javafx.scene.Parent.recomputeBounds(Parent.java:1648)
    at javafx.graphics@19/javafx.scene.Parent.doComputeGeomBounds(Parent.java:1501)
    at javafx.graphics@19/javafx.scene.Parent$1.doComputeGeomBounds(Parent.java:115)
    at javafx.graphics@19/com.sun.javafx.scene.ParentHelper.computeGeomBoundsImpl(ParentHelper.java:84)
    at javafx.graphics@19/com.sun.javafx.scene.layout.RegionHelper.superComputeGeomBoundsImpl(RegionHelper.java:78)
    at javafx.graphics@19/com.sun.javafx.scene.layout.RegionHelper.superComputeGeomBounds(RegionHelper.java:62)
    at javafx.graphics@19/javafx.scene.layout.Region.doComputeGeomBounds(Region.java:3355)
    at javafx.graphics@19/javafx.scene.layout.Region$1.doComputeGeomBounds(Region.java:168)
    at javafx.graphics@19/com.sun.javafx.scene.layout.RegionHelper.computeGeomBoundsImpl(RegionHelper.java:89)
    at javafx.graphics@19/com.sun.javafx.scene.NodeHelper.computeGeomBounds(NodeHelper.java:117)
    at javafx.graphics@19/javafx.scene.Node.updateGeomBounds(Node.java:3825)
    at javafx.graphics@19/javafx.scene.Node.getGeomBounds(Node.java:3787)
    at javafx.graphics@19/javafx.scene.Node.getLocalBounds(Node.java:3735)
    at javafx.graphics@19/javafx.scene.Node.intersectsBounds(Node.java:5270)
    at javafx.graphics@19/javafx.scene.Node$1.intersectsBounds(Node.java:559)
    at javafx.graphics@19/com.sun.javafx.scene.NodeHelper.intersectsBounds(NodeHelper.java:264)
    at javafx.graphics@19/javafx.scene.layout.Region.doPickNodeLocal(Region.java:3224)
    at javafx.graphics@19/javafx.scene.layout.Region$1.doPickNodeLocal(Region.java:184)
    at javafx.graphics@19/com.sun.javafx.scene.layout.RegionHelper.pickNodeLocalImpl(RegionHelper.java:104)
    at javafx.graphics@19/com.sun.javafx.scene.NodeHelper.pickNodeLocal(NodeHelper.java:130)
    at javafx.graphics@19/javafx.scene.Node.pickNode(Node.java:5180)
    at javafx.graphics@19/javafx.scene.Scene$MouseHandler.pickNode(Scene.java:4037)
    at javafx.graphics@19/javafx.scene.Scene.pick(Scene.java:2067)
    at javafx.graphics@19/javafx.scene.Scene$MouseHandler.process(Scene.java:3847)
    at javafx.graphics@19/javafx.scene.Scene.processMouseEvent(Scene.java:1887)
    at javafx.graphics@19/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2620)
    at javafx.graphics@19/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:411)
    at javafx.graphics@19/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:301)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
    at javafx.graphics@19/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:450)
    at javafx.graphics@19/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:424)
    at javafx.graphics@19/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:449)
    at javafx.graphics@19/com.sun.glass.ui.View.handleMouseEvent(View.java:551)
    at javafx.graphics@19/com.sun.glass.ui.View.notifyMouse(View.java:937)
    at javafx.graphics@19/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics@19/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)
    at java.base/java.lang.Thread.run(Thread.java:1589)

Normally when I get errors like this I will follow errors to the part that I wrote something incorrect or made some mistake with my work. But these don't point to anything in particular other than my runLoop which is pretty simple.

try
        {
            Database.deleteLastRow();
            Database.updateRows();
                
            JSONAccess.createNewRow();
            
            if(increaseCounter)
            {
                rowCounter++;
            }
            
            if(rowCounter >= Main.calculatedRows)
            {
                if(testLine)
                {
                    System.out.println("Starting");
                    testLine = false;
                }
                increaseCounter = false;
                MainGUI.txtContent.appendText("\n New Section");
                Database.compareValues();
            }
        }
        catch (Exception e)
        {
            MainGUI.btnStart.setDisable(false);
            MainGUI.btnStop.setDisable(true);
            System.out.println("Test to see if something goes wrong");
        }
public static void compareValues() throws Exception
    {
        Class.forName("org.hsqldb.jdbc.JDBCDriver");
        
        for (String tableName : symbolsList)
        {
            String compareSql = "SELECT * FROM tbl_" + tableName + " ORDER BY id";
            prepStatement = connection.prepareStatement(compareSql);
            
            ResultSet rs = prepStatement.executeQuery();
            int rowNum = 1;
            while (rs.next())
            {
                if (rowNum == 1)
                {
                    dblNew = rs.getDouble("val");
                }
                if (rowNum == Main.calculatedRows)
                {
                    dblOld = rs.getDouble("val");
                }
                rowNum = Main.calculatedRows;
            }
            mathsCompare(tableName, dblNew, dblOld);
        }
    }
    
    public static void mathsCompare(String tableName1, double dblNew1, double dblOld1) throws Exception
    {
        if(dblNew1>dblOld1)
        {
            double dblChange = ((dblNew1-dblOld1)/dblOld1)*100;
            
            if(dblChange >= Main.dblPercentage)
            {
                MainGUI.txtContent.appendText("\n" + tableName1);
                System.out.print("\n" + tableName1);
            }
        }
    }

If I have both System.out.println and MainGUI.txtContent.appendText enabled then the console gets an extra set of values out before the error comes out but thats when I get stumped

Any help appreciated.

  • 6
    In JavaFX, stack traces like that, where none of your code shows up in the trace, is a strong indication of improper use of threads. If you're calling `appendText` on a background thread, then that is likely the root of the problem. Or at least the most prevalent symptom of a larger design issue. You must never interact with the UI on a background thread; only the _JavaFX Application Thread_ is allowed to touch a live scene graph. – Slaw Jan 13 '23 at 05:11
  • You are correct there is a ScheduledThread to keep it running every x seconds (determined by user) without running this the whole program freezes up due to starting as soon as it stops. Also I can't just make the program wait x seconds as part of it is an API call which can vary as large as 800 ms. Any suggestions? Thanks – MatleBonVon Jan 13 '23 at 05:18
  • 6
    Refer to [Concurrency in JavaFX](https://docs.oracle.com/javase/8/javafx/interoperability-tutorial/concurrency.htm#JFXIP546) and method [Platform.runLater](https://openjfx.io/javadoc/17/javafx.graphics/javafx/application/Platform.html#runLater(java.lang.Runnable)) – Abra Jan 13 '23 at 05:37
  • @MatleBonVon You're correct to put I/O work, or otherwise time-consuming work, on a background thread. The exact solution to your problem depends on what you're trying to do. But the general suggestion is to execute any and all code that updates the UI, and only that code, on the _JavaFX Application Thread_. Abra linked some good resources. – Slaw Jan 13 '23 at 07:31
  • 3
    That said, it seems to me like you're trying to append results to a `TextArea` as the task executes. If you don't mind displaying all the results at once, then the idea is to have a `Task` return e.g., a `List` of objects, then watch that task for completion (e.g., via its `onSucceeded` handler) and grab the value. Handlers such as `onSucceeded` are designed to execute on the FX thread regardless of which thread actually executes the task. Otherwise, as noted by Abra, look into `Platform#runLater(Runnable)`. Try not to overwhelm the FX thread with tasks. – Slaw Jan 13 '23 at 07:34
  • Possible [duplicate](https://stackoverflow.com/questions/59379148/how-to-communicate-between-a-thread-and-the-gui). – jewelsea Jan 13 '23 at 08:00
  • I’ll reopen this for now, incase somebody wants to provide a more specific answer than the marked duplicate. – jewelsea Jan 13 '23 at 08:01

0 Answers0