diff --git a/chat/pom.xml b/chat/pom.xml index c771398..01c19d0 100644 --- a/chat/pom.xml +++ b/chat/pom.xml @@ -64,6 +64,12 @@ sqlite-jdbc 3.48.0.0 + + org.slf4j + slf4j-api + 2.0.16 + + diff --git a/chat/src/main/java/module-info.java b/chat/src/main/java/module-info.java index 4fb2698..ce8c6ff 100644 --- a/chat/src/main/java/module-info.java +++ b/chat/src/main/java/module-info.java @@ -7,6 +7,7 @@ module rtgre.chat { requires net.synedra.validatorfx; requires org.json; requires java.sql; + requires org.xerial.sqlitejdbc; opens rtgre.chat to javafx.fxml; diff --git a/chat/src/main/java/rtgre/ChatLauncher.java b/chat/src/main/java/rtgre/ChatLauncher.java index c22c2aa..a3b515d 100644 --- a/chat/src/main/java/rtgre/ChatLauncher.java +++ b/chat/src/main/java/rtgre/ChatLauncher.java @@ -18,6 +18,8 @@ public class ChatLauncher { ChatApplication.main(args); } } catch (Exception e) { + System.out.println(e.getMessage()); + e.printStackTrace(); System.out.println("test2"); ChatApplication.main(args); } diff --git a/chat/src/main/java/rtgre/chat/ChatApplication.java b/chat/src/main/java/rtgre/chat/ChatApplication.java index 70e5dc9..ec92f49 100644 --- a/chat/src/main/java/rtgre/chat/ChatApplication.java +++ b/chat/src/main/java/rtgre/chat/ChatApplication.java @@ -6,23 +6,28 @@ import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.stage.Stage; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.util.Locale; -import java.util.Objects; -import java.util.ResourceBundle; +import java.nio.file.Files; +import java.util.*; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.Logger; public class ChatApplication extends Application { public static final Logger LOGGER = Logger.getLogger(ChatApplication.class.getCanonicalName()); - + private ChatController controller; + private Stage stage; static { try { InputStream is = ChatApplication.class .getResource("logging.properties").openStream(); LogManager.getLogManager().readConfiguration(is); + if (!Files.exists(new File("target/").toPath())) { + Files.createDirectory(new File("target").toPath()); + } } catch (Exception e) { LOGGER.log(Level.INFO, "Cannot read configuration file", e); } @@ -41,6 +46,44 @@ public class ChatApplication extends Application { stage.setMinHeight(400); stage.setScene(scene); stage.show(); + this.controller = fxmlLoader.getController(); + this.stage = stage; + + try { + Properties properties = new Properties(); + properties.load(getClass().getResourceAsStream("config.properties")); + if (!properties.getProperty("width").isEmpty() && !properties.getProperty("height").isEmpty()) { + stage.setWidth(Double.parseDouble(properties.getProperty("width"))); + stage.setHeight(Double.parseDouble(properties.getProperty("height"))); + } + if (properties.getProperty("posx").isEmpty() || properties.getProperty("height").isEmpty()) { + stage.centerOnScreen(); + } else { + stage.setX(Double.parseDouble(properties.getProperty("posx"))); + stage.setY(Double.parseDouble(properties.getProperty("posy"))); + } + } catch (IOException e) { + LOGGER.warning("Cannot load stage config!"); + } + } + + @Override + public void stop() { + try { + Properties properties = new Properties(); + properties.load(getClass().getResourceAsStream("config.properties")); + properties.setProperty("width", String.valueOf(stage.getWidth())); + properties.setProperty("height", String.valueOf(stage.getHeight())); + properties.setProperty("posx", String.valueOf(stage.getX())); + properties.setProperty("posy", String.valueOf(stage.getY())); + + properties.setProperty("split1", String.valueOf(controller.senderSplitPane.getDividerPositions()[0])); + properties.setProperty("split2", String.valueOf(controller.exchangeSplitPane.getDividerPositions()[0])); + LOGGER.finest(properties.toString()); + properties.store(new FileOutputStream(getClass().getResource("config.properties").getPath()), null); + } catch (IOException e) { + LOGGER.warning("Cannot store stage info in config!"); + } } public static void main(String[] args) { diff --git a/chat/src/main/java/rtgre/chat/ChatController.java b/chat/src/main/java/rtgre/chat/ChatController.java index a01087c..3de0de4 100644 --- a/chat/src/main/java/rtgre/chat/ChatController.java +++ b/chat/src/main/java/rtgre/chat/ChatController.java @@ -36,12 +36,12 @@ import rtgre.modeles.*; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.net.URISyntaxException; import java.net.URL; -import java.util.Date; -import java.util.Objects; -import java.util.ResourceBundle; +import java.util.*; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -69,6 +69,7 @@ public class ChatController implements Initializable { public Label statusLabel; public Label dateTimeLabel; public Contact contact; + public SplitPane senderSplitPane; private ContactMap contactMap = new ContactMap(); private ObservableList contactObservableList = FXCollections.observableArrayList(); private ObservableList postsObservableList = FXCollections.observableArrayList(); @@ -78,7 +79,8 @@ public class ChatController implements Initializable { private RoomMap roomMap = new RoomMap(); private ObservableList roomObservableList = FXCollections.observableArrayList(); private ResourceBundle i18nBundle; - + private Properties properties = new Properties(); + private Vector hostlist; @Override public void initialize(URL url, ResourceBundle resourceBundle) { @@ -87,14 +89,41 @@ public class ChatController implements Initializable { this.avatarImageView.setImage(image); this.i18nBundle = resourceBundle; + try { + InputStream in = ChatController.class.getResourceAsStream("config.properties"); + System.out.println(ChatController.class.getResource("config.properties").getPath()); + properties.load(in); + if (contact != null) { + this.contact.setAvatar(Contact.base64ToImage(properties.getProperty("avatar"))); + } + this.avatarImageView.setImage(SwingFXUtils.toFXImage(Contact.base64ToBufferedImage(properties.getProperty("avatar")), null)); + for (String host : (String[]) properties.getProperty("hosts").split(",")) { + host = host.replace("[", "").replace("]", "").replace(" ", ""); + hostComboBox.getItems().addAll(host); + } + hostComboBox.setValue(!properties.getProperty("lasthost").isEmpty() ? properties.getProperty("lasthost") : hostComboBox.getItems().get(0)); + loginTextField.setText(!properties.getProperty("login").isEmpty() ? properties.getProperty("login") : ""); + if (!properties.getProperty("split2").isEmpty()) { + exchangeSplitPane.setDividerPositions(Double.parseDouble(properties.getProperty("split2"))); + } + if (!properties.getProperty("split1").isEmpty()) { + exchangeSplitPane.setDividerPositions(Double.parseDouble(properties.getProperty("split2"))); + } + + } catch (IOException e) { + LOGGER.warning("Impossible de charger le fichier de configuration! Configuration par défaut chargée"); + System.out.println(e.getMessage()); + e.printStackTrace(); + } catch (NullPointerException e) { + LOGGER.warning("Impossible de charger le fichier de configuration! Configuration par défaut chargée"); + System.out.println(e.getMessage()); + e.printStackTrace(); + } Thread dateTimeLoop = new Thread(this::dateTimeLoop); dateTimeLoop.setDaemon(true); dateTimeLoop.start(); - hostComboBox.getItems().addAll("localhost:2024"); - hostComboBox.getItems().addAll("localhost:2025"); - hostComboBox.setValue("localhost:2024"); hostComboBox.setOnAction(this::statusNameUpdate); statusLabel.setText(i18nBundle.getString("disconnected")); @@ -141,6 +170,8 @@ public class ChatController implements Initializable { if (controller.isOk()) { hostComboBox.getItems().add(controller.hostTextField.getText()); hostComboBox.setValue(controller.hostTextField.getText()); + properties.setProperty("hosts", hostComboBox.getItems().toString()); + properties.store(new FileOutputStream(getClass().getResource("config.properties").getPath()), null); } } catch (IOException e) { LOGGER.warning("Impossible d'ouvrir la fenêtre de dialogue: fxml introuvable \n" + e.getMessage()); @@ -206,12 +237,21 @@ public class ChatController implements Initializable { File selectedFile = fileChooser.showOpenDialog(stage); if (selectedFile != null) { avatarImageView.setImage(new Image(selectedFile.toURI().toString())); - contact.setAvatar(ImageIO.read(selectedFile)); + if (contact != null) { + contact.setAvatar(ImageIO.read(selectedFile)); + } + properties.setProperty("avatar", Contact.imageToBase64(ImageIO.read(selectedFile))); + properties.store(new FileOutputStream(getClass().getResource("config.properties").getPath()), null); + } } catch (IOException e) { LOGGER.warning("Impossible de lire l'image!"); } - client.sendEvent(new rtgre.modeles.Event("CONT", this.contact.toJsonObject())); + try { + client.sendEvent(new rtgre.modeles.Event("CONT", this.contact.toJsonObject())); + } catch (Exception e) { + LOGGER.warning("Impossible d'envoyer l'évenement CONT! L'utilisateur est-il connecté?"); + } } @@ -243,6 +283,14 @@ public class ChatController implements Initializable { initPostListView(); this.statusLabel.setText("%s%s@%s:%s".formatted(i18nBundle.getString("connected"), this.contact.getLogin(), host, port)); this.connectionButton.setText(i18nBundle.getString("disconnect")); + + try { + properties.setProperty("login", loginTextField.getText()); + properties.store(new FileOutputStream(getClass().getResource("config.properties").getPath()), null); + } catch (Exception e) { + LOGGER.warning("Unable to store login in config!"); + } + } catch (Exception e) { new Alert(Alert.AlertType.ERROR, i18nBundle.getString("connectionError")).showAndWait(); connectionButton.setSelected(false); @@ -281,6 +329,13 @@ public class ChatController implements Initializable { private void statusNameUpdate(Event event) { statusLabel.setText("not connected to " + hostComboBox.getValue()); + + properties.setProperty("lasthost", hostComboBox.getValue()); + try { + properties.store(new FileOutputStream(getClass().getResource("config.properties").getPath()), null); + } catch (IOException e) { + LOGGER.warning("Unable to write last host to config!"); + } } diff --git a/chat/src/main/java/rtgre/modeles/Contact.java b/chat/src/main/java/rtgre/modeles/Contact.java index 063de9f..96932ad 100644 --- a/chat/src/main/java/rtgre/modeles/Contact.java +++ b/chat/src/main/java/rtgre/modeles/Contact.java @@ -176,4 +176,16 @@ public class Contact { return null; } + public static BufferedImage base64ToBufferedImage(String avatar64) { + byte[] bytes64 = Base64.getDecoder().decode(avatar64); + try { + BufferedImage image = ImageIO.read(new ByteArrayInputStream(bytes64)); + return image; + } catch (IOException e) { + LOGGER.severe("Impossible de convertir le base64 en image"); + } + return null; + } + + } diff --git a/chat/src/main/java/rtgre/modeles/DatabaseApi.java b/chat/src/main/java/rtgre/modeles/DatabaseApi.java index f93b5a9..0fd4ca6 100644 --- a/chat/src/main/java/rtgre/modeles/DatabaseApi.java +++ b/chat/src/main/java/rtgre/modeles/DatabaseApi.java @@ -4,6 +4,7 @@ import javax.xml.transform.Result; import java.io.File; import java.sql.*; import java.util.UUID; +import org.sqlite.JDBC; import static rtgre.chat.ChatApplication.LOGGER; @@ -13,7 +14,7 @@ public class DatabaseApi { public DatabaseApi() { try { - this.con = DriverManager.getConnection("jdbc:sqlite:chat/src/main/resources/rtgre/chat/dbase.db"); + this.con = DriverManager.getConnection("jdbc:sqlite:target/dbase.db"); this.stmt = con.createStatement(); initDB(con); LOGGER.info("Database connected!"); diff --git a/chat/src/main/java/rtgre/server/ChatServer.java b/chat/src/main/java/rtgre/server/ChatServer.java index 6345a62..230b249 100644 --- a/chat/src/main/java/rtgre/server/ChatServer.java +++ b/chat/src/main/java/rtgre/server/ChatServer.java @@ -16,13 +16,13 @@ import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.Logger; +import static rtgre.chat.ChatApplication.LOGGER; + /** * Programme serveur qui renvoie les chaines de caractères lues jusqu'à recevoir le message "fin" */ public class ChatServer { - private static final Logger LOGGER = Logger.getLogger(ChatServer.class.getCanonicalName()); - private Vector clientList; private PostVector postVector; private ContactMap contactMap; @@ -41,6 +41,11 @@ public class ChatServer { public static void main(String[] args) throws IOException { + try { + Class.forName("org.sqlite.JDBC"); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } ChatServer server = new ChatServer(2024); //daisyConnect(); server.acceptClients(); diff --git a/chat/src/main/resources/rtgre/chat/chat-view.fxml b/chat/src/main/resources/rtgre/chat/chat-view.fxml index 5db502c..3976f60 100644 --- a/chat/src/main/resources/rtgre/chat/chat-view.fxml +++ b/chat/src/main/resources/rtgre/chat/chat-view.fxml @@ -71,10 +71,10 @@ - + - + diff --git a/chat/src/main/resources/rtgre/chat/config.properties b/chat/src/main/resources/rtgre/chat/config.properties new file mode 100644 index 0000000..5268e92 --- /dev/null +++ b/chat/src/main/resources/rtgre/chat/config.properties @@ -0,0 +1,10 @@ +hosts=localhost:2024,localhost:2025 +avatar=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 +login= +width=600 +height=400 +posx= +posy= +lasthost= +split1= +split2= \ No newline at end of file