Threads collision in JavaFX Custom Console - java

I've made a Java/JavaFX console, and now I face with an exception: Console reports an Internal error.The error is: java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-5. The code of the Console:
package core.console;
import javafx.concurrent.Service;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.stage.Stage;
import java.io.*;
import java.util.ResourceBundle;
public class Console implements Runnable{
private Console(ResourceBundle resourceBundle) throws IOException {
FXMLLoader loader = new FXMLLoader(this.getClass().getResource("Console.fxml"), resourceBundle);
Parent root = (Parent) loader.load();
controller = loader.getController();
Scene scene = new Scene(root);
stage = new Stage();
stage.setScene(scene);
textArea = controller.getTextArea();
show();
PipedOutputStream pout=new PipedOutputStream(this.pin);
System.setOut(new PrintStream(pout,true));
PipedOutputStream pout2=new PipedOutputStream(this.pin2);
System.setErr(new PrintStream(pout2,true));
System.setIn(new PipedInputStream(this.pout3));
reader = new Thread(this);
reader.setDaemon(true);
reader.start();
reader2 = new Thread(this);
reader2.setDaemon(true);
reader2.start();
}
public static Console getInstance(ResourceBundle resourceBundle) {
if (console == null) {
try {
console = new Console(resourceBundle);
} catch (IOException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
return console;
}
public void show() {
stage.show();
}
#Override
public synchronized void run()
{
try
{
while (Thread.currentThread()==reader)
{
try {
this.wait(100);
} catch(InterruptedException ie) {}
if (pin.available()!= 0)
{
String input=this.readLine(pin);
controller.appendText(input);
}
if (quit) return;
}
while (Thread.currentThread()==reader2)
{
try {
this.wait(100);
} catch(InterruptedException ie) {}
if (pin2.available()!= 0)
{
String input = this.readLine(pin2);
controller.appendText(input);
}
if (quit) return;
}
} catch (Exception e)
{
controller.appendText("\nConsole reports an Internal error.");
controller.appendText("The error is: "+e);
}
}
private synchronized String readLine(PipedInputStream in) throws IOException
{
String input="";
do
{
int available=in.available();
if (available==0) break;
byte b[]=new byte[available];
in.read(b);
input=input+new String(b,0,b.length);
}while( !input.endsWith("\n") && !input.endsWith("\r\n") && !quit);
return input;
}
private static Console console = null;
private ConsoleController controller;
private Stage stage;
private TextArea textArea;
private Thread reader;
private Thread reader2;
private final PipedInputStream pin=new PipedInputStream();
private final PipedInputStream pin2=new PipedInputStream();
private final PipedOutputStream pout3=new PipedOutputStream();
}
When starting the application, the console gives me described above exception, but everthing works. But if an exception is generated by the application, the console doesn't show it and everything became locked. What did I do wrong?

JavaFX is a single-threaded toolkit. You should never query or update the UI from the background threads. So you need to wrap all the calls to the JFX classes with
Platform.runLater(new Runnable() {
#Override
public void run() {
// Update/Query the FX classes here
}
});
In your context, the code in the Console.run() method is executed off the JavaFX application thread, so it should not be directly modifying UI objects by invoking controller.appendText(). In your case all of the controller.appendText() calls should be wrapped in the Platform.runLater construct defined above.

Related

Calling a function on the start of a JavaFX project using SceneBuilder

I created a GUI for my project using SceneBuilder and I set up all the button ID's and created a controller for the FXML file. I want to have a live clock running in the text area on launch and throughout the program. This is my first time using FXML to create a project in JavaFX so I'm confused as to where I should place this code. Normally the code works in a simple program without FXML and it is this code:
package com.example;
import example;
public class Layout extends Application {
TextArea clock;
public void start(Stage stage) throws FileNotFoundException {
clock = new TextArea();
clock.setEditable(false);
BorderPane bp = new BorderPane();
bp.setTop(clock);
refreshClock();
Scene scene = new Scene(bp);
stage.setScene(scene);
stage.show();
}
}
private void refreshClock()
{
Thread refreshClock = new Thread()
{
public void run()
{
while (true)
{
Date dte = new Date();
String topMenuStr = " " + dte.toString();
clock.setText(topMenuStr);
try
{
sleep(3000L);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
} // end while ( true )
} // end run thread
};
refreshClock.start();
}
When I attempt to do it my current progress in my Ui controller class, nothing pops up in the Text Area despite what code I do and I'm not sure what to do next. Should this code be in my main .java file? Here is what I tried:
package application;
import java.util.Date;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.control.TextArea;
public class UiController {
#FXML
private TextArea clockTextArea;
private void refreshClock()
{
Thread refreshClock = new Thread()
{
public void run()
{
while (true)
{
Date dte = new Date();
String topMenuStr = " " + dte.toString();
clockTextArea.setText(topMenuStr);
try
{
sleep(3000L);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
} // end while ( true )
} // end run thread
};
refreshClock.start();
}
public void initialize() {
refreshClock();
}
in javafx you work with events in the initialize() method.
E.g. button.setOnAction(e -> System.Exit(0)); or clock.onMouseClicked(e -> System.out.println("Test"));

How can I print the java console in new window with JFrame? [duplicate]

This question already has answers here:
How to set output stream to TextArea
(4 answers)
Redirect Console Output to JavaFX TextArea?
(1 answer)
Closed 4 years ago.
I have been working on a project and in terms of functionality it works completely but I just have it printing into the console. I want to print it in a new window with JFrame so that I can make a jar file out of it and have it run without needing the ide. How can i add JFrame or another tool to existing code to make it print text in a new window?
Swing...
Is implemented in How to set output stream to TextArea
JavaFX
A "very basic" example of the above implementation.
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Date;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class Test extends Application {
#Override
public void start(Stage primaryStage) {
TextArea textArea = new TextArea();
PrintStream ps = System.out;
System.setOut(new PrintStream(new StreamCapturer("STDOUT", new Consumer() {
#Override
public void appendText(String text) {
Platform.runLater(new Runnable() {
#Override
public void run() {
textArea.appendText(text);
}
});
}
}, ps)));
BorderPane root = new BorderPane(textArea);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
Thread t = new Thread(new Runnable() {
#Override
public void run() {
System.out.println("Just going to output the current time");
try {
while (true) {
Thread.sleep(1000);
System.out.println(new Date());
}
} catch (InterruptedException ex) {
}
}
});
t.setDaemon(true);
t.start();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
public interface Consumer {
public void appendText(String text);
}
public class StreamCapturer extends OutputStream {
private StringBuilder buffer;
private String prefix;
private Consumer consumer;
private PrintStream old;
public StreamCapturer(String prefix, Consumer consumer, PrintStream old) {
this.prefix = prefix;
buffer = new StringBuilder(128);
buffer.append("[").append(prefix).append("] ");
this.old = old;
this.consumer = consumer;
}
#Override
public void write(int b) throws IOException {
char c = (char) b;
String value = Character.toString(c);
buffer.append(value);
if (value.equals("\n")) {
consumer.appendText(buffer.toString());
buffer.delete(0, buffer.length());
buffer.append("[").append(prefix).append("] ");
}
old.print(c);
}
}
}

How can I continously check to see if the text inside a text file has changed to empty? Java

I have the following code in which I want to read what is in the text file and display in textField2 weather it has changed or not... If it has, then I need it to continue. If it hasn't, then I need it to check again until the file has been changed to empty. Can anyone help? Here is my code:
package application;
import javafx.event.ActionEvent;
import javafx.scene.control.TextField;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.Parent;
import javafx.fxml.FXMLLoader;
import javafx.fxml.FXML;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
// BorderPane root = new BorderPane();
Parent root = FXMLLoader.load(getClass().
getResource("Root.fxml"));
Scene scene = new Scene(root,550,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
#FXML
private TextField textField;
#FXML
private TextField textField2;
#FXML
protected void onClick(ActionEvent event) throws IOException {
BufferedWriter writer = null;
try {
//create a temporary file
// This will output the full path where the file will be written to...
writer = new BufferedWriter(new FileWriter("C:/Users/Custom/Documents/Sample.txt"));
writer.write(textField.getText());
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// Close the writer regardless of what happens...
writer.close();
} catch (Exception e) {
}
while (readFile("C:/Users/Custom/Documents/Sample.txt") != ""){
textField2.setText("Waiting...");
}
textField2.setText("Done!");
}
}
String readFile(String fileName) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(fileName));
try {
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
sb.append("\n");
line = br.readLine();
}
return sb.toString();
} finally {
br.close();
}
}
}
you can check the time stamp of file to determine whether it has changed or not.
Sample code :
Long fileLastModified;
File file = new File("file");
if (!file.exists()) {
syserr
}
if (file.lastModified() <= this.fileLastModified) {
return oldValue;
}
this.fileLastModified = file.lastModified();
Also, you should use threads as commented by redflar3
Hi you can make a timer to check the file once every 2 seconds or so a snippet goes here
new java.util.Timer().schedule(
new java.util.TimerTask() {
#Override
public void run() {
//Read your file
if(fileEmpty){
//Update your textfiled
cancel(); //Stops this timer
}
}
},
0,2000);
once your task is done just use purge(); to clear out all cancelled timers
hope this helps

How to make custom Java/JavaFX console?

It's necessary to make a custom console. I have the following code:
public class Console extends OutputStream{
private Console(ResourceBundle resourceBundle) throws IOException {
FXMLLoader loader = new FXMLLoader(this.getClass().getResource("Console.fxml"), resourceBundle);
controller = loader.getController();
Scene scene = new Scene((Parent) loader.load());
stage = new Stage();
stage.setScene(scene);
show();
}
#Override
public void write(int b) throws IOException {
controller.append(b);
}
public static Console getInstance(ResourceBundle resourceBundle) {
if (console == null) {
try {
console = new Console(resourceBundle);
} catch (IOException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
return console;
}
public void show() {
stage.show();
}
private static Console console = null;
private ConsoleController controller;
private Stage stage;
}
controller file:
public class ConsoleController implements Initializable {
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
}
#FXML
public void append(int i) {
textArea.appendText(String.valueOf((char) i));
}
#FXML
private TextArea textArea;
}
And all these are called from the 'start()':
#Override
public void start(Stage primaryStage) throws IOException {
...
System.setErr(new PrintStream(Console.getInstance(rb)));
}
During the exception nothing is printed in the textArea of the Console.fxml, but the following exception is thrown:
Exception in thread "JavaFX Application Thread"
Exception: java.lang.NullPointerException thrown from the UncaughtExceptionHandler in thread "JavaFX Application Thread"
What am I doing wrong?
------EDIT--------
After understanding that it's necessary to use several threads I have the following code:
public class Console{
private Console(ResourceBundle resourceBundle) throws IOException {
FXMLLoader loader = new FXMLLoader(this.getClass().getResource("Console.fxml"), resourceBundle);
Parent root = (Parent) loader.load();
controller = loader.getController();
Scene scene = new Scene(root);
stage = new Stage();
stage.setScene(scene);
show();
if (errorOutputThread != null) {
errorOutputThread.interrupt();
try {
errorOutputThread.join();
} catch (InterruptedException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
errorOutputThread = null;
}
if (outOutputThread != null) {
outOutputThread.interrupt();
try {
outOutputThread.join();
} catch (InterruptedException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
outOutputThread = null;
}
System.err.flush();
System.out.flush();
outPipedInputStream = new PipedInputStream();
outPipedOutputStream = new PipedOutputStream(outPipedInputStream);
System.setOut(new PrintStream(outPipedOutputStream));
errorPipedInputStream = new PipedInputStream();
errorPipedOutputStream = new PipedOutputStream(errorPipedInputStream);
System.setErr(new PrintStream(errorPipedOutputStream));
outOutputThread = new Thread(new ConsoleStream(outPipedInputStream, "OUT"));
outOutputThread.setDaemon(true);
outOutputThread.start();
errorOutputThread = new Thread(new ConsoleStream(errorPipedInputStream, "ERROR"));
errorOutputThread.setDaemon(true);
errorOutputThread.start();
controller.appendText("Start Console");
}
public static Console getInstance(ResourceBundle resourceBundle) {
if (console == null) {
try {
console = new Console(resourceBundle);
} catch (IOException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
return console;
}
public void show() {
stage.show();
}
private class ConsoleStream implements Runnable {
private ConsoleStream(InputStream in, String type) {
inputStream = in;
this.type = type;
}
public void run() {
try {
InputStreamReader is = new InputStreamReader(inputStream);
BufferedReader br = new BufferedReader(is);
String read = null;
read = br.readLine();
while(read != null) {
controller.appendText(read + "\n");
read = br.readLine();
}
} catch (IOException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
controller.appendText("Thread" + type + "started");
}
private final InputStream inputStream;
private String type;
}
private static Console console = null;
private ConsoleController controller;
private Stage stage;
private PrintStream printStream;
private PipedOutputStream customPipedOutputStream;
private PipedOutputStream errorPipedOutputStream;
private PipedOutputStream outPipedOutputStream;
private PipedInputStream customPipedInputStream;
private PipedInputStream errorPipedInputStream;
private PipedInputStream outPipedInputStream;
private Thread customOutputThread;
private Thread outOutputThread;
private Thread errorOutputThread;
}
But the result is only: "Start Console", there are no results of controller.appendText("Thread" + type + "started"); so it seems that this threads don't start. But why?
I think you have to load the form before you can get the controller instance
FXMLLoader loader = new FXMLLoader(this.getClass().getResource("Console.fxml"),resourceBundle);
Parent p = (Parent) loader.load()
controller = loader.getController();
Scene scene = new Scene(p);

Java : UI thread not resuming in two-threaded application

I'm currently building a Java application using two threads :
The first thread is about the UI of the application, and also the processing of command received via the bluetooth thread.
The bluetooth thread is bluetooth server waiting for a robot to connect and handling communication.
As of now, the UI thread is in wait() state until the bluetooth thread gets a new message to process.
The problem is, I can trace the notify/notifyAll call from the bluetooth thread, but my UI is not resuming it's activity.
I am now sure I misunderstood something about the proper way to manage synchronized threads, but I can't figure out what's wrong in my software.
Here is the code for the UI :
package mapper;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;
public class MapperUI extends JFrame implements Runnable {
private ArrayList<String> messageArray;
public MapperUI(){
super();
build();
this.setVisible(true);
new Thread(this).start();
}
private void build(){
setTitle("SLAM Mapper");
setSize(600,500);
setLocationRelativeTo(null);
setResizable(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setContentPane(buildContentPane());
}
private JPanel buildContentPane(){
JPanel main = new JPanel();
main.setLayout(new BorderLayout());
//TODO Implements auto-generated map after bluetooth communication
MapPanel map = new MapPanel();
main.add(map,BorderLayout.CENTER);
//TODO This fields will be buildt with stored message
JTable positions = new JTable(15,2);
main.add(positions,BorderLayout.EAST);
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout());
JButton bouton = new JButton("Start");
buttonPanel.add(bouton);
JButton bouton2 = new JButton("Send");
buttonPanel.add(bouton2);
main.add(buttonPanel,BorderLayout.SOUTH);
return main;
}
public synchronized void run(){
MapperCom bt = new MapperCom();
while(true){
try {
System.out.println("Mapper is Waiting......");
wait();
String message = bt.getMessage();
this.messageArray.add(message);
bt.setNextCommand(processMessage(message));
notifyAll();
System.out.println("Mapper Notify");
build();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public String processMessage(String message){
String command = "";
//TODO Build a response
command = "fffff\n";
return command;
}
}
Here is the bluetoothService :
package mapper;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.UUID;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.microedition.io.StreamConnectionNotifier;
public class MapperCom extends Thread {
public final UUID uuid = new UUID(
"27012f0c68af4fbf8dbe6bbaf7aa432a", false);
public final String name = "Server";
public final String url = "btspp://localhost:" + uuid
+ ";name=" + name
+ ";authenticate=false;encrypt=false;";
private LocalDevice local ;
private StreamConnectionNotifier server ;
private StreamConnection conn ;
private DataInputStream din ;
private DataOutputStream dout ;
private String command;
private String message;
public MapperCom(){
try {
this.command = "";
this.message = "";
System.out.println("Setting device to be discoverable...");
local = LocalDevice.getLocalDevice();
local.setDiscoverable(DiscoveryAgent.GIAC);
System.out.println("Start advertising service...");
server = (StreamConnectionNotifier)Connector.open(url);
System.out.println("Waiting for incoming connection...\n");
conn = server.acceptAndOpen();
System.out.println("Client Connected...");
din = new DataInputStream(conn.openInputStream());
dout = new DataOutputStream(conn.openOutputStream());
new Thread(this).start();
} catch (Exception e) {
System.out.println("Exception Occured: " + e.toString());
}
}
#Override
public synchronized void run(){
System.out.println("Bluetooth Thread Started");
while(true){
try {
String cmd = "";
char c;
System.out.println("Waiting for message");
while (((c = din.readChar()) > 0) && (c!='\n') ){
System.out.println("Char received :"+c);
cmd = cmd + c;
}
storeMessage(cmd);
System.out.println("Bt Notify......");
notifyAll();
System.out.println("Bt is Waiting for a command from mapper......");
wait();
sendResponse();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void sendResponse(){
try {
dout.writeChars(command);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public synchronized String getMessage(){
return this.message;
}
public synchronized void storeMessage (String data){
this.message = data;
System.out.println("Received " + data);
}
public synchronized int setNextCommand (String data){
int result = -1;
//TODO Implement proper protocol
this.command = data;
System.out.println("Sending " + data);
return result;
}
}
I think when you call notifyAll(), it calls this.notifyAll(). Which there are no other elements waiting in the same class. So what you can do is you share the same object over both classes and call obj.wait() and obj.notifyAll(). It will work.
Below is one sample program. Basically there needs some common lock between 2 threads if you want communication using wait & notify.
package ravi.tutorial.java.threads;
public class TestThreads {
/**
* #param args
*/
public static void main(String[] args) {
CommonLock commonLock = new CommonLock();
Thread1 thread1 = new Thread1(commonLock);
Thread2 thread2 = new Thread2(commonLock);
thread1.start();
thread2.start();
}
}
/*
* Common monitor lock between both threads, used for communication using wait
* notify.
*/
class CommonLock {
}
// Extending Thread instead of Runnable as its just a test
class Thread1 extends Thread {
private CommonLock commonLock;
public Thread1(CommonLock commonLock) {
this.commonLock = commonLock;
}
public void run() {
System.out.println("Started thread 1");
System.out.println("waiting thread 1");
try {
// TO wait on commonLock, first need to get lock on commonLock. SO
// put synchronized block of commonLock.
synchronized (commonLock) {
commonLock.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("DONE waiting thread 1 as I got notification from THread 2");
}
}
class Thread2 extends Thread {
private CommonLock commonLock;
public Thread2(CommonLock commonLock) {
this.commonLock = commonLock;
}
public void run() {
System.out.println("Running thread 2");
try {
System.out.println("Sleeping thread 2");
// Just take gap of 2 sec before notifying.
Thread.sleep(2000);
// TO notify on commonLock, first need to get lock on commonLock. SO
// put synchronized block of commonLock.
synchronized (commonLock) {
System.out.println("Notifying thread 2");
commonLock.notifyAll();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Look at this: Thread tutorial
wait( ) tells the calling thread to give up the monitor and go to
sleep until some other thread enters the same monitor and calls
notify( ).
notify( ) wakes up the first thread that called wait( ) on
the same object.
notifyAll( ) wakes up all the threads that called wait( ) on the
same object. The highest priority thread will run first.
This is main misunderstanding of thread conception in Java.

Categories