0

I am working on a test project using JavaFX where there are two table view is being used. Each table has two column. And GUI is designed in FXML file. When running the app, it continuously updates the column data using different threads. But after some time of program execution, it gives NullPointerException as like below:

Exception in thread "Thread-4" java.lang.NullPointerException
    at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:349)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
    at javafx.beans.property.ReadOnlyObjectPropertyBase.fireValueChangedEvent(ReadOnlyObjectPropertyBase.java:74)
    at javafx.beans.property.ReadOnlyObjectWrapper.fireValueChangedEvent(ReadOnlyObjectWrapper.java:102)
    at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:112)
    at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:146)
    at javafx.scene.control.TableView$TableViewFocusModel.setFocusedCell(TableView.java:3233)
    at javafx.scene.control.TableView$TableViewFocusModel.focus(TableView.java:3278)
    at javafx.scene.control.TableView$TableViewFocusModel.updateDefaultFocus(TableView.java:3423)
    at javafx.scene.control.TableView$TableViewFocusModel.updateItemsObserver(TableView.java:3208)
    at javafx.scene.control.TableView$TableViewFocusModel.access$2200(TableView.java:3125)
    at javafx.scene.control.TableView$TableViewFocusModel$1.invalidated(TableView.java:3156)
    at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:349)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
    at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(ObjectPropertyBase.java:105)
    at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:112)
    at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:146)
    at javafx.scene.control.TableView.setItems(TableView.java:843)
    at com.example.jfx.FXMLDocumentController$1.lambda$run$0(FXMLDocumentController.java:95)
    at java.lang.Thread.run(Thread.java:745)

Just for reference, let me share the fxml file and the controller class java file.

FXMLDocument.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane id="AnchorPane" prefHeight="528.0" prefWidth="625.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="com.example.jfx.FXMLDocumentController">
   <children>
      <Label layoutX="14.0" layoutY="14.0" prefHeight="15.0" prefWidth="119.0" text="TableViewTestApp" />
      <HBox layoutY="35.0" prefHeight="493.0" prefWidth="625.0">
         <children>
            <TableView fx:id="tblCountryPopulation" prefHeight="493.0" prefWidth="292.0">
              <columns>
                <TableColumn fx:id="tcCountryName" prefWidth="105.0" text="CountryName" />
                <TableColumn fx:id="tcPopulation" prefWidth="83.0" text="Population" />
              </columns>
            </TableView>
            <Separator orientation="VERTICAL" prefHeight="493.0" prefWidth="46.0" />
            <TableView fx:id="tblCityPopulation" prefHeight="493.0" prefWidth="281.0">
              <columns>
                <TableColumn fx:id="tcCityName" text="CityName" />
                <TableColumn fx:id="tcCityPopulation" prefWidth="113.0" text="CityPopulation" />
              </columns>
            </TableView>
         </children>
      </HBox>
   </children>
</AnchorPane>

FXMLDocumentController.java

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.example.jfx;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;

/**
 *
 * @author srahman
 */
public class FXMLDocumentController implements Initializable {

    private Label label;
    @FXML
    private TableView<CountryModel> tblCountryPopulation;
    @FXML
    private TableColumn<CountryModel, String> tcCountryName;
    @FXML
    private TableColumn<CountryModel, Integer> tcPopulation;
    @FXML
    private TableView<CityModel> tblCityPopulation;
    @FXML
    private TableColumn<CityModel, String> tcCityName;
    @FXML
    private TableColumn<CityModel, Integer> tcCityPopulation;

    private boolean mCountryThreadStatus = true;
    private List<CountryModel> mCountryData = null;
    private ObservableList<CountryModel> mCountryObservableList = null;
    private List<CountryModel> mTempCountryData = null;
    private Thread mCountryTableThread = null;

    private boolean mCityThreadStatus = true;
    private List<CityModel> mCityData = null;
    private ObservableList<CityModel> mCityObservableList = null;
    private List<CityModel> mTempCityData = null;
    private Thread mCityTableThread = null;

    private void handleButtonAction(ActionEvent event) {
        System.out.println("You clicked me!");
        label.setText("Hello World!");
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO
        tcCountryName.setCellValueFactory(new PropertyValueFactory<CountryModel, String>("CountryName"));
        tcCountryName.setSortable(false);

        tcPopulation.setCellValueFactory(new PropertyValueFactory<CountryModel, Integer>("Population"));
        tcPopulation.setSortable(false);

        tcCityName.setCellValueFactory(new PropertyValueFactory<CityModel, String>("CityName"));
        tcCityName.setSortable(false);

        tcCityPopulation.setCellValueFactory(new PropertyValueFactory<CityModel, Integer>("Population"));
        tcCityPopulation.setSortable(false);

        UpdateCountryPopulation();
        UpdateCityPopulation();
    }

    public void UpdateCountryPopulation() {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                // run in a second
                final long timeInterval = 100;
                Runnable runnable = () -> {
                    while (mCountryThreadStatus) {
                        // ------- code for task to run
                        mCountryData = GetCountryData();
                        if (mCountryData != null && tblCountryPopulation != null) {
                            try {
                                synchronized (mCountryData) {
                                    mCountryObservableList = FXCollections.observableList(mCountryData);
                                    if (mCountryObservableList.size() > 0 && mCountryObservableList != null) {
                                        tblCountryPopulation.setItems(mCountryObservableList);
                                        tblCountryPopulation.refresh();
                                    }
                                }
                            } catch (Exception aEx) {
                                System.out.println("Exception happend while inserting item in view: " + aEx.getMessage());
                            }
                        }
                        if (mCountryData != null && mCountryObservableList != null) {
                            mCountryObservableList = null;
                            mCountryData = null;
                        }
                        // ------- ends here
                        try {
                            Thread.sleep(timeInterval);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                };

                if (mCountryThreadStatus) {
                    mCountryTableThread = new Thread(runnable);
                    mCountryTableThread.start();
                    System.out.println("Current thread ID: " + Thread.currentThread().getId());
                }
            }
        });
    }

    public List<CountryModel> GetCountryData() {
        Random lRandom = new Random();
        int lTemp = lRandom.nextInt(15 - 5 + 1) + 5;
        int lPopulation = lRandom.nextInt(9999) + 1;
        try {
            if (mTempCountryData == null) {
                mTempCountryData = new ArrayList<>();
                mTempCountryData.add(new CountryModel("Test" + lTemp, lPopulation));
            } else {
                CountryModel lCountryModel = FindInExistinData("Test" + lTemp);
                if (lCountryModel == null) {
                    mTempCountryData.add(new CountryModel("Test" + lTemp, lPopulation));
                } else {
                    lCountryModel.setCountryName("Test" + lTemp);
                    lCountryModel.setPopulation(lPopulation);
                }
            }
        } catch (Exception aEx) {
            System.out.println("Exception : " + aEx.getMessage());
        }
        return mTempCountryData;
    }

    private CountryModel FindInExistinData(String aCountry) {
        for (int j = 0; j < mTempCountryData.size(); j++) {
            if (mTempCountryData.get(j).getCountryName().equals(aCountry)) {
                return mTempCountryData.get(j);
            }
        }
        return null;
    }

    public void UpdateCityPopulation() {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                // run in a second
                final long timeInterval = 100;
                Runnable runnable = () -> {
                    while (mCityThreadStatus) {
                        // ------- code for task to run
                        mCityData = GetCityData();
                        if (mCityData != null && tblCityPopulation != null) {
                            try {
                                synchronized (mCityData) {
                                    mCityObservableList = FXCollections.observableList(mCityData);
                                    if (mCityObservableList.size() > 0 && mCityObservableList != null) {
                                        tblCityPopulation.setItems(mCityObservableList);
                                        tblCityPopulation.refresh();
                                    }
                                }
                            } catch (Exception aEx) {
                                System.out.println("Exception happend while inserting item in view: " + aEx.getMessage());
                            }
                        }
                        if (mCityData != null && mCityObservableList != null) {
                            mCityObservableList = null;
                            mCityData = null;
                        }
                        // ------- ends here
                        try {
                            Thread.sleep(timeInterval);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                };

                if (mCityThreadStatus) {
                    mCityTableThread = new Thread(runnable);
                    mCityTableThread.start();
                    System.out.println("Current thread ID: " + Thread.currentThread().getId());
                }
            }
        });
    }

    public List<CityModel> GetCityData() {
        Random lRandom = new Random();
        int lTemp = lRandom.nextInt(15 - 5 + 1) + 5;
        int lPopulation = lRandom.nextInt(9999) + 1;
        try {
            if (mTempCityData == null) {
                mTempCityData = new ArrayList<>();
                mTempCityData.add(new CityModel("Test" + lTemp, lPopulation));
            } else {
                CityModel lCityModel = FindInExistinCityData("Test" + lTemp);
                if (lCityModel == null) {
                    mTempCityData.add(new CityModel("Test" + lTemp, lPopulation));
                } else {
                    lCityModel.setCityName("Test" + lTemp);
                    lCityModel.setPopulation(lPopulation);
                }
            }
        } catch (Exception aEx) {
            System.out.println("Exception : " + aEx.getMessage());
        }
        return mTempCityData;
    }

    private CityModel FindInExistinCityData(String aCity) {
        for (int j = 0; j < mTempCityData.size(); j++) {
            if (mTempCityData.get(j).getCityName().equals(aCity)) {
                return mTempCityData.get(j);
            }
        }
        return null;
    }

    void CleanUpThread() {
        mCountryThreadStatus = false;
        mCityThreadStatus = false;
    }
}

It would be highly appreciated if there is any helping hand is available. Advance apology if a similar question is already there. Thanks.

meditat
  • 1,197
  • 14
  • 33
gsmaker
  • 533
  • 1
  • 4
  • 21
  • I would be very careful about updating the UI out side of the context of it's dispatching thread - Mayne start by having a look at [Concurrency in JavaFX](https://docs.oracle.com/javafx/2/threads/jfxpub-threads.htm) – MadProgrammer Dec 05 '17 at 07:28
  • As quoted from the above link *"The JavaFX scene graph, which represents the graphical user interface of a JavaFX application, is not thread-safe and can only be accessed and modified from the UI thread also known as the JavaFX Application thread"* – MadProgrammer Dec 05 '17 at 07:29
  • @MadProgrammer: That means I can't use `mCountryTableThread = new Thread(runnable);` like thread to update the GUI. Then how to update the TableView continuously? Would you please suggest. – gsmaker Dec 05 '17 at 07:43
  • No, it means you need to take care and ensure all updates which may affect the ui are done in the Dispatching thread - make sure you read the linked article – MadProgrammer Dec 05 '17 at 07:44
  • @MadProgrammer: Sorry for not having enough knowledge about JAVAFX. I don't understand that why NullPointerException is coming from `tblCountryPopulation.setItems(mCountryObservableList);` statement? Is the observable list null!! (In debug found that is not) – gsmaker Dec 05 '17 at 08:04
  • @gsmaker You can observe what is NULL by setting a breakpoint to capture NullPointerException, assuming you use IntelliJ but I imagine you can do the same in Netbeans or Eclipse. Not sure why you are creating Runnables and Threads within the `Platform.runLater()` this inefficient and I would re-work this – D-Dᴙum Dec 05 '17 at 08:20
  • @kerry: Not sure why, but without Runnables and Threads can't update the tableview data continuously. – gsmaker Dec 05 '17 at 08:50

0 Answers0