Clavardage/Application/Clavardage/src/controller/Controller.java
2021-02-13 13:29:22 +01:00

547 lines
17 KiB
Java

package controller;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import javax.swing.JOptionPane;
import javax.websocket.DeploymentException;
import model.Chat;
import model.LocalUser;
import model.Message;
import model.Msg_Text;
import model.RemoteUser;
import view.Interface;
import websocket.Appel;
public class Controller {
/*** CONSTANTES ***/
final int NB_SECOND_WAITING_RESPONSE_BROADCAST = 1;
// TO REMOVE when we use broadcast
final static int portUDPlistening_remoteUsr1 = 31001;
final static int portUDPlistening_remoteUsr2 = 31002;
final static int portUDPlistening_remoteUsr3 = 31003;
final static int portUDPlistening_local = 31004;
final static int [] tabBroadcast = {portUDPlistening_remoteUsr1,portUDPlistening_remoteUsr2,portUDPlistening_remoteUsr3,portUDPlistening_local};
public Boolean interfaceRunning = false;
/*** ATTRIBUTS ***/
protected LocalUser myUser;
protected Interface view;
private TListeningUDP udp_connect_thread;
private TListeningTCPConnection tcp_connect_thread;
private Historique histoire;
protected Chat activeChat;
/**
* Constructor of Controller
* @parametres
* @param portUDPsend : int => le numéro de port pour envoyer ces informations lors d'un changements ou d'une nouvelle connexion
* @param portUDPlistening : int => le numéro de port pour recevoir les informations des nouveaux utilisateurs ou les changements
* @param portTCP : int => le numéro de port pour commencer une nouvelle conversation
* @throws IOException
* @descrition
* <p>
* On récupère l'adresse de la machine, on demande un pseudo à l'utilisateur que l'on vérifie
* Une fois validé l'utilisateur devient actif :
* - écoute UDP pour les pseudos
* - écoute TCP pour de nouvelles conversation
* - notification aux autres utilisateurs actifs
* </p>
*/
private Controller(int portUDPsend,int portUDPlistening,int portTCP,Historique histoire) throws IOException {
this.histoire= histoire;
// Récupération de l'adresse IP local
InetAddress addIP = null;
try
{
addIP = InetAddress.getLocalHost();
}
catch(UnknownHostException e) {
JOptionPane.showMessageDialog(null ,"Could not find local address!");
}
// Création de l'utilisateur
this.myUser = new LocalUser("Unknown",addIP,portUDPsend,portUDPlistening,portTCP);
try {
this.myUser.setPseudo(this.initPseudo()); // Initialisation du pseudo manuel
} catch (IOException e) {
e.printStackTrace();
}
// Création des threads d'écoutes
this.udp_connect_thread = new TListeningUDP("UDP Listening thread",this);
this.udp_connect_thread.start();
this.tcp_connect_thread = new TListeningTCPConnection("TCP main Listening thread",this);
this.tcp_connect_thread.start();
// Notification des utilisateurs distants
notify_remote_users();
// Création de l'interface
interfaceRunning =true;
view=Interface.createAndShowGUI(this);
}
/**
* Constructor of Controller
* @parametres
* @param portUDPsend : int => le numéro de port pour envoyé ces informations lors d'un changements ou d'une nouvelle connexion
* @param portUDPlistening : int => le numéro de port pour recevoir les informations des nouveaux utilisateurs ou les changements
* @param portTCP : int => le numéro de port pour commencer une nouvelle conversation
* @descrition
* <p>
* On récupère l'adresse de la machine, on demande un pseudo à l'utilisateur que l'on vérifie
* Une fois validé l'utilisateur devient actif :
* - écoute UDP pour les pseudos
* - écoute TCP pour de nouvelles conversation
* - notification aux autres utilisateurs actifs
* </p>
*/
private Controller(int portUDPsend,int portUDPlistening,int portTCP,String pseudo,Historique histoire) throws IOException{
this.histoire=histoire;
// Récupération de l'adresse IP local
InetAddress addIP = null;
try
{
addIP = InetAddress.getLocalHost();
}
catch(UnknownHostException e) {
JOptionPane.showMessageDialog(null ,"Could not find local address!");
}
// Création de l'utilisateur
this.myUser = new LocalUser(pseudo,addIP,portUDPsend,portUDPlistening,portTCP);
try {
if(this.validatePseudo(pseudo)) {
this.udp_connect_thread = new TListeningUDP("UDP Listening thread",this);
this.udp_connect_thread.start();
this.tcp_connect_thread = new TListeningTCPConnection("TCP main Listening thread",this);
this.tcp_connect_thread.start();
// Notification des utilisateurs distants
notify_remote_users();
// Création de l'interface
interfaceRunning =true;
view=Interface.createAndShowGUI(this);
}
else {
System.out.println("Unavailable "+pseudo);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/*** GETTERS ***/
public LocalUser getMyUser() {
return myUser;
}
public Interface getview() {
return view;
}
public TListeningUDP getUdp_connect_thread() {
return udp_connect_thread;
}
public TListeningTCPConnection getTcp_connect_thread() {
return tcp_connect_thread;
}
public Historique getHistory() {
return histoire;
}
/*** SETTERS ***/
public void setMyUser(LocalUser myUser) {
this.myUser = myUser;
}
public void setview(Interface view) {
this.view = view;
}
public void setUdp_connect_thread(TListeningUDP udp_connect_thread) {
this.udp_connect_thread = udp_connect_thread;
}
public void setTcp_connect_thread(TListeningTCPConnection tcp_connect_thread) {
this.tcp_connect_thread = tcp_connect_thread;
}
public void setHistory(Historique histoire) {
this.histoire=histoire;
}
/**************************** Initialisation pseudo et découverte utilisateur distant (+notification utilisateurs distants) **********************************/
/** initPseudo
* <p>
* Demande à l'utilisateur de rentrer un pseudo et valide de ce dernier en demandant aux
* utilisateurs distants leurs informations
* </p>
*/
public String initPseudo() throws IOException {
String tmpPseudo = JOptionPane.showInputDialog(null, "Enter nickname:"); // Read user input
while(!(this.validatePseudo(tmpPseudo))) { // On demande aux autres utilisateurs de nous envoyer leurs informations et on test si le pseudo est déjà utilisé
tmpPseudo = JOptionPane.showInputDialog(null, "Enter another nickname:"); // Read user input
}
//sc1.close();
JOptionPane.showMessageDialog(null ,"Your nickname : " + tmpPseudo + " is valid !");
return tmpPseudo;
}
/** changePseudo
* <p>
* Demande à l'utilisateur de rentrer un pseudo et valide de ce dernier en demandant aux
* utilisateurs distants leurs informations.
* On regarde si le pseudo est bien différent de l'ancien
* </p>
*/
public void changePseudo() throws IOException {
String oldPseudo = this.myUser.getPseudo(); //Saves the old one for comparison
String tmpPseudo = view.ChangePseudotextField.getText(); // Read user input
if(!(this.validatePseudo(tmpPseudo)) || tmpPseudo.equals(oldPseudo)) {
view.Pseudolabel.setText("Already exists, enter another nickname. Your current username is: " + oldPseudo); // Read user input
tmpPseudo = view.ChangePseudotextField.getText(); // Read user input
}
else {
this.myUser.setPseudo(tmpPseudo);
JOptionPane.showMessageDialog(null ,"Your new nickname : " + tmpPseudo + " is valid !");
this.notify_remote_users();
}
}
/** validatePseudo
* <p>
* *tmpPseudo : String => Le pseudo à valider
*</p><p>
* Envoi en broadcast (pour l'instant envoi sur les ports de notre ordinateur) d'une demande d'information
*</p><p>
* On attend les réponses pendant 5 secondes
*</p><p>
* On valide le pseudo en fonction des pseudos reçu
*</p><p>
* On en profite pour ajouter les utilisateurs répondant à notre liste d'utilisateur distant (RemoteUser)
* </p>
*/
public Boolean validatePseudo(String tmpPseudo) throws IOException {
Boolean valid = true;
DatagramSocket dgramSocket = new DatagramSocket(this.myUser.getPortUDPsend(),this.myUser.getAddIP());
// Création du message à envoyer
String toSend = this.myUser.getAddIP().getHostAddress()+":"+this.myUser.getPortUDPsend()+":info";
// Broadcast du message
broadcast(dgramSocket,toSend);
/*** For 5 seconds wait for answer : validate pseudo & add to userlist ***/
byte[] buffer = new byte[256];
DatagramPacket inPacket;
String response = null;
String[] tabresponse= new String [4];
dgramSocket.setSoTimeout(1000);
Boolean arecu;
int nbReponse =0;
System.out.println("("+myUser.getPseudo()+") Waiting for pseudo validation ...");
Date oldDate = new Date();
Date newDate = new Date();
while( (newDate.getTime()-oldDate.getTime()) < NB_SECOND_WAITING_RESPONSE_BROADCAST*1000 && valid) {
nbReponse++;
inPacket= new DatagramPacket(buffer, buffer.length);
arecu=true;
try{
dgramSocket.receive(inPacket);
}catch (Exception e) {
arecu=false;
}
buffer = inPacket.getData();
response = new String(buffer);
if(arecu) {
// On découpe la réponse en tableau de string ([adresseIP,tcpPort,nickname])
tabresponse = response.split(":");
// Si reception on ajoute l'utilisateur à notre liste d'utilisateur distant
this.myUser.addRemoteUser(InetAddress.getByName(tabresponse[0]),Integer.parseInt(tabresponse[1]),tabresponse[2]);
valid= (tmpPseudo.compareTo(tabresponse[2])!=0); // On regarde la différence entre notre pseudo et le pseudo reçu
}
newDate = new Date();
}
dgramSocket.close();
if(!valid) {
JOptionPane.showMessageDialog(null ,"Nickname : "+tmpPseudo +" is taken !");
}
return valid;
}
/***broadcast
*
* @param dgramSocket
* @param toSend
* @param broadcastIP
* @throws IOException
* <p>
* Simulation of broadcast with port given (tabBroadcast)
* <p>
*/
public void broadcast(DatagramSocket dgramSocket,String toSend) throws IOException {
InetAddress broadcastIP = InetAddress.getLocalHost();
DatagramPacket outPacket = null;
int tabBroadcastSize = tabBroadcast.length;
for(int i=0;i<tabBroadcastSize;i++) {
if(tabBroadcast[i]!=myUser.getPortUDPlistening()) {
outPacket= new DatagramPacket(toSend.getBytes(), toSend.length(),broadcastIP, tabBroadcast[i]);
dgramSocket.send(outPacket);
}
}
}
/** notify_remote_users
* <p>
* En utilisant le port UDP d'envoi, on envoie en broadcast les informations nous concernant
* </p>
* @throws IOException
*/
public void notify_remote_users() throws IOException {
DatagramSocket dgramSocket= null;
try {
dgramSocket= new DatagramSocket(this.myUser.getPortUDPsend(),this.myUser.getAddIP());
} catch (IOException e) {
e.printStackTrace();
}
// Send to other active user (simulation of broadcast)
String toSend = this.myUser.getAddIP().getHostAddress()+":"+this.myUser.getPortTCP()+":"+this.myUser.getPseudo()+":notify";
broadcast(dgramSocket,toSend);
dgramSocket.close();
}
/**************************** Gestion des sessions **********************************/
public Chat openSession(RemoteUser rm) {
Chat c = myUser.addChats(rm); // Create chat and add it to myUser
/*** Create socket send => ask connection to server of rm ***/
Socket link=null;
try {
// Connection => server tcp rm
System.out.println("("+this.myUser.getPseudo()+") Connecting to "+c.getRemoteUser().getPortTCP()+" of " + c.getRemoteUser().getPseudo());
link=new Socket(c.getRemoteUser().getAddIP(),c.getRemoteUser().getPortTCP()/*, InetAddress.getLocalHost() ,myUser.getPortTCP()*/);
c.setSocket(link);
// Sending data for identification (TO REMOVE IF != IP)
sendInfoProcess(Integer.toString(myUser.getPortTCP()), c); // tell rm session STOP
}catch(IOException e) {
System.out.println("Error linking to TCP server of "+ c.getRemoteUser().getPortTCP());
}
/*** recup history and put it in model ***/
try {
try {
c.addListMessage(this.getHistory().retrieveMessage(getMyUser(), c.getRemoteUser()));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (SQLException e) {
System.out.println("souci avec le retrieveMsgSQL");
e.printStackTrace();
}
return c;
}
public String askOpenSession(int index) {
String history="";
RemoteUser rm = myUser.getRemoteUsersList().get(index);
Chat c;
/*** Recup or create chat ***/
if(myUser.getChatIndexOf(rm)!=-1) { // if already a chat open
c = myUser.getChats().get(myUser.getChatIndexOf(rm));
JOptionPane.showMessageDialog(null ,"Active session with "+c.getRemoteUser().getPseudo()+" loading history");
}
else { // else create it
c=openSession(rm);
JOptionPane.showMessageDialog(null ,"New session with "+rm.getPseudo());
}
// Look for history
int nbMessage = c.getMessages().size();
for(int i=0;i<nbMessage;i++) {
history+="\n("+c.getMessages().get(i).getDate()+")\n"+c.getMessages().get(i).getAutor().getPseudo()+" : "+c.getMessages().get(i).getMessage();
}
this.activeChat = c;
return history;
}
public void askCloseSession() {
closeSession(this.activeChat);
this.activeChat = null;
}
public void closeSession(Chat c) {
sendInfoProcess("end", c); // tell rm session STOP
JOptionPane.showMessageDialog(null ,"Close session with "+c.getRemoteUser().getPseudo());
this.myUser.closeChat(c);
}
/**************************** Gestion des messages **********************************/
public void askToSend(String textMessage){
sendMessage(new Msg_Text(myUser,textMessage), this.activeChat);
}
public void sendMessage(Message msg,Chat c) {
/*** Init send process ***/
PrintWriter out=null;
try {
out = new PrintWriter(c.getUserSocket().getOutputStream(),true);
} catch (IOException e) {
e.printStackTrace();
}
c.addMessage(msg);
// TODO check if instance of Msg_Text or anything else and threat it given a type
String message = (String) msg.getMessage();
// Sauvegarde dans la base de données
DateFormat dateFormat = new SimpleDateFormat("yyyy MM dd HH mm ss");
Date date=new Date();
this.getHistory().saveMessage(getMyUser(), c.getRemoteUser(),message ,dateFormat.format(date));
// Send message
out.println(message);
}
public void sendInfoProcess(String msg,Chat c) {
PrintWriter out=null;
try {
out = new PrintWriter(c.getUserSocket().getOutputStream(),true);
} catch (IOException e) {
e.printStackTrace();
}
out.println(msg);
}
/*
public String askNewMessage() {
String message = "";
return message;
}*/
public String [] askUpdateActiveUsers() {
String[] pseudotab = new String[myUser.getRemoteUsersList().size()];
int size = myUser.getRemoteUsersList().size();
for(int i=0; i < size; i++) {
pseudotab[i] = myUser.getRemoteUsersList().get(i).getPseudo();
}
return pseudotab;
}
public void askForClose() {
interfaceRunning = false;
close();
}
public void close() {
if(interfaceRunning) {
myUser.closeAllChat();
tcp_connect_thread.close();
udp_connect_thread.close();
}
}
public static void main(String[] args) throws IOException, InterruptedException {
System.out.println("start program");
Historique histoire=new Historique();
/** Création des utilisateurs **/
ArrayList<Controller> lstCtr = new ArrayList<Controller>();
// REMOTE USERS
Controller ctr1 = new Controller(31011,portUDPlistening_remoteUsr1,31021,"Theau",histoire);
lstCtr.add(ctr1);
Controller ctr2 = new Controller(31012,portUDPlistening_remoteUsr2,31022,"Leonie",histoire);
lstCtr.add(ctr2);
Controller ctr3 = new Controller(31013,portUDPlistening_remoteUsr3,31023,"Alexandre",histoire);
lstCtr.add(ctr3);
// LOCAL USER
Controller ctr = new Controller(31014,portUDPlistening_local,31024,histoire);
lstCtr.add(ctr);
Appel app=new Appel();
app.test();
/** Loop **/
Boolean running = isRunning(lstCtr);
while(running) {
running = isRunning(lstCtr);
}
System.out.println("Fin de la boucle");//TOREMOVE
/** End - Close thread and socket for every controller**/
closeThreads(lstCtr);
System.out.println("end program");
JOptionPane.showMessageDialog(null ,"END");
}
static boolean isRunning(ArrayList<Controller> lstCtr){
boolean isRunning=false;
for(Controller ctr : lstCtr) {
isRunning|=ctr.interfaceRunning;
}
return isRunning;
}
static void closeThreads(ArrayList<Controller> lstCtr) {
for(Controller ctr : lstCtr) {
ctr.close();
}
}
}