From 7ac2b05254a19a6b362bbda0e3fad479ced239be Mon Sep 17 00:00:00 2001 From: Ziedelth Date: Wed, 19 Jan 2022 10:22:50 +0100 Subject: [PATCH] New controller --- src/fr/univ/lyon1/client/Client.java | 14 +- src/fr/univ/lyon1/client/ClientReceive.java | 14 +- src/fr/univ/lyon1/gui/ClientGUI.java | 2 +- src/fr/univ/lyon1/gui/MainGui.java | 4 +- .../gui/controller/ApplicationController.java | 221 ++++++++++++++++++ .../gui/controller/ConnectGuiController.java | 8 +- .../gui/handlers/ApplicationHandler.java | 18 ++ .../handlers/ServerConfigurationHandler.java | 2 +- src/main/resources/message_gui.fxml | 33 +++ 9 files changed, 293 insertions(+), 23 deletions(-) create mode 100644 src/fr/univ/lyon1/gui/controller/ApplicationController.java create mode 100644 src/fr/univ/lyon1/gui/handlers/ApplicationHandler.java create mode 100644 src/main/resources/message_gui.fxml diff --git a/src/fr/univ/lyon1/client/Client.java b/src/fr/univ/lyon1/client/Client.java index 2590cc8..72248e9 100644 --- a/src/fr/univ/lyon1/client/Client.java +++ b/src/fr/univ/lyon1/client/Client.java @@ -27,7 +27,7 @@ public class Client { protected final Socket socket; protected final ObjectOutputStream out; private ObjectInputStream in; - private List channels = new ArrayList<>(); + private final List channels = new ArrayList<>(); protected boolean started = false; public Client(String address, int port, String username, String password) throws IOException { @@ -64,8 +64,7 @@ public class Client { System.exit(0); } - public String sendMessage(String content) { - + public void sendMessage(String content) { try { out.writeObject(new Command(CommandType.message, List.of(new Message(content, channels.get(0))))); out.flush(); @@ -73,8 +72,6 @@ public class Client { System.err.println("Fail to send message !"); e.printStackTrace(); } - - return content; } public void action(Object data) throws IOException { @@ -100,6 +97,7 @@ public class Client { private void commandLogin() throws IOException { out.writeObject(new Command(CommandType.list, null)); out.flush(); + out.writeObject(new Command(CommandType.join, List.of("general"))); out.flush(); } @@ -129,17 +127,13 @@ public class Client { Thread clientSendThread = new Thread(new ClientSend(this, out, socket)); clientSendThread.start(); - Thread clientReceiveThread = new Thread(new ClientReceive(this, socket)); + Thread clientReceiveThread = new Thread(new ClientReceive(this)); clientReceiveThread.start(); started = true; out.writeObject(new Command(CommandType.login, List.of(username, password))); out.flush(); - - clientSendThread.join(); - socket.close(); - clientReceiveThread.interrupt(); } public ObjectInputStream getIn() throws IOException { diff --git a/src/fr/univ/lyon1/client/ClientReceive.java b/src/fr/univ/lyon1/client/ClientReceive.java index 4f5aa9d..9f59465 100644 --- a/src/fr/univ/lyon1/client/ClientReceive.java +++ b/src/fr/univ/lyon1/client/ClientReceive.java @@ -2,21 +2,19 @@ package fr.univ.lyon1.client; import java.io.IOException; import java.io.ObjectInputStream; -import java.net.Socket; import java.net.SocketException; public class ClientReceive implements Runnable { private final Client client; - private ObjectInputStream in; - private final Socket socket; - public ClientReceive(Client client, Socket socket) { + public ClientReceive(Client client) { this.client = client; - this.socket = socket; } @Override public void run() { + ObjectInputStream in; + try { in = client.getIn(); } catch (IOException e) { @@ -26,6 +24,7 @@ public class ClientReceive implements Runnable { while(true) { Object data; + try { data = in.readObject(); } catch (ClassNotFoundException|IOException e) { @@ -33,13 +32,16 @@ public class ClientReceive implements Runnable { System.err.println("Connexion closed"); break; } + System.err.println("Fail to read object !"); e.printStackTrace(); + try { - Thread.sleep(1000); + Thread.currentThread().join(1000); } catch (InterruptedException ex) { break; } + continue; } diff --git a/src/fr/univ/lyon1/gui/ClientGUI.java b/src/fr/univ/lyon1/gui/ClientGUI.java index 84326d8..1f43b9d 100644 --- a/src/fr/univ/lyon1/gui/ClientGUI.java +++ b/src/fr/univ/lyon1/gui/ClientGUI.java @@ -27,7 +27,7 @@ public class ClientGUI extends Client { if (started) return; - Thread clientReceiveThread = new Thread(new ClientReceive(this, super.socket)); + Thread clientReceiveThread = new Thread(new ClientReceive(this)); clientReceiveThread.start(); out.writeObject(new Command(CommandType.login, List.of(this.username, this.password))); // ToDo: Setup login diff --git a/src/fr/univ/lyon1/gui/MainGui.java b/src/fr/univ/lyon1/gui/MainGui.java index cdf6c45..27320e7 100644 --- a/src/fr/univ/lyon1/gui/MainGui.java +++ b/src/fr/univ/lyon1/gui/MainGui.java @@ -1,6 +1,6 @@ package fr.univ.lyon1.gui; -import fr.univ.lyon1.gui.handlers.MainHandler; +import fr.univ.lyon1.gui.handlers.ApplicationHandler; import fr.univ.lyon1.gui.handlers.ServerConfigurationHandler; import javafx.application.Application; import javafx.application.Platform; @@ -12,7 +12,7 @@ public class MainGui extends Application { @Override public void start(Stage stage) { try { - new MainHandler().launch(stage); + new ApplicationHandler().launch(stage); } catch (IOException e) { System.out.println(e.getMessage()); diff --git a/src/fr/univ/lyon1/gui/controller/ApplicationController.java b/src/fr/univ/lyon1/gui/controller/ApplicationController.java new file mode 100644 index 0000000..dd3a3cb --- /dev/null +++ b/src/fr/univ/lyon1/gui/controller/ApplicationController.java @@ -0,0 +1,221 @@ +package fr.univ.lyon1.gui.controller; + +import fr.univ.lyon1.common.Channel; +import fr.univ.lyon1.common.ChatSSL; +import fr.univ.lyon1.common.Message; +import fr.univ.lyon1.common.ServerConfiguration; +import fr.univ.lyon1.common.command.Command; +import fr.univ.lyon1.common.command.CommandType; +import fr.univ.lyon1.common.exception.ChatException; +import fr.univ.lyon1.common.exception.UnknownCommand; +import javafx.application.Platform; +import javafx.fxml.FXML; +import javafx.scene.control.TextArea; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.scene.text.Text; +import javafx.scene.text.TextFlow; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSocket; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.net.Socket; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ApplicationController { + @NotNull + private final List channels = new ArrayList<>(); + @Nullable + private Channel currentChannel = null; + + private boolean isRunning = true; + @NotNull + private final Socket socket; + @NotNull + private final ObjectOutputStream objectOutputStream; + @NotNull + private final ObjectInputStream objectInputStream; + + @FXML + public TextArea textSendArea; + @FXML + public TextFlow textReceiveArea; + @FXML + public VBox vboxChannelsList; + + public ApplicationController() throws IOException { + System.out.println("Loading configuration..."); + @NotNull + final ServerConfiguration serverConfiguration = ServerConfiguration.load(); + + System.out.println("Init socket..."); + this.socket = this.initSSLSocket(serverConfiguration.getAddress(), serverConfiguration.getPort()); + this.objectOutputStream = new ObjectOutputStream(socket.getOutputStream()); + this.objectInputStream = new ObjectInputStream(socket.getInputStream()); + + @NotNull + final Thread receiveObjectThread = new Thread(() -> { + while (this.isRunning) { + try { + @Nullable + final Object object = this.objectInputStream.readObject(); + + if (object != null) { + if (object instanceof Command) { + switch (((Command) object).getType()) { + // If client receive login type message + case login -> { + // Request all list of users + this.sendObject(new Command(CommandType.list, null)); + // TODO: List all channels available + // Join a channel + this.sendObject(new Command(CommandType.join, List.of("general"))); + } + + // If client receive a message type + case message -> { + @NotNull + final Message message = (Message) ((Command) object).getArgs().get(0); + System.out.println("Message received : " + message.toString()); + + // If same channel + if (this.currentChannel != null && message.getChannel().getUUID().equals(this.currentChannel.getUUID())) { + Platform.runLater(() -> this.textReceiveArea.getChildren().add(new Text("@" + message.getSender().getUsername() + ": " + message.getContent()))); + } + } + + // If client receive all users + case list -> { + System.out.println("Connected users : " + Arrays.toString(((Command) object).getArgs().toArray())); + } + + // If client receive a join type message + case join -> { + @NotNull + final Channel channel = (Channel) ((Command) object).getArgs().get(0); + boolean ifChannelExists = this.channels.stream().anyMatch(fChannel -> fChannel.getName().equalsIgnoreCase(channel.getName())); + System.out.println("Channel exists : " + ifChannelExists); + this.currentChannel = channel; + this.channels.add(channel); + System.out.println("Successfully connected to : " + this.currentChannel.toString()); + + if (!ifChannelExists) { + @NotNull + final Text text = new Text("- #" + channel.getName()); + text.setFill(Color.RED); + + text.setOnMousePressed(event -> { + System.out.println("Selected channel : #" + channel.getName()); + + try { + this.sendObject(new Command(CommandType.join, List.of(channel.getName()))); + } catch (IOException e) { + e.printStackTrace(); + } + }); + + Platform.runLater(() -> this.vboxChannelsList.getChildren().add(text)); + } else { + this.vboxChannelsList.getChildren().stream().filter(node -> node instanceof Text).forEach(txt -> ((Text) txt).setFill(Color.BLACK)); + + @NotNull + final Text text = (Text) this.vboxChannelsList.getChildren().stream().filter(node -> node instanceof Text && ((Text) node).getText().equalsIgnoreCase("- #" + channel.getName())).toList().get(0); + text.setFill(Color.RED); + } + } + } + } else if (object instanceof ChatException) { + ((ChatException) object).printStackTrace(); + } else + this.sendObject(new UnknownCommand()); + } else { + try { + this.closeSocket(); + } catch (IOException ex) { + System.out.println("Can not close connection"); + ex.printStackTrace(); + } + } + } catch (IOException | ClassNotFoundException e) { + if (e instanceof SocketException) { + try { + this.closeSocket(); + } catch (IOException ex) { + System.out.println("Can not close connection"); + ex.printStackTrace(); + } + } + + e.printStackTrace(); + } + } + }); + + receiveObjectThread.start(); + + System.out.println("Login..."); + this.sendObject(new Command(CommandType.login, List.of(serverConfiguration.getPseudo(), serverConfiguration.getPassword()))); + } + + private void closeSocket() throws IOException { + this.isRunning = false; + this.objectOutputStream.close(); + this.objectInputStream.close(); + this.socket.close(); + } + + @NotNull + private Socket initSSLSocket(@NotNull String address, int port) throws IOException { + SSLContext ctx = ChatSSL.getSSLContext(); + SocketFactory factory = ctx.getSocketFactory(); + Socket connection = factory.createSocket(address, port); + ((SSLSocket) connection).setEnabledProtocols(new String[] {ChatSSL.tlsVersion}); + SSLParameters sslParams = new SSLParameters(); + sslParams.setEndpointIdentificationAlgorithm("HTTPS"); + ((SSLSocket) connection).setSSLParameters(sslParams); + return connection; + } + + private void sendObject(@NotNull Object object) throws IOException { + this.objectOutputStream.writeObject(object); + this.objectOutputStream.flush(); + } + + public void keyPressedOnTextSendArea(KeyEvent keyEvent) { + if (keyEvent.getCode() == KeyCode.ENTER) { + this.send(); + } + } + + public void send() { + @NotNull + final String textToSend = this.textSendArea.getText(); + this.clear(); + System.out.print(">> Send : " + textToSend); + Platform.runLater(() -> this.textReceiveArea.getChildren().add(new Text("*>> " + textToSend))); + + if (this.currentChannel != null) { + try { + this.sendObject(new Command(CommandType.message, List.of(new Message(textToSend, this.currentChannel)))); + } catch (IOException e) { + System.out.println("Can not send message to server : " + e.getMessage()); + e.printStackTrace(); + } + } + } + + public void clear() { + this.textSendArea.setText(""); + } +} \ No newline at end of file diff --git a/src/fr/univ/lyon1/gui/controller/ConnectGuiController.java b/src/fr/univ/lyon1/gui/controller/ConnectGuiController.java index 262df8b..59d836b 100644 --- a/src/fr/univ/lyon1/gui/controller/ConnectGuiController.java +++ b/src/fr/univ/lyon1/gui/controller/ConnectGuiController.java @@ -1,7 +1,7 @@ package fr.univ.lyon1.gui.controller; import fr.univ.lyon1.common.ServerConfiguration; -import fr.univ.lyon1.gui.handlers.MainHandler; +import fr.univ.lyon1.gui.handlers.ApplicationHandler; import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.control.TextField; @@ -58,10 +58,12 @@ public class ConnectGuiController { System.out.println("File saved, next step..."); System.out.println("Get scene..."); + + // Change scene + @NotNull final Stage stage = (Stage) this.connectButton.getScene().getWindow(); - System.out.println("Scene : " + stage.toString()); - new MainHandler().launch(stage); + new ApplicationHandler().launch(stage); } catch (IOException e) { System.out.println(e.getMessage()); diff --git a/src/fr/univ/lyon1/gui/handlers/ApplicationHandler.java b/src/fr/univ/lyon1/gui/handlers/ApplicationHandler.java new file mode 100644 index 0000000..173d929 --- /dev/null +++ b/src/fr/univ/lyon1/gui/handlers/ApplicationHandler.java @@ -0,0 +1,18 @@ +package fr.univ.lyon1.gui.handlers; + +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.stage.Stage; + +import java.io.IOException; + +public class ApplicationHandler implements Handler { + @Override + public void launch(Stage stage) throws IOException { + FXMLLoader fxmlLoader = new FXMLLoader(ClassLoader.getSystemClassLoader().getResource("message_gui.fxml")); + Scene scene = new Scene(fxmlLoader.load(), 600, 400); + stage.setTitle("Chat client"); + stage.setScene(scene); + stage.show(); + } +} diff --git a/src/fr/univ/lyon1/gui/handlers/ServerConfigurationHandler.java b/src/fr/univ/lyon1/gui/handlers/ServerConfigurationHandler.java index 5c4211d..bc70d2c 100644 --- a/src/fr/univ/lyon1/gui/handlers/ServerConfigurationHandler.java +++ b/src/fr/univ/lyon1/gui/handlers/ServerConfigurationHandler.java @@ -10,7 +10,7 @@ public class ServerConfigurationHandler implements Handler { @Override public void launch(Stage stage) throws IOException { FXMLLoader fxmlLoader = new FXMLLoader(ClassLoader.getSystemClassLoader().getResource("connect_gui.fxml")); - Scene scene = new Scene(fxmlLoader.load(), 320, 240); + Scene scene = new Scene(fxmlLoader.load(), 640, 480); stage.setTitle("Configuration du serveur"); stage.setScene(scene); stage.show(); diff --git a/src/main/resources/message_gui.fxml b/src/main/resources/message_gui.fxml new file mode 100644 index 0000000..feaee24 --- /dev/null +++ b/src/main/resources/message_gui.fxml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + +