No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Model.java 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. package app.insa.clav.Core;
  2. import app.insa.clav.Messages.Message;
  3. import app.insa.clav.Messages.MessageChatTxt;
  4. import app.insa.clav.Messages.MessageInit;
  5. import app.insa.clav.Messages.MessagePseudo;
  6. import app.insa.clav.Reseau.*;
  7. import javafx.application.Application;
  8. import javafx.application.Platform;
  9. import jdk.jshell.execution.Util;
  10. import java.beans.PropertyChangeEvent;
  11. import java.beans.PropertyChangeListener;
  12. import java.beans.PropertyChangeSupport;
  13. import java.io.IOException;
  14. import java.nio.charset.StandardCharsets;
  15. import java.util.*;
  16. import java.net.*;
  17. //Toutes les interactions avec l'utilisateur (pour tester)
  18. /**
  19. * Notre application principale. Une seule instance. représente le modèle du MVC
  20. */
  21. public class Model implements PropertyChangeListener{
  22. /**
  23. * Liste des utilisateurs connectés.
  24. */
  25. private ArrayList<Utilisateurs> userList;
  26. private ServletConnection servCon;
  27. /**
  28. * Timer qui permet de planifier des éxécutions dans le temps
  29. */
  30. private Timer tim;
  31. /**
  32. * Notre interface UDP pour envoyer des messages
  33. */
  34. private UDPOutput UDPOut;
  35. /**
  36. * Interface UDP qui notifie quand un message est envoyé (observable)
  37. */
  38. private UDPInput UDPIn;
  39. /**
  40. * Infos sur notre utilisateur local
  41. */
  42. public Utilisateurs user;
  43. /**
  44. * Sert à stocker le pseudo de base quand on veut change de pseudo. Si la demande de pseudo est refusée, on revient à ce pseudo
  45. */
  46. private String ancienPseudo = "";
  47. /**
  48. * Observable, pour envoyer des notifications aux controleurs
  49. */
  50. private PropertyChangeSupport support;
  51. /**
  52. * Instance of the Model (Singleton Design Pattern)
  53. */
  54. private static Model instance = null;
  55. /**
  56. * Vrai si pseudo ok, faux sinon
  57. */
  58. private boolean isPseudoOk = true;
  59. /**
  60. * List of all TCPChatConnextions that are used
  61. */
  62. private ArrayList<TCPChatConnection> listTCPConnection;
  63. /**
  64. * Instance of TCPListener to listen for chat oppening demands
  65. */
  66. private TCPListener tcpListener;
  67. private DataBaseAccess dbAccess;
  68. /**
  69. * Instance of the Main JavaFx Application
  70. */
  71. private Application app;
  72. /**
  73. * Constructeur
  74. * Id de l'utilisateur (unique dans toutes les machines)
  75. * @param inputPort
  76. * Port d'input UDP
  77. * @param outputPort
  78. * Port d'Output UDP
  79. */
  80. /*
  81. ID 1 -> Listening on 6000, sending on 5000, tcpServer on 7000
  82. ID 2 -> Listening on 6001, sending on 5001, tcpServer on 7001
  83. ID 2 -> Listening on 6002, sending on 5002, tcpServer on 7002
  84. */
  85. private Model(int inputPort, int outputPort, int tcpListenerPort, Application app){
  86. try {
  87. this.user = new Utilisateurs("NA", InetAddress.getLocalHost(), 0, inputPort, false);
  88. this.UDPOut = new UDPOutput(InetAddress.getLocalHost(), outputPort);
  89. this.UDPIn = new UDPInput(user.getInetAddress(),inputPort);
  90. this.tcpListener = new TCPListener(this.user.getInetAddress(),tcpListenerPort,user.getId());
  91. this.tim= new Timer();
  92. this.support = new PropertyChangeSupport(this);
  93. }
  94. catch (IOException e){
  95. System.out.println("IOException dans la creation de l'utilisateur local");
  96. e.printStackTrace();
  97. }
  98. this.userList = new ArrayList<Utilisateurs>();
  99. this.listTCPConnection = new ArrayList<TCPChatConnection>();
  100. this.dbAccess = DataBaseAccess.getInstance();
  101. this.app = app;
  102. this.servCon = ServletConnection.getInstance();
  103. }
  104. /**
  105. * Returns the instance of the Model (and creates it if null)
  106. * id of user
  107. * @param inputPort
  108. * input Port UDP
  109. * @param outputPort
  110. * outPutPort UDP
  111. * @return
  112. * instance of Model
  113. */
  114. public static Model getInstance(int inputPort, int outputPort, int tcpListenerPort, Application app){
  115. synchronized(Model.class){
  116. if (instance == null) {
  117. instance = new Model(inputPort, outputPort,tcpListenerPort, app);
  118. }
  119. }
  120. return instance;
  121. }
  122. /**
  123. * getInstance, but no parameters (dont crete if not existing
  124. * @return instance of Model
  125. */
  126. public static Model getInstance(){
  127. return instance;
  128. }
  129. /**
  130. * Ouvre le thread d'écoute en UDP et ajoute l'application en observateur de ce thread
  131. */
  132. public void openInputUDP(){
  133. UDPIn.start();
  134. this.UDPIn.addPropertyChangeListener(this);
  135. }
  136. public void openTCPListener(){
  137. tcpListener.start();
  138. this.tcpListener.addPropertyChangeListener(this);
  139. }
  140. /**
  141. * permet de sauvegarder un socket TCP clanvardage qui vient d'être créé
  142. * @param co
  143. */
  144. public void addTCPConnexion(TCPChatConnection co){
  145. this.listTCPConnection.add(co);
  146. co.addPropertyChangeListener(this);
  147. }
  148. /**
  149. * Ajout un listener avec un nom de propriété.
  150. *
  151. * Liste des propriete :
  152. *
  153. * "pseudoRefused" -> Demande de nouveau pseudo refusée
  154. * "userListUpdated" -> La liste des utilisateurs connectés à changé
  155. * "pseudoAccepted" -> nouveau pseudo accepte
  156. *
  157. *
  158. * @param pcl
  159. * Listener du controller qui a appelé la methode
  160. * @param propertyName
  161. */
  162. public void addPropertyChangeListener(PropertyChangeListener pcl, String propertyName){
  163. this.support.addPropertyChangeListener(propertyName, pcl);
  164. }
  165. public void deletePropertyChangeListener(PropertyChangeListener pcl, String propertyName){
  166. this.support.removePropertyChangeListener(propertyName,pcl);
  167. }
  168. /**
  169. * Envoi un messagePseudo de type 1 aux 3 machines de test
  170. */
  171. public void sendPseudoBroadcast(){
  172. System.out.println("Send pseudo broadcast with" + this.user.getPseudo());
  173. try {
  174. if (user.getId() == 1 || user.getId() == 2) {
  175. MessagePseudo msg = new MessagePseudo(1, this.user.getInetAddress(), this.user.getPort(), InetAddress.getLocalHost(), 6002, this.user.getPseudo(),this.user.getId());
  176. UDPOut.sendMsg(msg);
  177. }
  178. if (user.getId() == 2 || user.getId() == 3) {
  179. MessagePseudo msg = new MessagePseudo(1, this.user.getInetAddress(), this.user.getPort(), InetAddress.getLocalHost(), 6000, this.user.getPseudo(),this.user.getId());
  180. UDPOut.sendMsg(msg);
  181. }
  182. if (user.getId() == 1 || user.getId() == 3) {
  183. MessagePseudo msg = new MessagePseudo(1, this.user.getInetAddress(), this.user.getPort(), InetAddress.getLocalHost(), 6001,this.user.getPseudo(),this.user.getId());
  184. UDPOut.sendMsg(msg);
  185. }
  186. }
  187. catch (UnknownHostException e){
  188. System.out.println(("exception Trouver host dans sendPseudoBroadcast"));
  189. e.printStackTrace();
  190. }
  191. }
  192. /**
  193. * Envoi message de type 4 (confirmation pseudo)
  194. */
  195. public void sendPseudoValideBroadcast(){
  196. System.out.println("Send pseudo Valide broadcast with" + this.user.getPseudo());
  197. try {
  198. if (user.getId() == 1 || user.getId() == 2) {
  199. MessagePseudo msg = new MessagePseudo(4, this.user.getInetAddress(), this.user.getPort(), InetAddress.getLocalHost(), 6002, this.user.getPseudo(),this.user.getId());
  200. UDPOut.sendMsg(msg);
  201. }
  202. if (user.getId() == 2 || user.getId() == 3) {
  203. MessagePseudo msg = new MessagePseudo(4, this.user.getInetAddress(), this.user.getPort(), InetAddress.getLocalHost(), 6000, this.user.getPseudo(),this.user.getId());
  204. UDPOut.sendMsg(msg);
  205. }
  206. if (user.getId() == 1 || user.getId() == 3) {
  207. MessagePseudo msg = new MessagePseudo(4, this.user.getInetAddress(), this.user.getPort(), InetAddress.getLocalHost(), 6001,this.user.getPseudo(),this.user.getId());
  208. UDPOut.sendMsg(msg);
  209. }
  210. }
  211. catch (UnknownHostException e){
  212. System.out.println(("exception Trouver host dans sendPseudoBroadcast"));
  213. e.printStackTrace();
  214. }
  215. }
  216. /**
  217. * Méthode appelée par le controleur quand la vue envoie un signal d'appuis bouton changer Pseudo
  218. * @param pseudo
  219. * Pseudo rentré par l'utilisateur
  220. */
  221. public void choosePseudo(String pseudo, boolean isConfirmationNeeded){
  222. this.ancienPseudo = this.user.getPseudo();
  223. this.user.setPseudo(pseudo);
  224. this.UDPIn.setFilterValue(2, true);
  225. this.UDPIn.setFilterValue(3, true);
  226. if (!this.user.isOutdoor()) {
  227. this.sendPseudoBroadcast();
  228. this.tim.schedule(new TimerTaskResponseWait(isConfirmationNeeded), 2000);
  229. ArrayList<Utilisateurs> outdoorUsers = servCon.getRemoteActiveUsers();
  230. for (Utilisateurs newUser : outdoorUsers) {
  231. if (newUser.getPseudo().equals(this.user.getPseudo())) {
  232. this.isPseudoOk = false;
  233. this.user.setPseudo(this.ancienPseudo);
  234. this.ancienPseudo = "";
  235. this.support.firePropertyChange("pseudoRefused", this.user.getPseudo(), this.ancienPseudo);
  236. }
  237. if (!this.userList.contains(newUser)) {
  238. this.userList.add(newUser);
  239. Collections.sort(this.userList);
  240. this.support.firePropertyChange("newUserConnected", -1, -2);
  241. }
  242. }
  243. }
  244. else{
  245. this.tim.schedule(new TimerTaskResponseWait(isConfirmationNeeded), 2000);
  246. ArrayList<Utilisateurs> users = servCon.getAllActiveUsers();
  247. for (Utilisateurs newUser : users) {
  248. if (newUser.getPseudo().equals(this.user.getPseudo())) {
  249. this.isPseudoOk = false;
  250. this.user.setPseudo(this.ancienPseudo);
  251. this.ancienPseudo = "";
  252. this.support.firePropertyChange("pseudoRefused", this.user.getPseudo(), this.ancienPseudo);
  253. }
  254. if (!this.userList.contains(newUser)) {
  255. this.userList.add(newUser);
  256. Collections.sort(this.userList);
  257. this.support.firePropertyChange("newUserConnected", -1, -2);
  258. }
  259. }
  260. }
  261. }
  262. /** Sets the id
  263. * @param id
  264. */
  265. public void setUserId(int id){
  266. this.user.setId(id);
  267. }
  268. /** Creates a Chat Room and TCPConnection between remote id and local id when local user initiates it
  269. * @param remoteId
  270. * @param remotePseudo
  271. */
  272. public void createChatFromLocalRequest(int remoteId, String remotePseudo){
  273. boolean isChatAlreadyCreated = false;
  274. for (TCPChatConnection tcpCo : listTCPConnection){
  275. if (tcpCo.remoteUserId == remoteId){
  276. isChatAlreadyCreated = true;
  277. break;
  278. }
  279. }
  280. if (!isChatAlreadyCreated) {
  281. for (Utilisateurs u : userList) {
  282. if (u.getPseudo().equals(remotePseudo)) {
  283. int destPort;
  284. if (u.getId() == 1) {
  285. destPort = 7000;
  286. } else if (u.getId() == 2) {
  287. destPort = 7001;
  288. } else {
  289. destPort = 7002;
  290. }
  291. MessageInit msgInit = new MessageInit(7, user.getInetAddress(), user.getPort(), u.getInetAddress(), destPort, user.getId());
  292. TCPChatConnection tcpCo = new TCPChatConnection(msgInit, u.getId());
  293. listTCPConnection.add(tcpCo);
  294. }
  295. }
  296. }
  297. }
  298. /**
  299. * Handler de notification (Obsevateur) pour le thread UDP.
  300. * @param evt
  301. * Evenement qui est envoyé avec la notification
  302. */
  303. public void propertyChange(PropertyChangeEvent evt){
  304. switch (evt.getPropertyName()){
  305. case "UDPInput":
  306. Message msgReceived = UDPIn.getMessageReceived();
  307. this.messageHandler(msgReceived);
  308. break;
  309. case "chatCreated" :
  310. TCPChatConnection tcpCo = this.tcpListener.getTCPChatConnection();
  311. tcpCo.addPropertyChangeListener(this);
  312. this.listTCPConnection.add(tcpCo);
  313. break;
  314. }
  315. }
  316. /**
  317. * Permet de faire les actions adéquates selon le type de message reçu. Appelé dans propertyChange
  318. * @param msg
  319. * message à analyser
  320. */
  321. public void messageHandler(Message msg){
  322. switch (msg.typeMessage){
  323. case 1 :
  324. MessagePseudo msgP1 = (MessagePseudo) msg;
  325. this.handleType1Message(msgP1);
  326. break;
  327. case 2 :
  328. MessagePseudo msgP2 = (MessagePseudo) msg;
  329. Utilisateurs newUser2 = new Utilisateurs(msgP2.pseudo,msgP2.srcIP,msgP2.id,msgP2.srcResponsePort,false);
  330. if (!this.userList.contains(newUser2)) {
  331. this.userList.add(newUser2);
  332. Collections.sort(this.userList);
  333. this.support.firePropertyChange("newUserConnected",-1,-2);
  334. }
  335. break;
  336. case 3 :
  337. System.out.println("Received message type 3");
  338. MessagePseudo msgP3 = (MessagePseudo) msg;
  339. Utilisateurs newUser3 = new Utilisateurs(msgP3.pseudo,msgP3.srcIP,msgP3.id,msgP3.srcResponsePort,false);
  340. if (!this.userList.contains(newUser3)) {
  341. this.userList.add(newUser3);
  342. Collections.sort(this.userList);
  343. this.support.firePropertyChange("newUserConnected",-1,-2);
  344. }
  345. this.isPseudoOk = false;
  346. this.user.setPseudo(this.ancienPseudo);
  347. this.ancienPseudo = "";
  348. this.support.firePropertyChange("pseudoRefused",this.user.getPseudo(),this.ancienPseudo);
  349. System.out.println("After pseudo refused");
  350. break;
  351. case 4:
  352. MessagePseudo msgP4 = (MessagePseudo) msg;
  353. Utilisateurs newUser4 = new Utilisateurs(msgP4.pseudo,msgP4.srcIP,msgP4.id,msgP4.srcResponsePort,false);
  354. this.userList.remove(newUser4);
  355. this.userList.add(newUser4);
  356. Collections.sort(this.userList);
  357. this.support.firePropertyChange("newUserConnected",-1,newUser4.getId());
  358. break;
  359. case 7 :
  360. MessagePseudo msgP7 = (MessagePseudo) msg;
  361. Utilisateurs User7 = new Utilisateurs(msgP7.pseudo,msgP7.srcIP,msgP7.id,msgP7.srcResponsePort,false);
  362. this.userList.remove(User7);
  363. this.support.firePropertyChange("newUserConnected",true,false);
  364. default :
  365. System.out.println("Message de type inconnu");
  366. }
  367. }
  368. /**
  369. * Méthode appelée dans messageHandler pour répondre à un message de type 1
  370. * @param msg
  371. * Message à analyser
  372. */
  373. private void handleType1Message(MessagePseudo msg){
  374. MessagePseudo msgResponse;
  375. if (this.user.getPseudo().equals(msg.pseudo)){
  376. msgResponse = new MessagePseudo(3, this.user.getInetAddress(), this.user.getPort(), msg.srcIP, msg.srcResponsePort,this.user.getPseudo(),this.user.getId());
  377. }
  378. else{
  379. msgResponse = new MessagePseudo(2, this.user.getInetAddress(), this.user.getPort(), msg.srcIP, msg.srcResponsePort,this.user.getPseudo(),this.user.getId());
  380. }
  381. this.UDPOut.sendMsg(msgResponse);
  382. }
  383. /**
  384. * @return the list of users
  385. */
  386. public ArrayList<Utilisateurs> getUserList(){
  387. return userList;
  388. }
  389. /** returns connected the user identified by the id
  390. * @param id
  391. * @return User identified by id
  392. */
  393. public Utilisateurs getUserFromId(int id){
  394. Utilisateurs res = null;
  395. for (Utilisateurs u : userList){
  396. if (u.getId() == id){
  397. res = u;
  398. break;
  399. }
  400. }
  401. return res;
  402. }
  403. /** Returns connected user identified by pseudo
  404. * @param pseudo
  405. * @return User identified by pseudo
  406. */
  407. public Utilisateurs getUserFromPseudo(String pseudo){
  408. Utilisateurs res = null;
  409. for (Utilisateurs u : userList){
  410. if (u.getPseudo().equals(pseudo)){
  411. res = u;
  412. break;
  413. }
  414. }
  415. return res;
  416. }
  417. /**
  418. * Sends a deconnection Messages (type 7) in broadcast
  419. */
  420. public void sendDeconnectionMessage() {
  421. try {
  422. if (user.getId() == 1 || user.getId() == 2) {
  423. MessagePseudo msg = new MessagePseudo(7, this.user.getInetAddress(), this.user.getPort(), InetAddress.getLocalHost(), 6002, this.user.getPseudo(),this.user.getId());
  424. UDPOut.sendMsg(msg);
  425. }
  426. if (user.getId() == 2 || user.getId() == 3) {
  427. MessagePseudo msg = new MessagePseudo(7, this.user.getInetAddress(), this.user.getPort(), InetAddress.getLocalHost(), 6000, this.user.getPseudo(),this.user.getId());
  428. UDPOut.sendMsg(msg);
  429. }
  430. if (user.getId() == 1 || user.getId() == 3) {
  431. MessagePseudo msg = new MessagePseudo(7, this.user.getInetAddress(), this.user.getPort(), InetAddress.getLocalHost(), 6001,this.user.getPseudo(),this.user.getId());
  432. UDPOut.sendMsg(msg);
  433. }
  434. }
  435. catch (UnknownHostException e){
  436. System.out.println(("exception Trouver host dans sendPseudoBroadcast"));
  437. e.printStackTrace();
  438. }
  439. try {
  440. this.app.stop();
  441. Platform.exit();
  442. System.exit(0);
  443. } catch (Exception e) {
  444. e.printStackTrace();
  445. }
  446. }
  447. /** Closes the Chat Connection when asked
  448. * @param tcpCo
  449. */
  450. public void notifyCloseChat(TCPChatConnection tcpCo) {
  451. this.listTCPConnection.remove(tcpCo);
  452. Socket link = tcpCo.getSocket();
  453. try {
  454. link.close();
  455. } catch (IOException e) {
  456. e.printStackTrace();
  457. }
  458. }
  459. public void deleteHistory(String remotePseudo) {
  460. int remoteId = this.getUserFromPseudo(remotePseudo).getId();
  461. for (TCPChatConnection tcpCo : this.listTCPConnection){
  462. if (tcpCo.remoteUserId == remoteId){
  463. this.listTCPConnection.remove(tcpCo);
  464. tcpCo.sendCloseChat();
  465. Socket link = tcpCo.getSocket();
  466. try {
  467. link.close();
  468. } catch (IOException e) {
  469. e.printStackTrace();
  470. }
  471. break;
  472. }
  473. }
  474. this.dbAccess.deleteHistory(remoteId,user.getId());
  475. }
  476. /**
  477. * Classe interne au model pour au bout d'une seconde d'envoi de demande pseudo type 1,
  478. * on desactive les filtes et on met à jour la vue.
  479. */
  480. class TimerTaskResponseWait extends TimerTask {
  481. boolean isConfirmationNeeded;
  482. /**
  483. * @param isConfirmationNeeded if true, will send type 4 messages in broadcast else does not send
  484. */
  485. public TimerTaskResponseWait(boolean isConfirmationNeeded) {
  486. this.isConfirmationNeeded = isConfirmationNeeded;
  487. }
  488. /**
  489. * Quand la seconde s'est écoulée, on met les filtres à faux pour ne plus prendre en compte les messages de type 2 et 3
  490. */
  491. public void run() {
  492. UDPIn.setFilterValue(2, false);
  493. UDPIn.setFilterValue(3, false);
  494. if (isPseudoOk){
  495. //envoi message de type 4 pour confirmer.
  496. support.firePropertyChange("pseudoValide",ancienPseudo,user.getPseudo());
  497. UDPIn.setFilterValue(1,true);
  498. if (isConfirmationNeeded){
  499. if (!user.isOutdoor()) {
  500. sendPseudoValideBroadcast();
  501. servCon.submitConnectionIndoor(user);
  502. }
  503. else{
  504. servCon.submitConnectionOutdoor(user);
  505. }
  506. }
  507. }
  508. else{
  509. isPseudoOk = true; //On reinitialise cet attribut.
  510. }
  511. }
  512. }
  513. }