如何 return 从 inputStream 客户端到 Javafx 控制器的对象?
How to return objects from inputStream Client to a Javafx Controller?
所以基本上我有一个 MainConstroller class,它有每个按钮的方法。我还有一个服务器多客户端应用程序。在客户端中,我有一个方法 sendMessage,它将一个字符串和一个对象作为参数发送到 outputStreams 到服务器。
在相同的方法中,我有 2 个用于来自服务器的消息的 inputStream 和一个对象。问题是这个方法 运行s 在实现 运行 方法的线程上,我不能 return 对象。
我试图创建一个静态 class 来保存这些,但是当在 Controller class 中调用时,getter 为空 class。
实现此目标的最佳方法是什么?
public void onSaveButton(javafx.event.ActionEvent actionEvent) throws Exception {
Parent root = null;
Boolean type = false;
String message = null;
if (adminCheckbox.isSelected()) {
root = FXMLLoader.load(getClass().getResource("/fxml/admin.fxml"));
type = true;
message = "Admin";
}
if (competitorCheckbox.isSelected()) {
root = FXMLLoader.load(getClass().getResource("/fxml/competitor.fxml"));
message = "Competitor";
}
PersonEntity personEntity = new PersonEntity();
personEntity.setIdTeam(Integer.parseInt(teamField.getText()));
personEntity.setType(type);
personEntity.setUsername(usernameField.getText());
client.sendMessageToServer(message, personEntity);
System.out.println(Utils.getMessage());}
和客户端方法:
public void sendMessageToServer(String message, Object object) throws Exception {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Say something and the message will be sent to the server: ");
//For receiving and sending data
boolean isClose = false;
while (!isClose) {
try {
ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
if (message.equals("Bye")) {
isClose = true;
}
outputStream.writeObject(message);
outputStream.writeObject(object);
String messageFromServer = (String) inputStream.readObject();
//System.out.println(messageFromServer);
int index = messageFromServer.indexOf(' ');
String word = messageFromServer.substring(0, index);
if (messageFromServer.equals("Bye")) {
isClose = true;
}
if (!word.equals("LIST")) {
Object obj = (Object) inputStream.readObject();
Utils.setMessage(messageFromServer);
return;
//Utils.setObject(obj);
//System.out.println("IN FOR " + Utils.getMessage());
} else {
List<Object> list = (List<Object>) inputStream.readObject();
Utils.setMessage(messageFromServer);
Utils.setObjects(list);
return;
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
您可以使用 thread-safe ConcurrentLinkedQueue(当然是静态的)。 Utils.setObjects
会将每个元素添加到队列中。在 UI 线程上,您偶尔会轮询队列中要显示在 UI 中的新项目。
参考文档:https://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ConcurrentLinkedQueue.html
我不知道你的服务器是什么,也不知道你应用程序的其余部分。我知道您正在使用自定义协议使用原始 TCP 套接字进行通信。
我所做的是从 Java 教程中获取类似示例的代码。该代码是来自:Writing the Server Side of a Socket 的 KnockKnock 客户端服务器代码。基本服务器代码没有变化,我只是用 JavaFX UI.
替换了 console-based 客户端
未更改的服务器代码:
https://docs.oracle.com/javase/tutorial/networking/sockets/examples/KnockKnockProtocol.java
https://docs.oracle.com/javase/tutorial/networking/sockets/examples/KKMultiServer.java
https://docs.oracle.com/javase/tutorial/networking/sockets/examples/KKMultiServerThread.java
替换的控制台客户端:
我提供了两种不同的客户端实现;一个在 JavaFX 应用程序线程上进行同步客户端调用,另一个使用 JavaFX 任务进行异步客户端调用。
当前的异步 task-based 解决方案对于 full-scale 生产系统来说不够健壮,因为它在技术上可能会丢失消息,并且不能稳健地匹配请求和响应消息。但是对于像这样的演示来说还可以。
为了简单执行,UI 应用程序在 运行 时都在 pre-defined 端口上启动本地服务器,但您可以删除启动服务器的代码UI 应用程序和 运行 如果需要,可以从命令行访问服务器。
KnockKnockSyncClient.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class KnockKnockSyncClient {
private Socket socket;
private PrintWriter out;
private BufferedReader in;
public String connect(String host, int port) {
try {
socket = new Socket(host, port);
out = new PrintWriter(
socket.getOutputStream(),
true
);
in = new BufferedReader(
new InputStreamReader(
socket.getInputStream()
)
);
return in.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public String sendMessage(String request) {
String response = null;
try {
out.println(request);
response = in.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
public void disconnect() {
try {
if (socket != null) {
socket.close();
socket = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
KnockKnockSyncApp
import javafx.animation.FadeTransition;
import javafx.application.*;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class KnockKnockSyncApp extends Application {
private static final String HOST = "localhost";
private static final int PORT = 8809;
public static final String QUIT_RESPONSE = "Bye.";
private ExecutorService serverExecutor;
private KnockKnockSyncClient client;
private static final String CSS = """
data:text/css,
.root {
-fx-font-size: 20;
}
.list-cell {
-fx-alignment: baseline-right;
-fx-text-fill: purple;
}
.list-cell:odd {
-fx-alignment: baseline-left;
-fx-text-fill: darkgreen;
}
""";
@Override
public void init() {
serverExecutor = Executors.newSingleThreadExecutor(r -> new Thread(r, "KKServer"));
serverExecutor.submit(
() -> {
try {
KKMultiServer.main(new String[]{ PORT + "" });
} catch (Exception e) {
e.printStackTrace();
}
}
);
client = new KnockKnockSyncClient();
}
@Override
public void start(Stage stage) {
ListView<String> messageView = new ListView<>();
TextField inputField = new TextField();
inputField.setPromptText("Enter a message for the server.");
inputField.setOnAction(event -> {
String request = inputField.getText();
messageView.getItems().add(request);
String response = client.sendMessage(request);
messageView.getItems().add(response);
messageView.scrollTo(Math.max(0, messageView.getItems().size() - 1));
inputField.clear();
if (QUIT_RESPONSE.equals(response)) {
closeApp(inputField.getScene());
}
});
VBox layout = new VBox(10,
messageView,
inputField
);
layout.setPadding(new Insets(10));
layout.setPrefWidth(600);
Scene scene = new Scene(layout);
scene.getStylesheets().add(CSS);
stage.setScene(scene);
stage.show();
inputField.requestFocus();
String connectResponse = client.connect(HOST, PORT);
if (connectResponse != null) {
messageView.getItems().add(connectResponse);
}
}
private void closeApp(Scene scene) {
Parent root = scene.getRoot();
root.setDisable(true);
FadeTransition fade = new FadeTransition(Duration.seconds(1), root);
fade.setToValue(0);
fade.setOnFinished(e -> Platform.exit());
fade.play();
}
@Override
public void stop() {
client.disconnect();
serverExecutor.shutdown();
}
public static void main(String[] args) {
launch();
}
}
KnockKnockAsyncClient.java
import javafx.concurrent.Task;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
public class KnockKnockAsyncClient extends Task<Void> {
private final String host;
private final int port;
private final BlockingQueue<String> messageQueue = new LinkedBlockingDeque<>();
public KnockKnockAsyncClient(String host, int port) {
this.host = host;
this.port = port;
}
@Override
protected Void call() {
try (
Socket kkSocket = new Socket(host, port);
PrintWriter out = new PrintWriter(kkSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(kkSocket.getInputStream()));
) {
String fromServer;
String fromUser;
while ((fromServer = in.readLine()) != null) {
// this is not a completely robust implementation because updateMessage
// can coalesce responses and there is no matching in send message calls
// to responses. A more robust implementation may allow matching of
// requests and responses, perhaps via a message id. It could also
// replace the updateMessage call with storage of results in a collection
// (e.g. an observable list) with thread safe notifications of response
// arrivals, e.g. through CompleteableFutures and/or Platform.runLater calls.
updateMessage(fromServer);
System.out.println("Server: " + fromServer);
if (fromServer.equals("Bye."))
break;
fromUser = messageQueue.take();
System.out.println("Client: " + fromUser);
out.println(fromUser);
}
} catch (InterruptedException e) {
if (isCancelled()) {
updateMessage("Cancelled");
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public void sendMessage(String request) {
messageQueue.add(request);
}
}
KnockKnockAsyncApp.java
import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
public class KnockKnockAsyncApp extends Application {
private static final String HOST = "localhost";
private static final int PORT = 8809;
public static final String QUIT_RESPONSE = "Bye.";
private ExecutorService serverExecutor;
private ExecutorService clientExecutor;
private static final String CSS = """
data:text/css,
.root {
-fx-font-size: 20;
}
.list-cell {
-fx-alignment: baseline-right;
-fx-text-fill: purple;
}
.list-cell:odd {
-fx-alignment: baseline-left;
-fx-text-fill: darkgreen;
}
""";
@Override
public void init() {
serverExecutor = Executors.newSingleThreadExecutor(r -> new Thread(r, "KKServer"));
serverExecutor.submit(
() -> {
try {
KKMultiServer.main(new String[]{ PORT + "" });
} catch (Exception e) {
e.printStackTrace();
}
}
);
clientExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() {
final AtomicInteger threadNum = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
return new Thread(r,"KKClient-" + threadNum.getAndAdd(1));
}
});
}
@Override
public void start(Stage stage) {
ListView<String> messageView = new ListView<>();
KnockKnockAsyncClient client = new KnockKnockAsyncClient(HOST, PORT);
// monitor and action responses from the server.
client.messageProperty().addListener((observable, oldValue, response) -> {
if (response != null) {
logMessage(messageView, response);
}
if (QUIT_RESPONSE.equals(response)) {
closeApp(messageView.getScene());
}
});
TextField inputField = new TextField();
inputField.setPromptText("Enter a message for the server.");
inputField.setOnAction(event -> {
String request = inputField.getText();
logMessage(messageView, request);
client.sendMessage(request);
inputField.clear();
});
VBox layout = new VBox(10,
messageView,
inputField
);
layout.setPadding(new Insets(10));
layout.setPrefWidth(600);
Scene scene = new Scene(layout);
scene.getStylesheets().add(CSS);
stage.setScene(scene);
stage.show();
inputField.requestFocus();
clientExecutor.submit(client);
}
private void logMessage(ListView<String> messageView, String request) {
messageView.getItems().add(request);
messageView.scrollTo(Math.max(0, messageView.getItems().size() - 1));
}
private void closeApp(Scene scene) {
Parent root = scene.getRoot();
root.setDisable(true);
FadeTransition fade = new FadeTransition(Duration.seconds(1), root);
fade.setToValue(0);
fade.setOnFinished(e -> Platform.exit());
fade.play();
}
@Override
public void stop() {
clientExecutor.shutdown();
serverExecutor.shutdown();
}
public static void main(String[] args) {
launch();
}
}
所以基本上我有一个 MainConstroller class,它有每个按钮的方法。我还有一个服务器多客户端应用程序。在客户端中,我有一个方法 sendMessage,它将一个字符串和一个对象作为参数发送到 outputStreams 到服务器。
在相同的方法中,我有 2 个用于来自服务器的消息的 inputStream 和一个对象。问题是这个方法 运行s 在实现 运行 方法的线程上,我不能 return 对象。
我试图创建一个静态 class 来保存这些,但是当在 Controller class 中调用时,getter 为空 class。
实现此目标的最佳方法是什么?
public void onSaveButton(javafx.event.ActionEvent actionEvent) throws Exception {
Parent root = null;
Boolean type = false;
String message = null;
if (adminCheckbox.isSelected()) {
root = FXMLLoader.load(getClass().getResource("/fxml/admin.fxml"));
type = true;
message = "Admin";
}
if (competitorCheckbox.isSelected()) {
root = FXMLLoader.load(getClass().getResource("/fxml/competitor.fxml"));
message = "Competitor";
}
PersonEntity personEntity = new PersonEntity();
personEntity.setIdTeam(Integer.parseInt(teamField.getText()));
personEntity.setType(type);
personEntity.setUsername(usernameField.getText());
client.sendMessageToServer(message, personEntity);
System.out.println(Utils.getMessage());}
和客户端方法:
public void sendMessageToServer(String message, Object object) throws Exception {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Say something and the message will be sent to the server: ");
//For receiving and sending data
boolean isClose = false;
while (!isClose) {
try {
ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
if (message.equals("Bye")) {
isClose = true;
}
outputStream.writeObject(message);
outputStream.writeObject(object);
String messageFromServer = (String) inputStream.readObject();
//System.out.println(messageFromServer);
int index = messageFromServer.indexOf(' ');
String word = messageFromServer.substring(0, index);
if (messageFromServer.equals("Bye")) {
isClose = true;
}
if (!word.equals("LIST")) {
Object obj = (Object) inputStream.readObject();
Utils.setMessage(messageFromServer);
return;
//Utils.setObject(obj);
//System.out.println("IN FOR " + Utils.getMessage());
} else {
List<Object> list = (List<Object>) inputStream.readObject();
Utils.setMessage(messageFromServer);
Utils.setObjects(list);
return;
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
您可以使用 thread-safe ConcurrentLinkedQueue(当然是静态的)。 Utils.setObjects
会将每个元素添加到队列中。在 UI 线程上,您偶尔会轮询队列中要显示在 UI 中的新项目。
参考文档:https://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ConcurrentLinkedQueue.html
我不知道你的服务器是什么,也不知道你应用程序的其余部分。我知道您正在使用自定义协议使用原始 TCP 套接字进行通信。
我所做的是从 Java 教程中获取类似示例的代码。该代码是来自:Writing the Server Side of a Socket 的 KnockKnock 客户端服务器代码。基本服务器代码没有变化,我只是用 JavaFX UI.
替换了 console-based 客户端未更改的服务器代码:
https://docs.oracle.com/javase/tutorial/networking/sockets/examples/KnockKnockProtocol.java
https://docs.oracle.com/javase/tutorial/networking/sockets/examples/KKMultiServer.java
https://docs.oracle.com/javase/tutorial/networking/sockets/examples/KKMultiServerThread.java
替换的控制台客户端:
我提供了两种不同的客户端实现;一个在 JavaFX 应用程序线程上进行同步客户端调用,另一个使用 JavaFX 任务进行异步客户端调用。
当前的异步 task-based 解决方案对于 full-scale 生产系统来说不够健壮,因为它在技术上可能会丢失消息,并且不能稳健地匹配请求和响应消息。但是对于像这样的演示来说还可以。
为了简单执行,UI 应用程序在 运行 时都在 pre-defined 端口上启动本地服务器,但您可以删除启动服务器的代码UI 应用程序和 运行 如果需要,可以从命令行访问服务器。
KnockKnockSyncClient.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class KnockKnockSyncClient {
private Socket socket;
private PrintWriter out;
private BufferedReader in;
public String connect(String host, int port) {
try {
socket = new Socket(host, port);
out = new PrintWriter(
socket.getOutputStream(),
true
);
in = new BufferedReader(
new InputStreamReader(
socket.getInputStream()
)
);
return in.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public String sendMessage(String request) {
String response = null;
try {
out.println(request);
response = in.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
public void disconnect() {
try {
if (socket != null) {
socket.close();
socket = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
KnockKnockSyncApp
import javafx.animation.FadeTransition;
import javafx.application.*;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class KnockKnockSyncApp extends Application {
private static final String HOST = "localhost";
private static final int PORT = 8809;
public static final String QUIT_RESPONSE = "Bye.";
private ExecutorService serverExecutor;
private KnockKnockSyncClient client;
private static final String CSS = """
data:text/css,
.root {
-fx-font-size: 20;
}
.list-cell {
-fx-alignment: baseline-right;
-fx-text-fill: purple;
}
.list-cell:odd {
-fx-alignment: baseline-left;
-fx-text-fill: darkgreen;
}
""";
@Override
public void init() {
serverExecutor = Executors.newSingleThreadExecutor(r -> new Thread(r, "KKServer"));
serverExecutor.submit(
() -> {
try {
KKMultiServer.main(new String[]{ PORT + "" });
} catch (Exception e) {
e.printStackTrace();
}
}
);
client = new KnockKnockSyncClient();
}
@Override
public void start(Stage stage) {
ListView<String> messageView = new ListView<>();
TextField inputField = new TextField();
inputField.setPromptText("Enter a message for the server.");
inputField.setOnAction(event -> {
String request = inputField.getText();
messageView.getItems().add(request);
String response = client.sendMessage(request);
messageView.getItems().add(response);
messageView.scrollTo(Math.max(0, messageView.getItems().size() - 1));
inputField.clear();
if (QUIT_RESPONSE.equals(response)) {
closeApp(inputField.getScene());
}
});
VBox layout = new VBox(10,
messageView,
inputField
);
layout.setPadding(new Insets(10));
layout.setPrefWidth(600);
Scene scene = new Scene(layout);
scene.getStylesheets().add(CSS);
stage.setScene(scene);
stage.show();
inputField.requestFocus();
String connectResponse = client.connect(HOST, PORT);
if (connectResponse != null) {
messageView.getItems().add(connectResponse);
}
}
private void closeApp(Scene scene) {
Parent root = scene.getRoot();
root.setDisable(true);
FadeTransition fade = new FadeTransition(Duration.seconds(1), root);
fade.setToValue(0);
fade.setOnFinished(e -> Platform.exit());
fade.play();
}
@Override
public void stop() {
client.disconnect();
serverExecutor.shutdown();
}
public static void main(String[] args) {
launch();
}
}
KnockKnockAsyncClient.java
import javafx.concurrent.Task;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
public class KnockKnockAsyncClient extends Task<Void> {
private final String host;
private final int port;
private final BlockingQueue<String> messageQueue = new LinkedBlockingDeque<>();
public KnockKnockAsyncClient(String host, int port) {
this.host = host;
this.port = port;
}
@Override
protected Void call() {
try (
Socket kkSocket = new Socket(host, port);
PrintWriter out = new PrintWriter(kkSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(kkSocket.getInputStream()));
) {
String fromServer;
String fromUser;
while ((fromServer = in.readLine()) != null) {
// this is not a completely robust implementation because updateMessage
// can coalesce responses and there is no matching in send message calls
// to responses. A more robust implementation may allow matching of
// requests and responses, perhaps via a message id. It could also
// replace the updateMessage call with storage of results in a collection
// (e.g. an observable list) with thread safe notifications of response
// arrivals, e.g. through CompleteableFutures and/or Platform.runLater calls.
updateMessage(fromServer);
System.out.println("Server: " + fromServer);
if (fromServer.equals("Bye."))
break;
fromUser = messageQueue.take();
System.out.println("Client: " + fromUser);
out.println(fromUser);
}
} catch (InterruptedException e) {
if (isCancelled()) {
updateMessage("Cancelled");
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public void sendMessage(String request) {
messageQueue.add(request);
}
}
KnockKnockAsyncApp.java
import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
public class KnockKnockAsyncApp extends Application {
private static final String HOST = "localhost";
private static final int PORT = 8809;
public static final String QUIT_RESPONSE = "Bye.";
private ExecutorService serverExecutor;
private ExecutorService clientExecutor;
private static final String CSS = """
data:text/css,
.root {
-fx-font-size: 20;
}
.list-cell {
-fx-alignment: baseline-right;
-fx-text-fill: purple;
}
.list-cell:odd {
-fx-alignment: baseline-left;
-fx-text-fill: darkgreen;
}
""";
@Override
public void init() {
serverExecutor = Executors.newSingleThreadExecutor(r -> new Thread(r, "KKServer"));
serverExecutor.submit(
() -> {
try {
KKMultiServer.main(new String[]{ PORT + "" });
} catch (Exception e) {
e.printStackTrace();
}
}
);
clientExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() {
final AtomicInteger threadNum = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
return new Thread(r,"KKClient-" + threadNum.getAndAdd(1));
}
});
}
@Override
public void start(Stage stage) {
ListView<String> messageView = new ListView<>();
KnockKnockAsyncClient client = new KnockKnockAsyncClient(HOST, PORT);
// monitor and action responses from the server.
client.messageProperty().addListener((observable, oldValue, response) -> {
if (response != null) {
logMessage(messageView, response);
}
if (QUIT_RESPONSE.equals(response)) {
closeApp(messageView.getScene());
}
});
TextField inputField = new TextField();
inputField.setPromptText("Enter a message for the server.");
inputField.setOnAction(event -> {
String request = inputField.getText();
logMessage(messageView, request);
client.sendMessage(request);
inputField.clear();
});
VBox layout = new VBox(10,
messageView,
inputField
);
layout.setPadding(new Insets(10));
layout.setPrefWidth(600);
Scene scene = new Scene(layout);
scene.getStylesheets().add(CSS);
stage.setScene(scene);
stage.show();
inputField.requestFocus();
clientExecutor.submit(client);
}
private void logMessage(ListView<String> messageView, String request) {
messageView.getItems().add(request);
messageView.scrollTo(Math.max(0, messageView.getItems().size() - 1));
}
private void closeApp(Scene scene) {
Parent root = scene.getRoot();
root.setDisable(true);
FadeTransition fade = new FadeTransition(Duration.seconds(1), root);
fade.setToValue(0);
fade.setOnFinished(e -> Platform.exit());
fade.play();
}
@Override
public void stop() {
clientExecutor.shutdown();
serverExecutor.shutdown();
}
public static void main(String[] args) {
launch();
}
}