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.