How to make custom Java/JavaFX console? - java

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);

Related

Conflicts between JavaFX and Socket

I'm trying to set up a Server with a simple GUI where I print when a client make a connection and for its standard operations. The problem happens when I try to execute it, because the GUI doesn't start (even if NetBeans says that the program is executed) and I can't understand if there is a sort of conflict between the threads or if I called it in a wing way. This is the code where the program starts:
public class Server extends Application {
#Override
public void start(Stage stage) throws Exception {
FXMLLoader sLoader = new FXMLLoader(getClass().getResource("server.fxml"));
BorderPane root = new BorderPane(sLoader.load());
ServerController sController = sLoader.getController();
sController.initModel();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
This is the controller of the Server, I put here, in the init function, the part where the Socket waits for the client:
public class ServerController {
#FXML
private TextArea textarea;
public void initModel() {
try {
int i = 1;
ServerSocket s = new ServerSocket(5000);
while (true) {
Socket incoming = s.accept(); // si mette in attesa di richiesta di connessione e la apre
textarea.setText("In attesa di connessioni...");
Runnable r = new ThreadedEchoHandler(incoming, i);
new Thread(r).start();
i++;
}
} catch (IOException e) {
e.printStackTrace();
}
}
class ThreadedEchoHandler implements Runnable {
private Socket incoming;
private int counter;
/**
* Constructs a handler.
*
* #param i the incoming socket
* #param c the counter for the handlers (used in prompts)
*/
public ThreadedEchoHandler(Socket in, int c) {
incoming = in;
counter = c;
}
//Con questo gestisco le stringhe
public void run() {
try {
try {
InputStream inStream = incoming.getInputStream();
OutputStream outStream = incoming.getOutputStream();
Scanner in = new Scanner(inStream);
PrintWriter out = new PrintWriter(outStream, true);
//out.println("Hello! Enter BYE to exit.");
// echo client input
boolean done = false;
while (!done && in.hasNextLine()) {
String line = in.nextLine();
//out.println("Echo: " + line);
//System.out.println("ECHO: " + line);
if (line.trim().equals("BYE")) {
done = true;
}
}
} finally {
incoming.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

Why can't I use setText() under Button event?

So I am remaking my kinda complex console program to GUI. However, I am very unexperienced JavaFX user.
Label cityNameLabel = (Label) scene.lookup("#cityNameLabel");
cityNameLabel.setText("No text");
Button startButton = (Button) scene.lookup("#startButton");
Here I have my Label successfully initiated. At this moment I can still use cityNameLabel.setText(); and it is gonna work.
This continues:
startButton.setOnAction(e -> {
String enteredUrl = linkField.getText();
if(isValidUrl(enteredUrl)) {
try {
cityNameLabel.setText("Test");
doJob(enteredUrl, cityNameLabel);
} catch (IOException e1) {
e1.printStackTrace();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}.........
When I try to change the label here it won't happen. What I actually need to do is to reference the cityNamelLabel to doJob() method and I want to doJob() method to change it (once it finds the name of the city).
Can anyone give a me a solution that would allow me to do what I want to do? (changing it afterwards from doJob();). Thank you!
EDIT: Here I post my attemp of minimal version. I hope it is enough
package net.maty;
public class Katastr extends Application {
final String programName = "KatastRED";
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage mainStage) throws Exception {
try {
mainStage.setTitle(programName);
mainStage.setResizable(false);
BorderPane root = (BorderPane)FXMLLoader.load(getClass().getResource("Katastr.fxml"));
Scene scene = new Scene(root,800,500);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
ChoiceBox threadNumberBox = (ChoiceBox) scene.lookup("#threadNumberBox");
threadNumberBox.getSelectionModel().select(5);
TextField linkField = (TextField) scene.lookup("#linkField");
//debug
linkField.setText("https://regiony.kurzy.cz/katastr/stary-kolin/objekty?strana=");
//
Text cityNameText = (Text) scene.lookup("#cityNameText");
Button startButton = (Button) scene.lookup("#startButton");
//HERE IS THE THING. EVERYTHING WORKS WELL EXCEPT THE FACT I CANT CHANGE CITYNAMETEXT
startButton.setOnAction(e -> {
String enteredUrl = linkField.getText();
if(isValidUrl(enteredUrl)) {
try {
cityNameText.setText("Example that will not show because it doesnt work.");
doJob(enteredUrl, cityNameText);
} catch (IOException e1) {
e1.printStackTrace();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
else {
Alert alert = new Alert(AlertType.ERROR);
alert.setTitle("Chyba!");
alert.setHeaderText(null);
alert.setContentText("Chybně zadané URL!");
alert.showAndWait();
}
});
mainStage.setScene(scene);
mainStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void doJob(String validatedUrl, Text cityNameText) throws IOException, InterruptedException {
Scanner in = new Scanner(System.in);
String url = validatedUrl;
String cityName = getCityName(url);
cityNameText.setText(cityName);
List<String> parcelLinks = getParcelLinks(url);.....
}
public static List<String> getParcelLinks(String url) throws IOException{
}
public static List<String> getAdressesFromPage(Document doc) {
}
public static List<String> createParcelUrls(List<String> links) throws IOException {
}
public static boolean isValidUrl(String url) {
}
public static String getCityName(String url) throws IOException {
}
}

JavaFX SetOnCloseRequest() closing all screens

In my application I have multiple .fxml files. When I set a screen in my application, I also set the SetOnCloseRequest() method. For some screens is the code in that method just to switch to another screen. For example: When I have my Create Bank screen open and I press the close button, I want to switch back to my Manage Banks screen. But when I press the close button, it seems to switch the screens correctly, but very shortly after it closes the Manage Banks screen for some reason and the application dont stops running. So I cant do anything after that, because I don't have any GUI.
Does someone knows how to make sure that the Manage Banks screens does not close?
Here is my main class with theSetOnCloseRequest() method:
public class ClientMain extends Application {
public static String screenCreateBankId = "createBank";
public static String screenCreateBankFile = "Screens/createBank.fxml";
public static String screenCreateBankAccountId = "createBankAccount";
public static String screenCreateBankAccountFile = "Screens/createBankAccount.fxml";
public static String screenLoginId = "login";
public static String screenLoginFile = "Screens/login.fxml";
public static String screenManageBanksId = "manageBanks";
public static String screenManageBanksFile = "Screens/manageBanks.fxml";
private static Stage primaryStage;
private static ScreensController mainContainer;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
ClientMain.primaryStage = primaryStage;
mainContainer = new ScreensController();
mainContainer.loadScreen(ClientMain.screenCreateBankId, ClientMain.screenCreateBankFile);
mainContainer.loadScreen(ClientMain.screenLoginId, ClientMain.screenLoginFile);
mainContainer.loadScreen(ClientMain.screenManageBanksId, ClientMain.screenManageBanksFile);
mainContainer.setScreen(ClientMain.screenLoginId);
Group root = new Group();
root.getChildren().addAll(mainContainer);
primaryStage.setResizable(false);
primaryStage.getIcons().add(new Image("file:assets/ideal_logo.jpg"));
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void setProperties(String name){
primaryStage.sizeToScene();
primaryStage.centerOnScreen();
switch (name){
case "createBank":
primaryStage.setTitle("Create Bank");
primaryStage.setOnCloseRequest(event -> mainContainer.setScreen(screenManageBanksId));
break;
case "login":
primaryStage.setTitle("Login");
primaryStage.setOnCloseRequest(event -> System.exit(0));
break;
case "manageBanks":
primaryStage.setTitle("Manage Banks");
primaryStage.setOnCloseRequest(event -> mainContainer.getManageBanksController().logoutAdmin());
break;
}
}
}
And here is my ScreensController class:
public class ScreensController extends StackPane {
private HashMap<String, Node> screens = new HashMap<>();
private Client client;
private NewTransactionController newTransactionController;
private BankAccountController bankAccountController;
private ManageBanksController manageBanksController;
public Client getClient() {
return this.client;
}
public NewTransactionController getNewTransactionController() {
return this.newTransactionController;
}
public BankAccountController getBankAccountController() {
return this.bankAccountController;
}
public ManageBanksController getManageBanksController() {
return this.manageBanksController;
}
public ScreensController() {
try {
this.client = new Client();
System.out.println("Client: Client created");
} catch (RemoteException e) {
System.out.println("Client: Cannot create Client");
System.out.println("Client: RemoteException: " + e.getMessage());
System.exit(0);
}
}
public void addScreen(String name, Node screen) {
screens.put(name, screen);
}
public void loadScreen(String name, String resource) {
try {
FXMLLoader myLoader = new FXMLLoader(getClass().getResource(resource));
Parent loadScreen = myLoader.load();
IControllers myScreenController = myLoader.getController();
if (myScreenController instanceof NewTransactionController) {
this.newTransactionController = (NewTransactionController) myScreenController;
} else if (myScreenController instanceof BankAccountController) {
this.bankAccountController = (BankAccountController) myScreenController;
} else if (myScreenController instanceof ManageBanksController) {
this.manageBanksController = (ManageBanksController) myScreenController;
}
myScreenController.setScreenParent(this);
addScreen(name, loadScreen);
} catch (Exception e) {
e.printStackTrace();
}
}
public void setScreen(final String name) {
if (screens.get(name) != null) {
if (!getChildren().isEmpty()) {
getChildren().remove(0);
getChildren().add(0, screens.get(name));
ClientMain.setProperties(name);
} else {
//First time start up
getChildren().add(screens.get(name));
ClientMain.setProperties(name);
}
} else {
System.out.println("Screen hasn't been loaded!!!");
}
}
}
try this
primaryStage.setOnCloseRequest(event -> {
event.consume();
mainContainer.setScreen(screenManageBanksId);
});
...
primaryStage.setOnCloseRequest(event -> {
event.consume();
mainContainer.getManageBanksController().logoutAdmin();
});

Play a QuickTime video with JavaFX

I try to open a MOV video file with JavaFX:
Media media = new Media ("file:/tmp/file.MOV");
But it throws a MediaException: MEDIA_UNSUPPORTED.
However, if I simply change the file extension from .MOV to .MP4, it works perfectly, and the video is played with no error.
How can I force JavaFX to play my file without having to rename it?
Well that was a fun two hours. Here you go! Just make sure you have a zero byte mp4 file like in the example. It's pretty key in how it works.
public class TestFrame extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
StackPane root = new StackPane();
File actualFile = new File("shelter.mov");
File emptyfile = new File("blank.mp4");
Media media = new Media(emptyfile.toURI().toString());
copyData(media, actualFile);
MediaPlayer mediaPlayer = null;
try {
mediaPlayer = new MediaPlayer(media);
} catch (Exception e) {
e.printStackTrace();
}
mediaPlayer.setAutoPlay(true);
MediaView mediaView = new MediaView(mediaPlayer);
root.getChildren().add(mediaView);
Scene scene = new Scene(root, 720, 480);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
private void copyData(Media media, File f) {
try {
Field locatorField = media.getClass().getDeclaredField("jfxLocator");
// Inside block credits:
// http://stackoverflow.com/questions/3301635/change-private-static-final-field-using-java-reflection
{
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(locatorField, locatorField.getModifiers() & ~Modifier.FINAL);
locatorField.setAccessible(true);
}
CustomLocator customLocator = new CustomLocator(f.toURI());
customLocator.init();
customLocator.hack("video/mp4", 100000, f.toURI());
locatorField.set(media, customLocator);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
static class CustomLocator extends Locator {
public CustomLocator(URI uri) throws URISyntaxException {
super(uri);
}
#Override
public void init() {
try {
super.init();
} catch (Exception e) {
}
}
public void hack(String type, long length, URI uri){
this.contentType = type;
this.contentLength = length;
this.uri = uri;
this.cacheMedia();
}
}
}
How it works: Normally the default Locator throws an exception about the content type and it all ends there. Replacing the Media's locator with a custom one then manually setting the contentType, length, and uri gets it to play without a peep.

Threads collision in JavaFX Custom Console

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.

Categories