547 lines
17 KiB
Java
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();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|