mirror of
https://github.com/Akomry/sae302_applicom.git
synced 2025-12-06 08:43:54 +00:00
Merge branch 'dev-doc' into 'dev'
doc: ajout de la javadoc sur toutes les classes See merge request iut_rt/but2/sae302-applicom/bouclyma!16
This commit is contained in:
commit
ddba5f8c39
21 changed files with 920 additions and 93 deletions
|
|
@ -5,7 +5,14 @@ import rtgre.server.ChatServer;
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Application pour lancer soit ChatServer, soit ChatApplication
|
||||
*/
|
||||
public class ChatLauncher {
|
||||
/**
|
||||
* Avec le paramètre "server" sur la ligne de commande, ChatServer, sinon lance ChatApplication
|
||||
* @param args Paramètres de la ligne de commande
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
for (String arg : args) {
|
||||
System.out.println(arg);
|
||||
|
|
|
|||
|
|
@ -16,9 +16,15 @@ import java.util.logging.Level;
|
|||
import java.util.logging.LogManager;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Application graphique de chat
|
||||
*/
|
||||
public class ChatApplication extends Application {
|
||||
/** Logger de l'application graphique */
|
||||
public static final Logger LOGGER = Logger.getLogger(ChatApplication.class.getCanonicalName());
|
||||
/** Controller de l'application de chat */
|
||||
private ChatController controller;
|
||||
/** Stage principal */
|
||||
private Stage stage;
|
||||
static {
|
||||
try {
|
||||
|
|
@ -33,6 +39,11 @@ public class ChatApplication extends Application {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lancement de l'application
|
||||
* @param stage La scène graphique
|
||||
* @throws IOException En cas de problème d'accès aux ressources
|
||||
*/
|
||||
@Override
|
||||
public void start(Stage stage) throws IOException {
|
||||
ResourceBundle i18nBundle = ResourceBundle.getBundle("rtgre.chat.i18nBundle",
|
||||
|
|
@ -67,6 +78,9 @@ public class ChatApplication extends Application {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fermeture de l'application
|
||||
*/
|
||||
@Override
|
||||
public void stop() {
|
||||
try {
|
||||
|
|
@ -86,6 +100,10 @@ public class ChatApplication extends Application {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Programme principal
|
||||
* @param args Les arguments du programme principal
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
launch();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,45 +49,84 @@ import java.util.regex.Pattern;
|
|||
|
||||
import static rtgre.chat.ChatApplication.LOGGER;
|
||||
|
||||
/**
|
||||
* Controller de l'application de chat
|
||||
*
|
||||
*/
|
||||
public class ChatController implements Initializable {
|
||||
|
||||
/** Pattern associé à une regex pour vérifier si le login est correct */
|
||||
private static final Pattern LOGIN_PATTERN = Pattern.compile("^([a-z][a-z0-9]{2,7})$");
|
||||
private static final Pattern HOST_PATTERN = Pattern.compile("/^[a-z]*((\\:?)\\d{1,5})?$/gm");
|
||||
/** Pattern associé à une regex pour récupérer le nom d'hôte et du port à partir d'un socket */
|
||||
private final Pattern hostPortPattern = Pattern.compile("^([-.a-zA-Z0-9]+)(?::([0-9]{1,5}))?$");
|
||||
/** Menu `Add Host` */
|
||||
public MenuItem hostAddMenuItem;
|
||||
/** Menu `Change avatar` */
|
||||
public MenuItem avatarMenuItem;
|
||||
/** Menu `About` */
|
||||
public MenuItem aboutMenuItem;
|
||||
/** Liste déroulante des serveurs disponibles */
|
||||
public ComboBox<String> hostComboBox;
|
||||
/** Zone de saisie du login */
|
||||
public TextField loginTextField;
|
||||
/** Bouton de connexion */
|
||||
public ToggleButton connectionButton;
|
||||
/** ImageView de l'avatar */
|
||||
public ImageView avatarImageView;
|
||||
/** Séparateur des messages/destainataires */
|
||||
public SplitPane exchangeSplitPane;
|
||||
/** Vue des messages */
|
||||
public ListView<Post> postListView;
|
||||
/** Vue des salons */
|
||||
public ListView<Room> roomsListView;
|
||||
/** Vue des contacts */
|
||||
public ListView<Contact> contactsListView;
|
||||
/** Zone de saisie du message */
|
||||
public TextField messageTextField;
|
||||
/** Bouton d'envoi du message */
|
||||
public Button sendButton;
|
||||
/** Statut */
|
||||
public Label statusLabel;
|
||||
/** Horodatage */
|
||||
public Label dateTimeLabel;
|
||||
/** Contact associé à l'utilisateur courant */
|
||||
public Contact contact;
|
||||
/** Séparateur des salons/contacts */
|
||||
public SplitPane senderSplitPane;
|
||||
/** Annuaire des contacts */
|
||||
private ContactMap contactMap = new ContactMap();
|
||||
/** Liste observable associée aux contacts */
|
||||
private ObservableList<Contact> contactObservableList = FXCollections.observableArrayList();
|
||||
/** Liste observable associée aux messages */
|
||||
private ObservableList<Post> postsObservableList = FXCollections.observableArrayList();
|
||||
/** Instance de validateur permettant de vérifier la validité du login à partir du pattern LOGIN_PATTERN */
|
||||
private Validator validatorLogin = new Validator();
|
||||
/** Client de connexion */
|
||||
private ChatClient client = null;
|
||||
/** Liste des messages */
|
||||
private PostVector postVector;
|
||||
/** Liste des salons */
|
||||
private RoomMap roomMap = new RoomMap();
|
||||
/** Liste observable associée aux salons */
|
||||
private ObservableList<Room> roomObservableList = FXCollections.observableArrayList();
|
||||
/** ResourceBundle contenant les textes en fonction des langues */
|
||||
private ResourceBundle i18nBundle;
|
||||
/** Propriétés chargées à partir d'un fichier `config.properties` */
|
||||
private Properties properties = new Properties();
|
||||
private Vector<String> hostlist;
|
||||
/** Menu contextuel associé à un clic droit sur un message */
|
||||
private ContextMenu contextMenu = new ContextMenu();
|
||||
/** Menu `removeItem` */
|
||||
private MenuItem removeMenuItem;
|
||||
/** Menu `editMenuItem` */
|
||||
private MenuItem editMenuItem;
|
||||
/** Menu `cancelMenuItem` */
|
||||
private MenuItem cancelMenuItem;
|
||||
|
||||
|
||||
/**
|
||||
* Initialisation du composant graphique
|
||||
* @param url L'url
|
||||
* @param resourceBundle Le ResourceBundle contenant les textes relatifs aux langues
|
||||
*/
|
||||
@Override
|
||||
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||
LOGGER.info("Initialisation de l'interface graphique");
|
||||
|
|
@ -173,12 +212,21 @@ public class ChatController implements Initializable {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche le menu contextuel lors d'un clic droit sur un message dans la PostListView
|
||||
*
|
||||
* @param e L'évènement associé à un clic droit sur la PostListView
|
||||
*/
|
||||
private void handleContextMenu(ContextMenuEvent e) {
|
||||
if (postListView.getSelectionModel().getSelectedItem().getFrom().equals(contact.getLogin())) {
|
||||
contextMenu.show(postListView, e.getScreenX(), e.getScreenY());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise le menu de contexte lors d'un clic droit sur un message dans la PostListView
|
||||
*
|
||||
*/
|
||||
private void initContextMenu() {
|
||||
this.removeMenuItem = new MenuItem();
|
||||
this.editMenuItem = new MenuItem();
|
||||
|
|
@ -195,6 +243,13 @@ public class ChatController implements Initializable {
|
|||
contextMenu.getItems().addAll(removeMenuItem, editMenuItem, cancelMenuItem);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Supprime le message sélectionné par le menu contextuel, affiche une vue de modification du message, puis envoie
|
||||
* un événement POST associé au nouveau message.
|
||||
* Conserve l'`UUID`, le `timestamp`, les champs `from` et `to`
|
||||
* @param actionEvent L'évènement lié au bouton "Edit message" dans le menu
|
||||
*/
|
||||
private void onMessageEdit(ActionEvent actionEvent) {
|
||||
UUID postUUID = postListView.getSelectionModel().getSelectedItem().getId();
|
||||
long timestamp = postListView.getSelectionModel().getSelectedItem().getTimestamp();
|
||||
|
|
@ -214,6 +269,12 @@ public class ChatController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime le message sélectionné par le menu contextuel, et envoie un nouvel évènement POST associé au message
|
||||
* de remplacement
|
||||
* Conserve l'`UUID`, le `timestamp`, les champs `from` et `to`
|
||||
* @param actionEvent L'évènement lié au bouton "Delete message" dans le menu
|
||||
*/
|
||||
private void onMessageRemove(ActionEvent actionEvent) {
|
||||
UUID postUUID = postListView.getSelectionModel().getSelectedItem().getId();
|
||||
long timestamp = postListView.getSelectionModel().getSelectedItem().getTimestamp();
|
||||
|
|
@ -225,6 +286,10 @@ public class ChatController implements Initializable {
|
|||
postListView.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ouvre une fenêtre de dialogue permettant d'ajouter un hôte à la liste des serveurs.
|
||||
* @param actionEvent L'évènement lié au bouton "Add Host" dans le menu
|
||||
*/
|
||||
private void handleHostAdd(ActionEvent actionEvent) {
|
||||
try {
|
||||
ChatHostAddController controller = showNewStage(i18nBundle.getString("addHost"), "chathostadd-view.fxml");
|
||||
|
|
@ -260,6 +325,9 @@ public class ChatController implements Initializable {
|
|||
return fxmlLoader.getController();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise RoomListView avec sa CelFactory et sa liste observable
|
||||
*/
|
||||
private void initRoomListView() {
|
||||
try {
|
||||
roomsListView.setCellFactory(roomListView -> new RoomListViewCell());
|
||||
|
|
@ -269,6 +337,10 @@ public class ChatController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie au serveur un message à destination du contact sélectionné, contenant le texte du champ éditable des messages
|
||||
* @param actionEvent L'évènement lié au bouton Send ou à l'appui sur `Entrée` dans le champ éditable des messages
|
||||
*/
|
||||
private void onActionSend(ActionEvent actionEvent) {
|
||||
String login = null;
|
||||
if (!(getSelectedContactLogin() == null)) {
|
||||
|
|
@ -284,10 +356,11 @@ public class ChatController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ouvre une fenêtre de dialogue permettant de choisir son avatar à partir d'un fichier image
|
||||
* @param event L'évènement lié au clic sur l'avatar ou sur le bouton `"Change avatar"` dans le menu
|
||||
*/
|
||||
private void handleAvatarChange(Event event) {
|
||||
/**
|
||||
* Ouvre une fenêtre de dialogue permettant de choisir son avatar
|
||||
*/
|
||||
try {
|
||||
FileChooser fileChooser = new FileChooser();
|
||||
Stage stage = (Stage) avatarImageView.getScene().getWindow();
|
||||
|
|
@ -315,7 +388,10 @@ public class ChatController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Connexion au serveur
|
||||
* @param observable L'évènement lié au clic sur le bouton Connexion/Déconnexion
|
||||
*/
|
||||
private void handleConnection(Observable observable) {
|
||||
if (connectionButton.isSelected()) {
|
||||
java.awt.Image img = SwingFXUtils.fromFXImage(this.avatarImageView.getImage(), null);
|
||||
|
|
@ -367,6 +443,9 @@ public class ChatController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vide toutes les maps, les vecteurs et les listes observables.
|
||||
*/
|
||||
private void clearLists() {
|
||||
this.contactMap = new ContactMap();
|
||||
this.postVector = new PostVector();
|
||||
|
|
@ -376,6 +455,10 @@ public class ChatController implements Initializable {
|
|||
roomObservableList.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le login est conforme ou s'il n'est pas égal à `"system"`
|
||||
* @param context Le contexte de vérification
|
||||
*/
|
||||
private void checkLogin(Check.Context context) {
|
||||
String login = context.get("login");
|
||||
if (!LOGIN_PATTERN.matcher(login).matches()) {
|
||||
|
|
@ -388,6 +471,11 @@ public class ChatController implements Initializable {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour le label de statut situé en bas à gauche de l'application en fonction de si
|
||||
* l'utilisateur est connecté à un serveur, et si oui, son login et le socket de connexion du serveur
|
||||
* @param event L'évènement lié à l'interaction avec la HostComboBox
|
||||
*/
|
||||
private void statusNameUpdate(Event event) {
|
||||
statusLabel.setText("not connected to " + hostComboBox.getValue());
|
||||
|
||||
|
|
@ -399,7 +487,10 @@ public class ChatController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gestion de l'horloge : affichage de la date courante toutes les secondes dans le label dateTimeLabel`
|
||||
*
|
||||
*/
|
||||
private void dateTimeLoop() {
|
||||
while (true) {
|
||||
try {
|
||||
|
|
@ -413,6 +504,9 @@ public class ChatController implements Initializable {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise la CellFactory et la liste observable associée à la ContactListView
|
||||
*/
|
||||
private void initContactListView() {
|
||||
try {
|
||||
contactsListView.setCellFactory(contactListView -> new ContactListViewCell());
|
||||
|
|
@ -425,6 +519,10 @@ public class ChatController implements Initializable {
|
|||
LOGGER.severe(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise la CellFactory et la liste observable associée à la PostListView
|
||||
*/
|
||||
private void initPostListView() {
|
||||
try {
|
||||
postListView.setCellFactory(postListView -> new PostListViewCell(this));
|
||||
|
|
@ -434,7 +532,10 @@ public class ChatController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Le contact sélectionné dans la ListView
|
||||
* @return Le login associé au contact
|
||||
*/
|
||||
public String getSelectedContactLogin() {
|
||||
Contact contact;
|
||||
String login;
|
||||
|
|
@ -448,6 +549,10 @@ public class ChatController implements Initializable {
|
|||
return login;
|
||||
}
|
||||
|
||||
/**
|
||||
* Le salon sélectionné dans la ListView
|
||||
* @return Le nom du salon
|
||||
*/
|
||||
public String getSelectedRoomName() {
|
||||
Room room;
|
||||
String roomName;
|
||||
|
|
@ -461,14 +566,27 @@ public class ChatController implements Initializable {
|
|||
return roomName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Le contact utilisant l'application
|
||||
*
|
||||
* @return Le contact
|
||||
*/
|
||||
public Contact getContact() {
|
||||
return contact;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter de l'annuaire des contacts
|
||||
* @return L'annuaire des contacts
|
||||
*/
|
||||
public ContactMap getContactsMap() {
|
||||
return contactMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vide la vue des messages puis envoie un évènement JOIN et un évènement LSTP en fonction du salon sélectionné
|
||||
* @param roomSelected Le salon sélectionné
|
||||
*/
|
||||
void handleRoomSelection(Room roomSelected) {
|
||||
|
||||
if (roomSelected != null) {
|
||||
|
|
@ -491,6 +609,10 @@ public class ChatController implements Initializable {
|
|||
postListView.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Vide la vue des messages puis envoie un évènement LSTP en fonction du contact sélectionné
|
||||
* @param contactSelected Le contact sélectionné
|
||||
*/
|
||||
void handleContactSelection(Contact contactSelected) {
|
||||
if (contactSelected != null) {
|
||||
LOGGER.info("Clic sur " + contactSelected);
|
||||
|
|
@ -509,6 +631,11 @@ public class ChatController implements Initializable {
|
|||
postListView.refresh();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback gérant les évènements réseaux reçus en provenance du serveur, en fonction du type de l'évènement
|
||||
* @param event L'évènement reçu
|
||||
*/
|
||||
public void handleEvent(rtgre.modeles.Event event) {
|
||||
LOGGER.info("Received new event! : " + event);
|
||||
LOGGER.info(event.getType());
|
||||
|
|
@ -524,6 +651,10 @@ public class ChatController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Traite les évènements de type "CONT" informant de l'état d'un contact
|
||||
* @param content Le contenu d'un évènement `"CONT"`
|
||||
*/
|
||||
private void handleRoomEvent(JSONObject content) {
|
||||
LOGGER.info(content.toString());
|
||||
Room room = new Room(content.getString("room"));
|
||||
|
|
@ -532,6 +663,10 @@ public class ChatController implements Initializable {
|
|||
roomsListView.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Traite la réception d'un post
|
||||
* @param content Le contenu d'un évènement `"POST"`
|
||||
*/
|
||||
private void handlePostEvent(JSONObject content) {
|
||||
|
||||
System.out.println("Selected: " + roomsListView.getSelectionModel().getSelectedItem());
|
||||
|
|
@ -606,6 +741,12 @@ public class ChatController implements Initializable {
|
|||
postListView.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Traite les évènements de type "CONT" informant de l'état d'un contact
|
||||
* @param content Le contenu d'un évènement `"CONT"`
|
||||
*/
|
||||
private void handleContEvent(JSONObject content) {
|
||||
Contact contact = contactMap.getContact(content.getString("login"));
|
||||
java.awt.Image avatar = null;
|
||||
|
|
|
|||
|
|
@ -16,18 +16,33 @@ import java.util.ResourceBundle;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Interface graphique permettant à l'utilisateur de choisir un serveur (hote:port) sur lequel il se souhaite se connecter.
|
||||
*/
|
||||
public class ChatHostAddController implements Initializable {
|
||||
|
||||
|
||||
/** Le champ de saisie de l'hôte */
|
||||
public TextField hostTextField;
|
||||
/** Bouton de réinitialisation du camp de saisie */
|
||||
public Button resetButton;
|
||||
/** Wrapper du bouton Submit */
|
||||
public HBox submitWrapper;
|
||||
/** Bouton Submit */
|
||||
public Button submitButton;
|
||||
/** Si la vue possède une valeur de retour */
|
||||
private boolean ok = false;
|
||||
/** Le pattern à satisfaire par un serveur `hote:port` */
|
||||
public static final Pattern HOST_PORT_REGEX = Pattern.compile("^([-.a-zA-Z0-9]+)(?::([0-9]{1,5}))?$");
|
||||
/** Objet Validator permettant de vérifier la validité d'un serveur `hote:port` */
|
||||
private Validator validatorHost = new Validator();
|
||||
/** ResourceBundle contenant les textes relatifs aux langues */
|
||||
private ResourceBundle i18nBundle;
|
||||
|
||||
/**
|
||||
* Initialisation du composant graphique
|
||||
* @param url L'url
|
||||
* @param resourceBundle Le ResourceBundle contenant les textes relatifs aux langues
|
||||
*/
|
||||
@Override
|
||||
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||
submitButton.setOnAction(this::onActionSubmit);
|
||||
|
|
@ -49,6 +64,10 @@ public class ChatHostAddController implements Initializable {
|
|||
.immediate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la valeur de `hostTextField` est conforme.
|
||||
* @param context Le contexte de vérification
|
||||
*/
|
||||
private void checkHost(Check.Context context) {
|
||||
String host = context.get("host");
|
||||
if (!HOST_PORT_REGEX.matcher(host).matches()) {
|
||||
|
|
@ -56,15 +75,29 @@ public class ChatHostAddController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback sur le bouton `Reset`
|
||||
* Efface le contenu saisi dans le champ `hostTextField`
|
||||
* @param actionEvent L'évènement associé au clic sur le bouton Reset
|
||||
*/
|
||||
private void onActionReset(ActionEvent actionEvent) {
|
||||
hostTextField.setText("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback sur le bouton `Add`
|
||||
* Ferme la fenêtre pour revenir dans l'application graphique appelante.
|
||||
* @param actionEvent L'évènement associé au clic sur le bouton Add
|
||||
*/
|
||||
private void onActionSubmit(ActionEvent actionEvent) {
|
||||
ok = true;
|
||||
((Stage) submitButton.getScene().getWindow()).close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter du mode de fermeture de la fenêtre
|
||||
* @return la fermeture s'est-elle finie par un clic sur le bouton Send ?
|
||||
*/
|
||||
public boolean isOk() {
|
||||
return ok;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,14 +9,26 @@ import javafx.stage.Stage;
|
|||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* Interface graphique permettant à l'utilisateur de modifier un message.
|
||||
*/
|
||||
public class ModifyMessageController implements Initializable {
|
||||
|
||||
|
||||
/** Le champ de saisie de l'hôte */
|
||||
public TextField hostTextField;
|
||||
/** Bouton de réinitialisation du camp de saisie */
|
||||
public Button resetButton;
|
||||
/** Bouton Submit */
|
||||
public Button submitButton;
|
||||
/** Si la vue possède une valeur de retour */
|
||||
private Boolean ok;
|
||||
|
||||
|
||||
/**
|
||||
* Initialisation du composant graphique
|
||||
* @param url L'url
|
||||
* @param resourceBundle Le ResourceBundle contenant les textes relatifs aux langues
|
||||
*/
|
||||
@Override
|
||||
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||
resetButton.setOnAction(this::onActionReset);
|
||||
|
|
@ -24,15 +36,29 @@ public class ModifyMessageController implements Initializable {
|
|||
hostTextField.setOnAction(this::onActionSubmit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback sur le bouton `Reset`
|
||||
* Efface le contenu saisi dans le champ `hostTextField`
|
||||
* @param actionEvent L'évènement associé au clic sur le bouton Reset
|
||||
*/
|
||||
private void onActionReset(ActionEvent actionEvent) {
|
||||
hostTextField.setText("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback sur le bouton `Add`
|
||||
* Ferme la fenêtre pour revenir dans l'application graphique appelante.
|
||||
* @param actionEvent L'évènement associé au clic sur le bouton Add
|
||||
*/
|
||||
private void onActionSubmit(ActionEvent actionEvent) {
|
||||
ok = true;
|
||||
((Stage) submitButton.getScene().getWindow()).close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter du mode de fermeture de la fenêtre
|
||||
* @return la fermeture s'est-elle finie par un clic sur le bouton Send ?
|
||||
*/
|
||||
public boolean isOk() {
|
||||
return ok;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ public class ContactListViewCell extends ListCell<Contact> {
|
|||
* Callback déclenchée à chaque modification d'un objet d'une liste d'observable.
|
||||
*
|
||||
* @param contact Le contact à mettre à jour
|
||||
* @param empty La liste de cellule doit-elle être complètement remise à zéro ?
|
||||
* @param empty La liste de cellule doit-elle être complètement remise à zéro ?
|
||||
*/
|
||||
@Override
|
||||
protected void updateItem(Contact contact, boolean empty) {
|
||||
|
|
|
|||
|
|
@ -15,11 +15,21 @@ import rtgre.modeles.Post;
|
|||
import java.awt.image.BufferedImage;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Classe modélisant la fabrique de cellule de la vue des posts
|
||||
* {@link ChatController#postListView}.
|
||||
*
|
||||
* @see ListCell
|
||||
*/
|
||||
public class PostListViewCell extends ListCell<Post> {
|
||||
|
||||
/** Controller de l'application */
|
||||
ChatController controller;
|
||||
|
||||
/**
|
||||
* Constructeur par défaut
|
||||
* @param controller Le controller de l'application grapihque
|
||||
*/
|
||||
public PostListViewCell(ChatController controller) {
|
||||
this.controller = controller;
|
||||
}
|
||||
|
|
@ -28,7 +38,7 @@ public class PostListViewCell extends ListCell<Post> {
|
|||
* Callback déclenchée à chaque modification d'un objet d'une liste d'observable.
|
||||
*
|
||||
* @param post Le post
|
||||
* @param empty La liste de cellule doit-elle être complètement remise à zéro ?
|
||||
* @param empty La liste de cellule doit-elle être complètement remise à zéro ?
|
||||
*/
|
||||
@Override
|
||||
protected void updateItem(Post post, boolean empty) {
|
||||
|
|
@ -41,6 +51,11 @@ public class PostListViewCell extends ListCell<Post> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de la cellule d'un post.
|
||||
*
|
||||
* @param post Le post à mettre à jour
|
||||
*/
|
||||
void updatePost(Post post) {
|
||||
|
||||
Text datetimeText = new Text("\n%1$td/%1$tm/%1$tY %1$tH:%1$tM:%1$tS\n".formatted(new Date(post.getTimestamp())));
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import javafx.scene.shape.Rectangle;
|
|||
import javafx.scene.text.Font;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.scene.text.TextAlignment;
|
||||
import rtgre.chat.ChatController;
|
||||
import rtgre.modeles.Contact;
|
||||
import rtgre.modeles.Room;
|
||||
|
||||
|
|
@ -21,8 +22,20 @@ import java.awt.image.BufferedImage;
|
|||
|
||||
import static rtgre.chat.ChatApplication.LOGGER;
|
||||
|
||||
/**
|
||||
* Classe modélisant la fabrique de cellule de la vue des salons
|
||||
* {@link ChatController#roomsListView}.
|
||||
*
|
||||
* @see ListCell
|
||||
*/
|
||||
public class RoomListViewCell extends ListCell<Room> {
|
||||
|
||||
/**
|
||||
* Callback déclenchée à chaque modification d'un objet d'une liste d'observable.
|
||||
*
|
||||
* @param room Le salon
|
||||
* @param empty La liste de cellule doit-elle être complètement remise à zéro ?
|
||||
*/
|
||||
@Override
|
||||
protected void updateItem(Room room, boolean empty) {
|
||||
super.updateItem(room, empty);
|
||||
|
|
@ -36,6 +49,11 @@ public class RoomListViewCell extends ListCell<Room> {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Couleur d'un salon, choisie parmi une banque de couleurs, en fonction d'un nom de salon.
|
||||
* @param roomName Nom du salon
|
||||
* @return La couleur associée à la première lettre du nom du salon
|
||||
*/
|
||||
public Color colorFromName(String roomName) {
|
||||
switch (roomName) {
|
||||
case "#all":
|
||||
|
|
@ -51,6 +69,11 @@ public class RoomListViewCell extends ListCell<Room> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de la cellule d'un salon.
|
||||
*
|
||||
* @param room Le salon à mettre à jour
|
||||
*/
|
||||
private void updateRoom(Room room) {
|
||||
LOGGER.finest("Mise à jour de " + room);
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,12 @@ import java.util.logging.Logger;
|
|||
|
||||
import static rtgre.chat.ChatApplication.LOGGER;
|
||||
|
||||
/**
|
||||
* Classe modélisant
|
||||
*/
|
||||
public class ChatClient extends ClientTCP {
|
||||
|
||||
/** Le Controller du chat associé à la connexion */
|
||||
private final ChatController listener;
|
||||
|
||||
/**
|
||||
|
|
@ -27,7 +31,7 @@ public class ChatClient extends ClientTCP {
|
|||
* @param host IP ou nom de domaine du serveur
|
||||
* @param port port d'écoute du serveur
|
||||
* @param listener instance de ChatController liée au client
|
||||
* @throws IOException
|
||||
* @throws IOException si la connexion échoue
|
||||
*/
|
||||
public ChatClient(String host, int port, ChatController listener) throws IOException {
|
||||
super(host, port);
|
||||
|
|
@ -35,7 +39,10 @@ public class ChatClient extends ClientTCP {
|
|||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Envoi d'un évènement, sérialisé dans sa représentation JSON, au serveur.
|
||||
* @param event L'évènement à envoyer
|
||||
*/
|
||||
public void sendEvent(Event event) {
|
||||
connected = true;
|
||||
try {
|
||||
|
|
@ -54,11 +61,20 @@ public class ChatClient extends ClientTCP {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoi de l'évènement d'authentification
|
||||
* @param contact Le contact associé à l'utilisateur
|
||||
*/
|
||||
public void sendAuthEvent(Contact contact) {
|
||||
Event authEvent = new Event(Event.AUTH, new JSONObject().put("login", contact.getLogin()));
|
||||
sendEvent(authEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Demande la liste des posts (évènement de type "LSTP")
|
||||
* @param since L'horodatage à partir duquel est demandée la liste des posts
|
||||
* @param select Le login du contact ou le salon de discussion avec lequel les posts ont été échangés
|
||||
*/
|
||||
public void sendListPostEvent(long since, String select) {
|
||||
Event listPostEvent = new Event(
|
||||
Event.LIST_POSTS,
|
||||
|
|
@ -69,17 +85,27 @@ public class ChatClient extends ClientTCP {
|
|||
sendEvent(listPostEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Demande la liste des salons (évènement de type "LSTR")
|
||||
*/
|
||||
public void sendListRoomEvent() {
|
||||
Event listRoomEvent = new Event(Event.LIST_ROOMS, new JSONObject());
|
||||
sendEvent(listRoomEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie un évènement de fermeture de connexion (de type "QUIT")
|
||||
*/
|
||||
public void sendQuitEvent() {
|
||||
Event quitEvent = new Event(Event.QUIT, new JSONObject());
|
||||
sendEvent(quitEvent);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Boucle de réception des messages : chaque message est un évènement sérialisé en JSON, qui est transféré à ChatController.handleEvent(rtgre.modeles.Event) pour traitement.
|
||||
* Si le message n'est pas conforme (format JSON), la connection est stoppée.
|
||||
*/
|
||||
@Override
|
||||
public void receiveLoop() {
|
||||
LOGGER.info(RED + "Boucle de réception de messages..." + RST);
|
||||
|
|
@ -102,18 +128,34 @@ public class ChatClient extends ClientTCP {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter du logger
|
||||
* @return Le logger utilisé par ChatClient
|
||||
*/
|
||||
public Logger getLogger() {
|
||||
return LOGGER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter du listener
|
||||
* @return Le controller associé à la connexion
|
||||
*/
|
||||
public ChatController getListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoi d'un message au travers d'un évènement "MSG", contenant l'objet `Message` adressé à un destinataire `to` avec un contenu `body`
|
||||
* @param msg Le message à envoyer
|
||||
*/
|
||||
public void sendMessageEvent(Message msg) {
|
||||
sendEvent(new Event("MESG", msg.toJsonObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoi d'un post au travers d'un évènement "POST".
|
||||
* @param selectedItem Post à envoyer
|
||||
*/
|
||||
public void sendPostEvent(Post selectedItem) {
|
||||
Event postEvent = new Event(Event.POST, selectedItem.toJsonObject());
|
||||
sendEvent(postEvent);
|
||||
|
|
|
|||
|
|
@ -10,17 +10,43 @@ import java.util.logging.LogManager;
|
|||
import static rtgre.chat.ChatApplication.LOGGER;
|
||||
|
||||
/**
|
||||
* Client TCP: envoie des chaines de caractères à un serveur et lit les chaines en retour.
|
||||
* <p>
|
||||
* Serveur netcat à lancer en face : <code>nc -k -l -p 2024 -v</code>
|
||||
* Client TCP : envoie des chaines de caractères à un serveur et lit les chaines en retour.
|
||||
*/
|
||||
public class ClientTCP {
|
||||
|
||||
/** Couleur rouge */
|
||||
public static final String RED = "\u001b[31m";
|
||||
/** Couleur bleue */
|
||||
public static final String BLUE = "\u001b[34m";
|
||||
/** Couleur standard */
|
||||
public static final String RST = "\u001b[0m";
|
||||
/** Fin de message */
|
||||
public static final String END_MESSAGE = "fin";
|
||||
|
||||
/**
|
||||
* Socket connecté au serveur
|
||||
*/
|
||||
protected Socket sock;
|
||||
|
||||
/**
|
||||
* Flux de caractères UTF-8 en sortie
|
||||
*/
|
||||
protected PrintStream out;
|
||||
|
||||
/**
|
||||
* Flux de caractères UTF-8 en entrée
|
||||
*/
|
||||
protected BufferedReader in;
|
||||
|
||||
/**
|
||||
* Chaine de caractères "ip:port" du client
|
||||
*/
|
||||
protected String ipPort;
|
||||
|
||||
/**
|
||||
* Le client est-il connecté ?
|
||||
*/
|
||||
protected boolean connected;
|
||||
|
||||
/*
|
||||
static {
|
||||
try {
|
||||
|
|
@ -32,6 +58,11 @@ public class ClientTCP {
|
|||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Programme principal [Déprécié]
|
||||
* @param args Arguments
|
||||
* @throws Exception Si la connexion échoue
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
/*
|
||||
ClientTCP client = new ClientTCP("localhost", 2024);
|
||||
|
|
@ -55,35 +86,13 @@ public class ClientTCP {
|
|||
client.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Socket connecté au serveur
|
||||
*/
|
||||
protected Socket sock;
|
||||
|
||||
/**
|
||||
* Flux de caractères UTF-8 en sortie
|
||||
*/
|
||||
protected PrintStream out;
|
||||
|
||||
/**
|
||||
* Flux de caractères UTF-8 en entrée
|
||||
*/
|
||||
protected BufferedReader in;
|
||||
|
||||
/**
|
||||
* Chaine de caractères "ip:port" du client
|
||||
*/
|
||||
protected String ipPort;
|
||||
|
||||
protected boolean connected;
|
||||
|
||||
/**
|
||||
* Le constructeur ouvre la connexion TCP au serveur <code>host:port</code>
|
||||
* et récupère les flux de caractères en entrée {@link #in} et sortie {@link #out}
|
||||
*import static rtgre.chat.ChatApplication.LOGGER;
|
||||
* @param host IP ou nom de domaine du serveur
|
||||
* @param port port d'écoute du serveur
|
||||
* @throws IOException
|
||||
* @throws IOException si la connexion échoue ou si les flux ne sont pas récupérables
|
||||
*/
|
||||
public ClientTCP(String host, int port) throws IOException {
|
||||
System.out.printf("Connexion à [%s:%d]%n", host, port);
|
||||
|
|
@ -116,10 +125,18 @@ public class ClientTCP {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter de connected
|
||||
* @return L'état de connected
|
||||
*/
|
||||
public boolean isConnected() {
|
||||
return connected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter de connected
|
||||
* @param connected L'utilisateur est-il connecté ?
|
||||
*/
|
||||
public void setConnected(boolean connected) {
|
||||
this.connected = connected;
|
||||
}
|
||||
|
|
@ -153,6 +170,9 @@ public class ClientTCP {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Boucle d'envoi de messages
|
||||
*/
|
||||
public void sendLoop() {
|
||||
System.out.println(BLUE + "Boucle d'envoi de messages..." + RST);
|
||||
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
|
||||
|
|
@ -176,6 +196,9 @@ public class ClientTCP {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Boucle de réception de messages
|
||||
*/
|
||||
public void receiveLoop() {
|
||||
System.out.println(RED + "Boucle de réception de messages..." + RST);
|
||||
connected = true;
|
||||
|
|
|
|||
|
|
@ -15,17 +15,25 @@ import java.util.Objects;
|
|||
|
||||
import static rtgre.chat.ChatApplication.LOGGER;
|
||||
|
||||
/**
|
||||
* Classe modélisant un contact avec son `login`, son `avatar` et son état (connecté ou non)
|
||||
*/
|
||||
public class Contact {
|
||||
/** Le login du contact */
|
||||
protected String login;
|
||||
/** L'avatar du contact */
|
||||
protected java.awt.Image avatar;
|
||||
/** L'utilisateur est connecté ? */
|
||||
protected boolean connected;
|
||||
/** Le salon courant */
|
||||
protected String currentRoom;
|
||||
/** Le compteur de messages non-lus */
|
||||
protected UnreadCount unreadCount = new UnreadCount();
|
||||
|
||||
/**
|
||||
* Crée un objet Contact
|
||||
* @param: String login
|
||||
* @param: java.awt.Image avatar
|
||||
* @param login Login du contact
|
||||
* @param avatar Avatar du contact au format java.awt.Image
|
||||
*/
|
||||
public Contact(String login, java.awt.Image avatar) {
|
||||
this.login = login;
|
||||
|
|
@ -36,9 +44,9 @@ public class Contact {
|
|||
|
||||
/**
|
||||
* Crée un objet Contact
|
||||
* @param: String login
|
||||
* @param: boolean connected
|
||||
* @param: java.awt.Image avatar
|
||||
* @param login Login du contact
|
||||
* @param connected Utilisateur connecté ?
|
||||
* @param avatar au format java.awt.Image
|
||||
*/
|
||||
public Contact(String login, boolean connected, java.awt.Image avatar) {
|
||||
this.login = login;
|
||||
|
|
@ -47,15 +55,19 @@ public class Contact {
|
|||
this.currentRoom = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter de `currentRoom`
|
||||
* @return Le salon actuel
|
||||
*/
|
||||
public String getCurrentRoom() {
|
||||
return currentRoom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée un objet Contact
|
||||
* @param: String login
|
||||
* @param: boolean connected
|
||||
* @param: File banques_avatars
|
||||
* @param login Login du contact
|
||||
* @param connected Utilisateur connecté ?
|
||||
* @param banques_avatars Image contenant les avatars par défaut relatifs aux logins
|
||||
*/
|
||||
public Contact(String login, boolean connected, File banques_avatars) {
|
||||
this.login = login;
|
||||
|
|
@ -70,28 +82,52 @@ public class Contact {
|
|||
this.currentRoom = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter de `login`
|
||||
* @return Le login du contact
|
||||
*/
|
||||
public String getLogin() {
|
||||
return this.login;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter de `avatar`
|
||||
* @return L'avatar du contact au format java.awt.Image
|
||||
*/
|
||||
public java.awt.Image getAvatar() {
|
||||
return this.avatar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Représentation textuelle de l'objet Contact
|
||||
* @return Le contact au format `@login`
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "@" + this.login;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter du booléen `connected`
|
||||
* @return Le statut de connexion du contact
|
||||
*/
|
||||
public boolean isConnected() {
|
||||
return this.connected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter du booléen `connected`
|
||||
* @param connected Le statut de connexion du contact
|
||||
*/
|
||||
public void setConnected(boolean connected) {
|
||||
this.connected = connected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Egalité de deux contacts, s'ils ont le même login
|
||||
* @param o Le salon auquel est comparé
|
||||
* @return true si sont égaux, false sinon
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
|
@ -105,10 +141,18 @@ public class Contact {
|
|||
return Objects.hashCode(login);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter du compteur de messages non-lus
|
||||
* @return L'object `UnreadCount` du contact
|
||||
*/
|
||||
public UnreadCount getUnreadCount() {
|
||||
return unreadCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sérialise le contact courant en objet JSON
|
||||
* @return L'objet JSONObject sérialisé
|
||||
*/
|
||||
public JSONObject toJsonObject() {
|
||||
return new JSONObject()
|
||||
.put("login", this.login)
|
||||
|
|
@ -116,20 +160,33 @@ public class Contact {
|
|||
.put("avatar", Contact.imageToBase64((BufferedImage) avatar));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sérialise le contact courant en chaîne de caractères au format JSON
|
||||
* @return un String au format JSON
|
||||
*/
|
||||
public String toJson() {
|
||||
return toJsonObject().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit un objet Contact à partir d'un objet JSON et de la banque d'avatars
|
||||
* @param jsonObject L'objet JSON source
|
||||
* @param banque_avatars La banque d'avatars
|
||||
* @return Un objet Contact
|
||||
*/
|
||||
public static Contact fromJSON(JSONObject jsonObject, File banque_avatars) {
|
||||
return new Contact(jsonObject.getString("login"), jsonObject.getBoolean("connected"), banque_avatars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renvoie une sous-image en fonction d'une banque d'image et d'un login.
|
||||
* @param fichier La banque d'avatars
|
||||
* @param login Le login dont on cherche l'avatar
|
||||
* @return Un objet BufferedImage contenant l'image recherchée
|
||||
* @throws IOException si le fichier est introucable
|
||||
*/
|
||||
public static BufferedImage avatarFromLogin(File fichier, String login) throws IOException {
|
||||
/**
|
||||
* Renvoie une sous-image en fonction d'une banque d'image et d'un login.
|
||||
* @param: File fichier
|
||||
* @param: String login
|
||||
*/
|
||||
|
||||
BufferedImage img = ImageIO.read(fichier);
|
||||
int width = img.getWidth() / 9;
|
||||
int height = img.getHeight();
|
||||
|
|
@ -137,22 +194,39 @@ public class Contact {
|
|||
return img.getSubimage(n*width, 0, width, height);
|
||||
}
|
||||
|
||||
public void setAvatarFromFile(File f) {
|
||||
/**
|
||||
* Initialise l'avatar du contact courant en fonction de son login actuel et d'un fichier de banque d'avatars
|
||||
* @param file La banque d'avatars
|
||||
*/
|
||||
public void setAvatarFromFile(File file) {
|
||||
try {
|
||||
this.avatar = avatarFromLogin(f, this.login);
|
||||
this.avatar = avatarFromLogin(file, this.login);
|
||||
} catch (IOException e) {
|
||||
System.out.println("Erreur : " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter de `avatar`
|
||||
* @param avatar L'avatar au format java.awt.Image
|
||||
*/
|
||||
public void setAvatar(java.awt.Image avatar) {
|
||||
this.avatar = avatar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter de currentRoom
|
||||
* @param currentRoom Le salon courant
|
||||
*/
|
||||
public void setCurrentRoom(String currentRoom) {
|
||||
this.currentRoom = currentRoom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforme une image au format java.awt.Image ou BufferedImage en une image encodée en base64.
|
||||
* @param img L'image en java.awt.Image ou en BufferedImage
|
||||
* @return L'image encodée en base64 si l'image est chargée correctement, un String vide sinon
|
||||
*/
|
||||
public static String imageToBase64(BufferedImage img) {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
try {
|
||||
|
|
@ -165,6 +239,11 @@ public class Contact {
|
|||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforme une image encodée en base64 en une image au format java.awt.Image.
|
||||
* @param avatar64 L'image encodée en base64
|
||||
* @return L'image en java.awt.Image
|
||||
*/
|
||||
public static java.awt.Image base64ToImage(String avatar64) {
|
||||
byte[] bytes64 = Base64.getDecoder().decode(avatar64);
|
||||
try {
|
||||
|
|
@ -176,6 +255,11 @@ public class Contact {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforme une image encodée en base64 en une image au format BufferedImage.
|
||||
* @param avatar64 L'image encodée en base64
|
||||
* @return L'image en BufferedImage
|
||||
*/
|
||||
public static BufferedImage base64ToBufferedImage(String avatar64) {
|
||||
byte[] bytes64 = Base64.getDecoder().decode(avatar64);
|
||||
try {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -8,10 +8,18 @@ import org.sqlite.JDBC;
|
|||
|
||||
import static rtgre.chat.ChatApplication.LOGGER;
|
||||
|
||||
/**
|
||||
* Classe modélisant la connexion à la base de données des posts.
|
||||
*/
|
||||
public class DatabaseApi {
|
||||
/** Connexion à la base de données */
|
||||
private Connection con;
|
||||
/** Curseur "statement" à exécuter */
|
||||
private Statement stmt;
|
||||
|
||||
/**
|
||||
* Constructeur par défaut : connecte la base de donnée et et créer un statement
|
||||
*/
|
||||
public DatabaseApi() {
|
||||
try {
|
||||
this.con = DriverManager.getConnection("jdbc:sqlite:target/dbase.db");
|
||||
|
|
@ -24,6 +32,10 @@ public class DatabaseApi {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée la base de données si elle n'existe pas déjà
|
||||
* @param con La connexion à la base de données
|
||||
*/
|
||||
private void initDB(Connection con) {
|
||||
try {
|
||||
String sql = "CREATE TABLE IF NOT EXISTS `posts` ("
|
||||
|
|
@ -42,6 +54,11 @@ public class DatabaseApi {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère un post selon son UUID
|
||||
* @param uuid l'UUID du post
|
||||
* @return Une liste de résultats contenant le Post
|
||||
*/
|
||||
public ResultSet getPostById(UUID uuid) {
|
||||
String query = "SELECT * FROM posts WHERE id = " + uuid.toString();
|
||||
try {
|
||||
|
|
@ -52,6 +69,11 @@ public class DatabaseApi {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère tous les posts dont le timestamp est supérieur à celui donné
|
||||
* @param timestamp Le timestamp de comparaison
|
||||
* @return Une liste de résultats contenant les posts, triés dans l'ordre chronologique
|
||||
*/
|
||||
public ResultSet getPostsSince(long timestamp) {
|
||||
String query = "SELECT * FROM posts WHERE timestamp >= " + timestamp + "ORDER BY timestamp DESC";
|
||||
try {
|
||||
|
|
@ -62,6 +84,10 @@ public class DatabaseApi {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère tous les posts de la base de données
|
||||
* @return Une liste de résultats contenant tous les posts en base
|
||||
*/
|
||||
public ResultSet getPosts() {
|
||||
String query = "SELECT * FROM posts";
|
||||
try {
|
||||
|
|
@ -72,6 +98,11 @@ public class DatabaseApi {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute un post dans la base
|
||||
* @param post Le poste à ajouter
|
||||
* @return `true` si le post a bien été ajouté, `false` si une erreur est survenue
|
||||
*/
|
||||
public boolean addPost(Post post) {
|
||||
String query = "INSERT INTO posts VALUES (?, ?, ?, ?, ?)";
|
||||
try {
|
||||
|
|
@ -90,6 +121,11 @@ public class DatabaseApi {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enlève un post de la base de données
|
||||
* @param post Le post à retirer
|
||||
* @return `true` si le post a bien été retiré, `false` si une erreur est survenue
|
||||
*/
|
||||
public boolean removePost(Post post) {
|
||||
String query = "DELETE FROM posts WHERE id=?";
|
||||
try {
|
||||
|
|
@ -105,6 +141,9 @@ public class DatabaseApi {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ferme la connexion à la base de données
|
||||
*/
|
||||
public void close() {
|
||||
try {
|
||||
con.close();
|
||||
|
|
|
|||
|
|
@ -3,47 +3,95 @@ package rtgre.modeles;
|
|||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Classe modélisant un évènement permettant un échange client/serveur pour une application de chat.
|
||||
*/
|
||||
public class Event {
|
||||
/** Type de l'évènement client -> serveur : authentification */
|
||||
public static final String AUTH = "AUTH";
|
||||
/** Type de l'évènement client -> serveur : déconnexion */
|
||||
public static final String QUIT = "QUIT";
|
||||
/** Type de l'évènement client -> serveur : envoi d'un message */
|
||||
public static final String MESG = "MESG";
|
||||
/** Type de l'évènement client -> serveur : rejoindre un salon */
|
||||
public static final String JOIN = "JOIN";
|
||||
/** Type de l'évènement serveur -> client : envoi d'un post */
|
||||
public static final String POST = "POST";
|
||||
/** Type de l'évènement serveur -> client : informations sur un contact */
|
||||
public static final String CONT = "CONT";
|
||||
/** Type de l'évènement client -> serveur : demander la liste des contacts */
|
||||
public static final String LIST_CONTACTS = "LSTC";
|
||||
/** Type de l'évènement client -> serveur : demander la liste des posts */
|
||||
public static final String LIST_POSTS = "LSTP";
|
||||
/** Type de l'évènement système interne au client */
|
||||
public static final String SYSTEM = "SYST";
|
||||
/** Type de l'évènement client -> serveur : demander la liste des salons */
|
||||
public static final String LIST_ROOMS = "LSTR";
|
||||
/** Type de l'évènement serveur -> client : informations sur un salon de discussion */
|
||||
public static final String ROOM = "ROOM";
|
||||
/** Le type d'évènement de l'Event courant */
|
||||
private final String type;
|
||||
/** Le contenu de l'Event courant */
|
||||
private final JSONObject content;
|
||||
|
||||
|
||||
/**
|
||||
* Getter du type
|
||||
* @return Le type de l'évènement
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter du contenu
|
||||
* @return Le contenu de l'évènement
|
||||
*/
|
||||
public JSONObject getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructeur par défaut à partir d'un type et d'un objet JSON servant de contenu
|
||||
* @param type Le type de l'évènement
|
||||
* @param content Le contenu de l'évènement
|
||||
*/
|
||||
public Event(String type, JSONObject content) {
|
||||
this.type = type;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Représentation textuelle de l'objet
|
||||
* @return Chaine de caractères représentant l'évènement
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Event{type=" + type + ", content=" + content.toString() + "}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Renvoie l'objet JSON représentant l'évènement
|
||||
* @return L'objet JSON représentant l'évènement
|
||||
*/
|
||||
public JSONObject toJsonObject() {
|
||||
return new JSONObject().put("type", type).put("content", content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sérialisation de la représentation JSON d'un évènement
|
||||
* @return La chaine de caractères représentant l'évènement
|
||||
*/
|
||||
public String toJson() {
|
||||
return toJsonObject().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Méthode de classe instanciant et renvoyant un évènement à partir d'une chaine de caractères représentant un évènement JSON
|
||||
* @param json La représentation JSON d'un évènement
|
||||
* @return L'évènement associé
|
||||
* @throws JSONException s'il y a une erreur dans la sérialisation
|
||||
*/
|
||||
public static Event fromJson(String json) throws JSONException {
|
||||
JSONObject jsonObject = new JSONObject(json);
|
||||
String type = jsonObject.getString("type");
|
||||
|
|
|
|||
|
|
@ -1,30 +1,45 @@
|
|||
package rtgre.modeles;
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
/**
|
||||
* Classe modélisant un message adressé à un destinataire
|
||||
*/
|
||||
public class Message {
|
||||
/**
|
||||
* Un message décrit sous la forme :
|
||||
* @serialField : String to: Le destinataire
|
||||
* @serialField : String body: le corps du message
|
||||
*/
|
||||
/** Login du destinataire du message */
|
||||
protected String to;
|
||||
/** Contenu textuel du message */
|
||||
protected String body;
|
||||
|
||||
/**
|
||||
* Constructeur par défaut
|
||||
* @param to Le destinataire du message
|
||||
* @param body Le contenu du message
|
||||
*/
|
||||
public Message(String to, String body) {
|
||||
|
||||
this.to = to;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter de `to`
|
||||
* @return Le destinataire du message
|
||||
*/
|
||||
public String getTo() {
|
||||
return to;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter de `body`
|
||||
* @return Le message sous la forme d'une chaine de caractères
|
||||
*/
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Représentation textuelle d'un message
|
||||
* @return La chaine de caractère représentant un message
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Message{" +
|
||||
|
|
@ -33,27 +48,30 @@ public class Message {
|
|||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Renvoie l'objet JSON représentant un message
|
||||
* @return L'objet JSON
|
||||
*/
|
||||
public JSONObject toJsonObject() {
|
||||
/**
|
||||
* Transforme le message courant en objet JSON
|
||||
*/
|
||||
return new JSONObject()
|
||||
.put("to", this.to)
|
||||
.put("body", this.body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sérialise dans une chaine de caractères la représentation JSON d'un message
|
||||
* @return La représentation textuelle associée à la représentation JSON d'un message
|
||||
*/
|
||||
public String toJson() {
|
||||
/**
|
||||
* Transforme l'objet courant en String JSON
|
||||
*/
|
||||
return toJsonObject().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Message sur la base d'une représentation JSON
|
||||
* @param json La représentation JSON
|
||||
* @return Un message
|
||||
*/
|
||||
public static Message fromJson(JSONObject json) {
|
||||
/**
|
||||
* Crée un objet message à partir d'un objet JSON
|
||||
* @param: JSONObject json: l'objet JSON à transformer
|
||||
*/
|
||||
return new Message(json.getString("to"), json.getString("body"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,28 +4,41 @@ import org.json.JSONObject;
|
|||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Classe modélisant un post échangé entre un émetteur et un destinataire privé ou public
|
||||
*/
|
||||
public class Post extends Message {
|
||||
|
||||
/** Identifiant unique du post */
|
||||
protected UUID id;
|
||||
/** Horodatage du post exprimé en nombre de millisecondes depuis le 01/01/1970 */
|
||||
protected long timestamp;
|
||||
/** Login du contact qui a envoyé ce post */
|
||||
protected String from;
|
||||
|
||||
|
||||
/**
|
||||
* Constructeur par défaut
|
||||
* @param id L'identifiant du post
|
||||
* @param timestamp Le timestamp du post
|
||||
* @param from L'émetteur du post
|
||||
* @param to Le destinataire du post
|
||||
* @param body Le contenu du post
|
||||
*/
|
||||
public Post(UUID id, long timestamp, String from, String to, String body) {
|
||||
/**
|
||||
* Crée un objet Post
|
||||
* @param: UUID id
|
||||
* @param: long timestamp
|
||||
* @param: String from
|
||||
* @param: String to,
|
||||
* @param: String body
|
||||
*/
|
||||
super(to, body);
|
||||
this.id = id;
|
||||
this.timestamp = timestamp;
|
||||
this.from = from;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructeur d'un post sur la base de son émetteur, son destinataire et son contenu. L'identifiant est choisi de manière unique aléatoirement ; le timestamp correspond à la date courante
|
||||
* @param from L'émetteur du post
|
||||
* @param to Le destinataire du post
|
||||
* @param body Le contenu du post
|
||||
*/
|
||||
public Post(String from, String to, String body) {
|
||||
super(to, body);
|
||||
this.from = from;
|
||||
|
|
@ -35,6 +48,11 @@ public class Post extends Message {
|
|||
this.id = UUID.randomUUID();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructeur à partir d'un Message
|
||||
* @param from Login de l'émetteur
|
||||
* @param message Message (incluant le destinataire et le contenu)
|
||||
*/
|
||||
public Post(String from, Message message) {
|
||||
super(message.to, message.body);
|
||||
this.from = from;
|
||||
|
|
@ -44,6 +62,11 @@ public class Post extends Message {
|
|||
this.id = UUID.randomUUID();
|
||||
}
|
||||
|
||||
/**
|
||||
* Egalité de deux posts, s'ils ont le même identifiant unique
|
||||
* @param o Le post auquel est comparé
|
||||
* @return true si sont égaux, false sinon
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
|
@ -57,18 +80,34 @@ public class Post extends Message {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter de l'identifiant
|
||||
* @return L'identifiant
|
||||
*/
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter du timestamp
|
||||
* @return Le timestamp
|
||||
*/
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter de l'émeteur
|
||||
* @return L'émetteur
|
||||
*/
|
||||
public String getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
/**
|
||||
* Représentation textuelle du post
|
||||
* @return La représentation textuelle du post
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Post{" +
|
||||
|
|
@ -80,6 +119,10 @@ public class Post extends Message {
|
|||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Objet JSON représentant un post
|
||||
* @return La représentation JSON d'un post
|
||||
*/
|
||||
@Override
|
||||
public JSONObject toJsonObject() {
|
||||
return new JSONObject()
|
||||
|
|
@ -90,11 +133,20 @@ public class Post extends Message {
|
|||
.put("body", this.body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sérialise dans une chaine de caractères la représentation JSON d'un objet.
|
||||
* @return La représentation textuelle associée à la représentation JSON d'un post
|
||||
*/
|
||||
@Override
|
||||
public String toJson() {
|
||||
return toJsonObject().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Création d'un post à partir d'un objet JSON représentant un post
|
||||
* @param jsonObject L'objet JSON représentant un post
|
||||
* @return Le post créé
|
||||
*/
|
||||
public static Post fromJson(JSONObject jsonObject) {
|
||||
return new Post(
|
||||
UUID.fromString(jsonObject.getString("id")),
|
||||
|
|
|
|||
|
|
@ -7,8 +7,16 @@ import java.util.Vector;
|
|||
|
||||
import static rtgre.chat.ChatApplication.LOGGER;
|
||||
|
||||
/**
|
||||
* Classe modélisant une liste de posts
|
||||
*/
|
||||
public class PostVector extends Vector<Post> {
|
||||
|
||||
/**
|
||||
* Extrait un post en fonction de son identifiant
|
||||
* @param uuid L'identifiant du post recherché
|
||||
* @return Le post correspondant
|
||||
*/
|
||||
public Post getPostById(UUID uuid) {
|
||||
for (Post post : this) {
|
||||
if (post.id == uuid) {
|
||||
|
|
@ -18,6 +26,11 @@ public class PostVector extends Vector<Post> {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renvoie la liste des posts, qui ont été créé à partir d'un timestamp donné
|
||||
* @param timestamp Le timestamp à partir duquel extraire les posts
|
||||
* @return La liste des posts extraits
|
||||
*/
|
||||
public Vector<Post> getPostsSince(long timestamp) {
|
||||
Vector<Post> posts = new Vector<>();
|
||||
for (Post post : this) {
|
||||
|
|
@ -28,6 +41,9 @@ public class PostVector extends Vector<Post> {
|
|||
return posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Charge la liste des posts depuis la base de données
|
||||
*/
|
||||
public void loadPosts() {
|
||||
try {
|
||||
DatabaseApi database = new DatabaseApi();
|
||||
|
|
|
|||
|
|
@ -5,38 +5,73 @@ import org.json.JSONObject;
|
|||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Classe modélisant un salon avec son nom et sa liste d'utilisateurs autorisés.
|
||||
*/
|
||||
public class Room {
|
||||
/** Le nom du salon */
|
||||
protected String roomName;
|
||||
/** La liste des utilisateurs autorisés à rejoindre le salon. Si `null`, tout le monde peut poster */
|
||||
protected HashSet<String> loginSet;
|
||||
/** Le compteur de messages non-lus */
|
||||
protected UnreadCount unreadCount = new UnreadCount();
|
||||
|
||||
|
||||
/**
|
||||
* Le getter associé à roomName
|
||||
* @return Le nom du salon
|
||||
*/
|
||||
public String getRoomName() {
|
||||
return roomName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Le getter associé au loginSet
|
||||
* @return La liste des utilisateurs autorisés à rejoindre le salon
|
||||
*/
|
||||
public HashSet<String> getLoginSet() {
|
||||
return loginSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructeur par défaut
|
||||
* @param roomName Le nom du salon
|
||||
*/
|
||||
public Room(String roomName) {
|
||||
this.roomName = roomName;
|
||||
this.loginSet = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abréviation du nom du salon avec la 1re lettre de son nom
|
||||
* @return La première lettre du nom du salon (# non compris)
|
||||
*/
|
||||
public String abbreviation() {
|
||||
return this.roomName.split("#")[1].substring(0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Représentation textuelle d'un salon
|
||||
* @return Le nom du salon
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.roomName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter de LoginSet
|
||||
* @param loginSet La liste des utilisateurs autorisés à se connecter au salon
|
||||
*/
|
||||
public void setLoginSet (HashSet<String> loginSet) {
|
||||
this.loginSet = loginSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Egalité de deux salons, s'ils ont le même nom
|
||||
* @param o Le salon auquel est comparé
|
||||
* @return true si sont égaux, false sinon
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
|
@ -45,6 +80,10 @@ public class Room {
|
|||
return Objects.equals(roomName, room.roomName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute un login au contact du salon (en créant le loginSet au besoin)
|
||||
* @param login Le login du contact à ajouter au salon
|
||||
*/
|
||||
public void add(String login) {
|
||||
if (loginSet == null) {
|
||||
loginSet = new HashSet<>();
|
||||
|
|
@ -52,16 +91,28 @@ public class Room {
|
|||
loginSet.add(login);
|
||||
}
|
||||
|
||||
/**
|
||||
* Représentation JSON d'un salon, incluant son nom et la liste des utilisateurs autorisés à y poster
|
||||
* @return La représentation JSON
|
||||
*/
|
||||
public JSONObject toJsonObject() {
|
||||
return new JSONObject()
|
||||
.put("room", this.roomName)
|
||||
.put("loginSet", this.loginSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Chaine de caractères associée à la représentation JSON d'un contact
|
||||
* @return La chaine de caractères correspondant à la représentation JSON
|
||||
*/
|
||||
public String toJson() {
|
||||
return this.toJsonObject().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter de l'unreadCount
|
||||
* @return Le compteur de messages non-lus
|
||||
*/
|
||||
public UnreadCount getUnreadCount() {
|
||||
return this.unreadCount;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,21 @@ package rtgre.modeles;
|
|||
import java.util.HashSet;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Modélise un annuaire des salons sous la forme d’un tableau associatif clé=“#nom de salon” ⇒ valeur=“objet Room”.
|
||||
*/
|
||||
public class RoomMap extends TreeMap<String, Room> {
|
||||
/**
|
||||
* Ajoute un salon à l'annuaire des salons
|
||||
* @param room Le salon à ajouter
|
||||
*/
|
||||
public void add(Room room) {
|
||||
this.put(room.getRoomName(), room);
|
||||
}
|
||||
|
||||
/**
|
||||
* Charge 4 salons dans l'annuaire des salons : "`#all`", "`#juniors`", "`#ducks`", "`#mice`"
|
||||
*/
|
||||
public void loadDefaultRooms() {
|
||||
this.add(new Room("#all"));
|
||||
this.add(new Room("#juniors"));
|
||||
|
|
@ -15,6 +25,9 @@ public class RoomMap extends TreeMap<String, Room> {
|
|||
this.add(new Room("#mice"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Charge les listes des utilisateurs autorisés pour les 4 salons chargés au préalable
|
||||
*/
|
||||
public void setLoginSets() {
|
||||
|
||||
HashSet<String> juniors = new HashSet<>();
|
||||
|
|
|
|||
|
|
@ -1,17 +1,33 @@
|
|||
package rtgre.modeles;
|
||||
|
||||
/**
|
||||
* Classe modélisant le compteur de messages non-lus
|
||||
*/
|
||||
public class UnreadCount {
|
||||
/** Le compteur */
|
||||
private int unreadCount = 0;
|
||||
|
||||
/**
|
||||
* Incrémente le compteur de posts reçus, mais non lus unreadCount et renvoie sa valeur.
|
||||
* @return Le compteur après incrémentation
|
||||
*/
|
||||
public int incrementUnreadCount() {
|
||||
unreadCount += 1;
|
||||
return unreadCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter de unreadCount
|
||||
* @param unreadCount La valeur à donner à unreadCount
|
||||
*/
|
||||
public void setUnreadCount(int unreadCount) {
|
||||
this.unreadCount = unreadCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter de unreadCount
|
||||
* @return Le compteur
|
||||
*/
|
||||
public int getUnreadCount() {
|
||||
return unreadCount;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,11 +23,19 @@ import static rtgre.chat.ChatApplication.LOGGER;
|
|||
*/
|
||||
public class ChatServer {
|
||||
|
||||
/** Liste des clients connectés */
|
||||
private Vector<ChatClientHandler> clientList;
|
||||
/** Liste des messages */
|
||||
private PostVector postVector;
|
||||
/** Annuaire des contacts */
|
||||
private ContactMap contactMap;
|
||||
/** Liste des salons */
|
||||
private RoomMap roomMap;
|
||||
/** Connexion à la base de données */
|
||||
private DatabaseApi database;
|
||||
/** Socket passif en écoute */
|
||||
private ServerSocket passiveSock;
|
||||
|
||||
|
||||
static {
|
||||
try {
|
||||
|
|
@ -39,23 +47,28 @@ public class ChatServer {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Le programme principal : instancie un serveur en écoute sur le port 2024 et le place en attente de clients.
|
||||
* @param args Arguments du programme principal
|
||||
* @throws IOException en cas de problème de connexion ou de base de données
|
||||
*/
|
||||
public static void main(String[] args) throws IOException {
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC");
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new IOException("Cannot connect to database");
|
||||
}
|
||||
ChatServer server = new ChatServer(2024);
|
||||
//daisyConnect();
|
||||
server.acceptClients();
|
||||
}
|
||||
|
||||
/**
|
||||
* Socket passif en écoute
|
||||
*/
|
||||
private ServerSocket passiveSock;
|
||||
|
||||
/**
|
||||
* Constructeur : initialisation du serveur, en écoute sur le port fourni
|
||||
* @param port Le port de connexion
|
||||
* @throws IOException si la connexion ne peut être établie
|
||||
*/
|
||||
public ChatServer(int port) throws IOException {
|
||||
passiveSock = new ServerSocket(port);
|
||||
LOGGER.info("Serveur en écoute " + passiveSock);
|
||||
|
|
@ -69,6 +82,26 @@ public class ChatServer {
|
|||
postVector.loadPosts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter de `PostVector`
|
||||
* @return La liste des posts
|
||||
*/
|
||||
public PostVector getPostVector() {
|
||||
return postVector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter de `roomMap`
|
||||
* @return La liste des salons
|
||||
*/
|
||||
public RoomMap getRoomMap() {
|
||||
return roomMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ferme la connexion du serveur, en fermant la connexion auprès de tous ses clients, puis en fermant son socket en écoute passive.
|
||||
* @throws IOException si la connexion
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
for (ChatClientHandler client : clientList) {
|
||||
client.close();
|
||||
|
|
@ -95,16 +128,28 @@ public class ChatServer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retire `client` de la liste des clients connectés `clientList`
|
||||
* @param client client à retirer de la liste `clientList`
|
||||
*/
|
||||
public void removeClient(ChatClientHandler client) {
|
||||
clientList.remove(client);
|
||||
LOGGER.fine("Client [%s] retiré de la liste (%d clients connectés)"
|
||||
.formatted(client.getIpPort(), clientList.size()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter de `clientList`
|
||||
* @return La liste des clients
|
||||
*/
|
||||
public Vector<ChatClientHandler> getClientList() {
|
||||
return clientList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter de passiveSocket
|
||||
* @return Le socket en écoute passive du serveur
|
||||
*/
|
||||
public ServerSocket getPassiveSocket() {
|
||||
return passiveSock;
|
||||
}
|
||||
|
|
@ -125,6 +170,11 @@ public class ChatServer {
|
|||
//client.echoLoop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renvoie le client de connexion (objet ChatServer.ChatClientHandler) associé à un contact
|
||||
* @param contact Le contact recherché
|
||||
* @return Le client de connexion associé ou `null` si le contact n'existe pas
|
||||
*/
|
||||
public ChatClientHandler findClient(Contact contact) {
|
||||
for (ChatClientHandler user: clientList) {
|
||||
if (user.user.equals(contact)) {
|
||||
|
|
@ -134,6 +184,11 @@ public class ChatServer {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoi d'un évènement event à un contact donné, sous réserve qu'il soit connecté. Si l'envoi échoue, ferme la connexion avec le contact.
|
||||
* @param contact Le contact destinataire
|
||||
* @param event L'évènement à envoyer
|
||||
*/
|
||||
public void sendEventToContact(Contact contact, Event event) {
|
||||
ChatClientHandler user = findClient(contact);
|
||||
if (!(user == null)) {
|
||||
|
|
@ -146,6 +201,10 @@ public class ChatServer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoi d'un évènement à tous les contacts connectés
|
||||
* @param event L'évènement à envoyer
|
||||
*/
|
||||
public void sendEventToAllContacts(Event event) {
|
||||
for (Contact contact: contactMap.values()) {
|
||||
if (contact.isConnected()) {
|
||||
|
|
@ -154,17 +213,28 @@ public class ChatServer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter de contactMap
|
||||
* @return La liste des contacts
|
||||
*/
|
||||
public ContactMap getContactMap() {
|
||||
return contactMap;
|
||||
}
|
||||
|
||||
/** Temporaire : connecte pour test */
|
||||
/**
|
||||
* Temporaire : connecte daisy pour test
|
||||
* @throws IOException si la connexion ne peut être établie
|
||||
*/
|
||||
public static void daisyConnect() throws IOException {
|
||||
ChatClient client = new ChatClient("localhost", 2024, null);
|
||||
client.sendAuthEvent(new Contact("daisy", null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gestion du dialogue avec un client TCP
|
||||
*/
|
||||
private class ChatClientHandler {
|
||||
/** Message de fin d'une connexion */
|
||||
public static final String END_MESSAGE = "fin";
|
||||
/**
|
||||
* Socket connecté au client
|
||||
|
|
@ -182,7 +252,7 @@ public class ChatServer {
|
|||
* Chaine de caractères "ip:port" du client
|
||||
*/
|
||||
private String ipPort;
|
||||
|
||||
/** Contact associé au client courant */
|
||||
private Contact user;
|
||||
|
||||
/**
|
||||
|
|
@ -191,7 +261,7 @@ public class ChatServer {
|
|||
* {@link #in} (flux de caractères UTF-8 en entrée).
|
||||
*
|
||||
* @param sock socket connecté au client
|
||||
* @throws IOException
|
||||
* @throws IOException si la connexion ne peut être établie ou si les flux ne peuvent être récupérés
|
||||
*/
|
||||
public ChatClientHandler(Socket sock) throws IOException {
|
||||
this.sock = sock;
|
||||
|
|
@ -225,6 +295,9 @@ public class ChatServer {
|
|||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Boucle de réception d'évènement : réceptionne les messages reçus et les délèguent à `handleEvent(java.lang.String)` pour les interpréter
|
||||
*/
|
||||
public void eventReceiveLoop() {
|
||||
try {
|
||||
String message = null;
|
||||
|
|
@ -249,6 +322,13 @@ public class ChatServer {
|
|||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Traitement d'un évènement. Ventile vers les méthodes traitant chaque type d'évènement.
|
||||
* @param message objet évènement sous la forme d'une chaine JSON brute de réception
|
||||
* @return `false` si l'évènement est de type Event.QUIT , `true` pour tous les autres types.
|
||||
* @throws JSONException si l'objet JSON n'est pas conforme
|
||||
* @throws IllegalStateException si l'authentification n'est pas effectuée
|
||||
*/
|
||||
private boolean handleEvent(String message) throws JSONException, IllegalStateException {
|
||||
Event event = Event.fromJson(message);
|
||||
if (event.getType().equals(Event.AUTH)) {
|
||||
|
|
@ -292,6 +372,10 @@ public class ChatServer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour un Post en fonction de son UUID
|
||||
* @param content le contenu d'un évènement "POST"
|
||||
*/
|
||||
private void doPost(JSONObject content) {
|
||||
database = new DatabaseApi();
|
||||
database.removePost(Post.fromJson(content));
|
||||
|
|
@ -303,6 +387,10 @@ public class ChatServer {
|
|||
LOGGER.info("didpost");
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour un contact et envoie à tous les autres utilisateurs la mise à jour
|
||||
* @param content Le contenu d'un évènement "CONT"
|
||||
*/
|
||||
private void doCont(JSONObject content) {
|
||||
if (user.isConnected()) {
|
||||
sendEventToAllContacts(new Event("CONT", content));
|
||||
|
|
@ -310,6 +398,10 @@ public class ChatServer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gère l'arrivée à un utilisateur dans un salon donné dans le contenu du message.
|
||||
* @param content Le contenu d'un évènement "JOIN"
|
||||
*/
|
||||
private void doJoin(JSONObject content) {
|
||||
if (content.getString("room").isEmpty()) {
|
||||
user.setCurrentRoom(null);
|
||||
|
|
@ -326,6 +418,10 @@ public class ChatServer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gère la demande d'envoi de la liste des salons : récupère tous les posts dont l'utilisateur est autorisé à accéder, puis les envoie un par un au client via des évènements "ROOM".
|
||||
* @param content Le contenu d'un évènement "LSTR"
|
||||
*/
|
||||
private void doListRoom(JSONObject content) {
|
||||
if (contactMap.getContact(user.getLogin()).isConnected()) {
|
||||
for (Room room: roomMap.values()) {
|
||||
|
|
@ -340,6 +436,12 @@ public class ChatServer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gère la demande d'envoi de la liste des posts : récupère tous les posts ayant trait au login ou au salon indiqué dans content et étant postérieur au timestamp indiqué dans content, puis les envoie un par un au client via des évènements "POST".
|
||||
* @param content Le contenu d'un évènement "LSTP"
|
||||
* @throws JSONException si le format JSON n'est pas respecté
|
||||
* @throws IllegalStateException si le login ou le salon demandé n'existent pas
|
||||
*/
|
||||
private void doListPost(JSONObject content) throws JSONException, IllegalStateException {
|
||||
if (contactMap.getContact(user.getLogin()).isConnected()) {
|
||||
if (!contactMap.containsKey(content.getString("select")) && !roomMap.containsKey(content.getString("select"))) {
|
||||
|
|
@ -365,6 +467,12 @@ public class ChatServer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gère la réception d'un message, en créant le Post associé et en l'envoyant à son destinataire privé ou aux membres d'un salon de discussion public
|
||||
* @param content Le contenu JSON représentant un message
|
||||
* @throws JSONException si le format JSON n'est pas respecté
|
||||
* @throws IllegalStateException si un évènement destiné à un contact ne peut être envoyé
|
||||
*/
|
||||
private void doMessage(JSONObject content) throws JSONException, IllegalStateException {
|
||||
if (contactMap.getContact(user.getLogin()).isConnected()) {
|
||||
if (content.getString("to").equals(user.getLogin()) ||
|
||||
|
|
@ -408,6 +516,12 @@ public class ChatServer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gère la demande de la liste des contacts : les contacts sont envoyés un par un au client sous la forme d'évènement "CONT"
|
||||
* @param content Le contenu de la demande de la liste des contacts
|
||||
* @throws JSONException si le format JSON n'est pas respecté
|
||||
* @throws IllegalStateException si un évènement destiné à un contact ne peut être envoyé
|
||||
*/
|
||||
private void doListContact(JSONObject content) throws JSONException, IllegalStateException {
|
||||
for (Contact contact: contactMap.values()) {
|
||||
if (contactMap.getContact(user.getLogin()).isConnected()) {
|
||||
|
|
@ -416,7 +530,18 @@ public class ChatServer {
|
|||
}
|
||||
}
|
||||
|
||||
private void doLogin(JSONObject content) {
|
||||
/**
|
||||
* Gère l'authentification d'un client en :
|
||||
* * récupérant son login dans content.
|
||||
* * en vérifiant qu'il fait partie des contacts autorisés dans l'annuaire des contacts.
|
||||
* * en modifiant son état de connexion dans l'annuaire des contacts.
|
||||
* * en informant les autres clients de la connexion.
|
||||
* Si aucun login n'est fourni, si le client n'est pas autorisé à se connecter, ou si le client s'authentifie alors qu'il est déjà connecté, une exception IllegalStateException est levée.
|
||||
* @param content Le contenu de la demande
|
||||
* @throws JSONException si le format JSON n'est pas respecté
|
||||
* @throws IllegalStateException si l'utilisateur n'est pas autorisé à se connecter ou s'il est déjà connecté
|
||||
*/
|
||||
private void doLogin(JSONObject content) throws JSONException, IllegalStateException {
|
||||
String login = content.getString("login");
|
||||
if (login.isEmpty()) {
|
||||
LOGGER.warning("Aucun login fourni");
|
||||
|
|
@ -436,6 +561,11 @@ public class ChatServer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie une chaine de caractères
|
||||
* @param message Chaine de caractères à transmettre
|
||||
* @throws IOException lorsqu'une erreur sur le flux de sortie est détectée
|
||||
*/
|
||||
public void send(String message) throws IOException {
|
||||
LOGGER.finest("send: %s".formatted(message));
|
||||
out.println(message);
|
||||
|
|
@ -444,10 +574,19 @@ public class ChatServer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter de ipPort
|
||||
* @return L'IP et le port du client
|
||||
*/
|
||||
public String getIpPort() {
|
||||
return ipPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie un message à tous les autres clients que le client courant
|
||||
* @param fromClient Le client courant
|
||||
* @param message Le message à envoyer
|
||||
*/
|
||||
public void sendAllOtherClients(ChatClientHandler fromClient, String message) {
|
||||
for (ChatClientHandler client : clientList) {
|
||||
if (!client.equals(fromClient)) {
|
||||
|
|
@ -463,6 +602,11 @@ public class ChatServer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attente d'une chaine de caractères en entrée.
|
||||
* @return chaine de caractères reçue
|
||||
* @throws IOException lorsque la fin du flux est atteinte
|
||||
*/
|
||||
public String receive() throws IOException {
|
||||
String message = in.readLine();
|
||||
LOGGER.info("receive: %s".formatted(message));
|
||||
|
|
@ -472,6 +616,9 @@ public class ChatServer {
|
|||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ferme la connexion TCP
|
||||
*/
|
||||
public void close() {
|
||||
LOGGER.info("[%s] Fermeture de la connexion".formatted(ipPort));
|
||||
try {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue