Advertisement
Guest User

Untitled

a guest
Feb 12th, 2019
123
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 20.72 KB | None | 0 0
  1. package server;
  2.  
  3. import java.io.File;
  4. import java.io.IOException;
  5. import java.net.InetAddress;
  6. import java.net.ServerSocket;
  7. import java.net.Socket;
  8. import java.net.UnknownHostException;
  9. import java.nio.ByteBuffer;
  10. import java.nio.channels.FileChannel;
  11. import java.nio.channels.SocketChannel;
  12. import java.nio.file.Paths;
  13. import java.nio.file.StandardOpenOption;
  14. import java.rmi.AlreadyBoundException;
  15. import java.rmi.registry.LocateRegistry;
  16. import java.rmi.registry.Registry;
  17. import java.rmi.server.UnicastRemoteObject;
  18. import java.util.HashSet;
  19. import java.util.Set;
  20. import java.util.concurrent.ConcurrentHashMap;
  21. import java.util.concurrent.ExecutorService;
  22. import java.util.concurrent.Executors;
  23.  
  24. public class Turing {
  25.  
  26. private static ExecutorService e;
  27. private static Object updateDB;
  28. private static Object updateSets;
  29. private static ConcurrentHashMap<String, User> database;
  30. private static ConcurrentHashMap<String, Document> docs;
  31. private static Set<String> usersOnline;
  32. private static Set<String> usersOffline;
  33. private static Set<InetAddress> multicastAddresses;
  34. private static ServerSocket welcomeSocket;
  35.  
  36. // Inizializzazioni varie
  37. private static void init() throws UnknownHostException, IOException, AlreadyBoundException {
  38.  
  39. updateDB = new Object(); // Usato per la gestione sincronizzazione tra database e docs
  40. updateSets = new Object(); // Usato per la gestione sincronizzazione tra usersOnline ed usersOffline
  41. database = new ConcurrentHashMap<String, User>(); // <username, User>, database per la gestione di User.java
  42. docs = new ConcurrentHashMap<String, Document>(); // <docName, Document>, database per la gestione di Document.java
  43. usersOnline = new HashSet<String>();
  44. usersOffline = new HashSet<String>();
  45. multicastAddresses = new HashSet<InetAddress>(); // Salvo gli indirizzi di Multicast già assegnati a documenti esistenti
  46. welcomeSocket = new ServerSocket(Configurations.DEFAULT_PORT, 0, InetAddress.getByName(null));
  47.  
  48. e = Executors.newFixedThreadPool(Configurations.THREADPOOL_EX_THREADS); // Vedasi relazione per piccolo commento a riguardo
  49.  
  50. // Classica RMI come abbiamo affrontato durante il corso
  51. RegistrationRMI obj = new RegistrationRMI(database, usersOffline, usersOnline);
  52. RegistrationInterface stub = (RegistrationInterface) UnicastRemoteObject.exportObject(obj, 0);
  53.  
  54. Registry registry = LocateRegistry.createRegistry(Configurations.REGISTRATION_PORT);
  55. registry.bind(RegistrationInterface.SERVICE_NAME, stub);
  56.  
  57. File dir = new File("Documents/"); // Dove vengono salvati i documenti (lato server)
  58. if(!dir.exists())
  59. dir.mkdir();
  60.  
  61. }
  62.  
  63.  
  64. // Semplice inizializzazione del database in modo da avere elementi su cui lavorare senza doverli ricreare ogni volta
  65. private static void boredInit() throws UnknownHostException {
  66.  
  67. System.err.println("Nomi, docs ed invite di default attivi. Per disattivare, in Configurations.java, BORED_INIT = false.");
  68.  
  69. User asd = new User("asd", "asd");
  70. User jkl = new User("jkl", "jkl");
  71. User iop = new User("iop", "iop");
  72.  
  73. usersOffline.add("asd");
  74. usersOffline.add("jkl");
  75. usersOffline.add("iop");
  76. database.put("asd", asd);
  77. database.put("jkl", jkl);
  78. database.put("iop", iop);
  79.  
  80. createDoc("asd", "qwe", 5);
  81. createDoc("asd", "solodiasd", 3);
  82. createDoc("asd", "qwertyuiop", 6);
  83.  
  84. sendInvite("asd", "jkl", "qwe");
  85. sendInvite("asd", "jkl", "qwertyuiop");
  86. sendInvite("asd", "iop", "qwertyuiop");
  87. }
  88.  
  89.  
  90. public static void main(String[] args) throws IOException, AlreadyBoundException {
  91.  
  92. init();
  93.  
  94. if(Configurations.BORED_INIT) // In caso si voglia testare eseguendo più volte, mette elementi di default
  95. boredInit();
  96.  
  97. PendingInvites p = new PendingInvites(); // Listener di supporto per gli inviti live
  98. p.start();
  99.  
  100. Socket connectionSocket;
  101.  
  102. System.out.println("*** Turing è operativo! ***"); // Tutto è inizializzato correttamente, quindi..
  103.  
  104. while (true) { // .. Turing diventa il Listener
  105. connectionSocket = null;
  106. connectionSocket = welcomeSocket.accept();
  107.  
  108. if(Configurations.DEBUG)
  109. System.out.println("Server: Un client si è connesso.");
  110. e.execute(new RequestHandler(connectionSocket));
  111. }
  112. }
  113.  
  114.  
  115. // Controlla se esiste un utente di nome username nei Sets
  116. private static boolean checkAll(String username) {
  117. return (usersOnline.contains(username) || usersOffline.contains(username));
  118. }
  119.  
  120.  
  121. // *** Richiesta di Login ***
  122. static int login(String username, String password) {
  123.  
  124. synchronized(updateSets) {
  125. // Se l'utente risulta tra gli utenti offline && la password è corretta
  126. if(usersOffline.contains(username) && database.get(username).checkPassword(password)) {
  127. usersOffline.remove(username); // Rimuovi dal Set degli utenti offline
  128. usersOnline.add(username); // E metti nel Set degli utenti online
  129.  
  130. if(Configurations.DEBUG)
  131. System.out.println("Utente " + username + " si è connesso.");
  132. return 0;
  133. }
  134.  
  135. // Se è già connesso..
  136. if(usersOnline.contains(username)) {
  137. if(Configurations.DEBUG)
  138. System.err.println("Utente " + username + " ha tentato un doppio login.");
  139. return -1;
  140. }
  141. }
  142.  
  143. // Se arrivo qua, le credenziali non erano corrette
  144. if(Configurations.DEBUG)
  145. System.err.println("Tentativo di login con credenziali errate.");
  146. return -2;
  147. }
  148.  
  149.  
  150. // Richiesta degli inviti a cui si è stati invitati durante il periodo di permanenza offline
  151. static Set<String> getPendingInvites(String username) {
  152.  
  153. synchronized(updateDB) {
  154. User u = database.get(username);
  155. Set<String> tmp = null;
  156.  
  157. if(u == null) {
  158. if(Configurations.DEBUG)
  159. System.err.println("Turing, getPendInv: questa stampa dovrebbe essere impossibile.");
  160. return tmp;
  161. }
  162.  
  163. if(Configurations.DEBUG)
  164. System.out.println("Turing: " + u.getUser() + " ha richiesto i nomi dei documenti a cui è stato invitato mentre era offline.");
  165.  
  166. return u.getPendingInvites();
  167. }
  168. }
  169.  
  170.  
  171. // Richiesta di un Set usato per la gestione degli inviti in diretta (live)
  172. static Set<String> getInstaInvites(String nameServed) {
  173. Set<String> tmp = null;
  174.  
  175. synchronized(updateDB) {
  176. User u = database.get(nameServed);
  177.  
  178. if(u != null)
  179. tmp = u.getInstaInvites();
  180. }
  181.  
  182. return tmp;
  183. }
  184.  
  185.  
  186. // Una volta ottenuti gli inviti, non voglio che vengano mostrati nuovamente all'accesso successivo -> clear
  187. static void resetInvites(String username) {
  188. database.get(username).resetPendingInvites();
  189. }
  190.  
  191.  
  192. // Identico a resetInvites, semplicemente con un target diverso
  193. static void resetInstaInvites(String username) {
  194. database.get(username).resetInstaInvites();
  195. }
  196.  
  197.  
  198. // *** Richiesta di logout ***
  199. static boolean disconnect (String username) {
  200.  
  201. synchronized(updateSets) {
  202. // Se l'utente è online
  203. if(usersOnline.contains(username)) {
  204. usersOnline.remove(username); // Lo rimuovo dal Set degli utenti online
  205. usersOffline.add(username); // Per metterlo nel Set degli utenti offline
  206. if(Configurations.DEBUG)
  207. System.out.println("Utente " + username + " si è disconnesso.");
  208. return true;
  209. }
  210. }
  211.  
  212. if(Configurations.DEBUG)
  213. System.err.println("È arrivata una richiesta di disconnect di " + username + " che non ha avuto alcun effetto.");
  214.  
  215. return false;
  216. }
  217.  
  218.  
  219. // *** Richiesta di Creazione Documento ***
  220. static int createDoc(String creator, String docName, int sections) {
  221.  
  222. synchronized(updateDB) {
  223. // Se esiste un documento con lo stesso nome -> errore
  224. if(docs.containsKey(docName)) {
  225. if(Configurations.DEBUG)
  226. System.err.println(creator + " ha tentato di creare un duplicato di " + docName);
  227. return -1;
  228. }
  229.  
  230. // Controllo extra sulla validità dell'user richiedente, non dovrebbe essere necessario
  231. if (!database.containsKey(creator)) {
  232. if(Configurations.DEBUG)
  233. System.err.println(creator + " utente sconosciuto (?)");
  234. return -2;
  235. }
  236.  
  237. InetAddress addr = null;
  238. String aux;
  239.  
  240. // Cerca un indirizzo tra 239.0.0.0 e 239.255.255.255 non già utilizzato
  241. // La motivazione del range limitato a 239.* è nella relazione
  242. do {
  243. aux = "239." + (int)Math.floor(Math.random()*256) + "." + (int)Math.floor(Math.random()*256) + "." + (int)Math.floor(Math.random()*256);
  244. try {
  245. addr = InetAddress.getByName(aux);
  246. if(Configurations.DEBUG && multicastAddresses.contains(addr))
  247. System.err.println("Una cosa altamente improbabile non è impossibile."); // è altamente improbabile che due documenti abbiano lo stesso address
  248. } catch (UnknownHostException e) { e.printStackTrace(); }
  249. } while(!addr.isMulticastAddress() && multicastAddresses.contains(addr));
  250.  
  251. // Se arrivo qua, l'indirizzo di Multicast non è usato da altri documenti, quindi le chat sono coerenti
  252. multicastAddresses.add(addr);
  253. Document d = new Document(creator, docName, sections, addr);
  254.  
  255. // Aggiorno effettivamente il database
  256. docs.put(docName, d);
  257. database.get(creator).addToEditableDocs(docName);
  258. }
  259.  
  260. File dir = new File("Documents/" + docName); // Crea dentro Documents (creato in init() ) la cartella docName
  261. dir.mkdir();
  262.  
  263. for(int i = 0; i < sections; i++) {
  264. File x = new File("Documents/" + docName, docName + i + ".txt"); // Crea dentro la precedente cartella gli X files richiesti
  265. try {
  266. x.createNewFile();
  267. } catch (IOException e) { e.printStackTrace(); }
  268. }
  269.  
  270. if(Configurations.DEBUG)
  271. System.out.println(creator + " ha creato il documento " + docName + " con " + sections + " sezioni.");
  272. return 0;
  273. }
  274.  
  275.  
  276. // *** Richiesta di invito a documento ***
  277. static int sendInvite(String sender, String receiver, String docName) {
  278.  
  279. synchronized(updateDB) {
  280. User rec = database.get(receiver);
  281.  
  282. // Se il mittente non è registrato
  283. if(!checkAll(sender)) {
  284. if(Configurations.DEBUG)
  285. System.err.println(sender + " non registrato.");
  286. return -1;
  287. }
  288. // Se il destinatario non è registrato
  289. if(!checkAll(receiver)) {
  290. if(Configurations.DEBUG)
  291. System.err.println(receiver + " non registrato.");
  292. return -2;
  293. }
  294. // Se il documento non esiste
  295. if(!docs.containsKey(docName)) {
  296. if(Configurations.DEBUG)
  297. System.err.println(docName + " non è un documento esistente.");
  298. return -3;
  299. }
  300. // Se il mittente non ha creato il documento
  301. if(!docs.get(docName).isCreator(sender)) {
  302. if(Configurations.DEBUG)
  303. System.err.println(sender + " non può invitare al documento " + docName + " in quanto non è creatore.");
  304. return -4;
  305. }
  306. // Se il destinatario è già editor
  307. if(database.get(receiver).isEditor(docName)) {
  308. if(Configurations.DEBUG)
  309. System.err.println(receiver + " è già editor di " + docName);
  310. return -5;
  311. }
  312.  
  313. if(usersOnline.contains(receiver)) { // L'utente è online, scrivo immediatamente (live invite)
  314. docs.get(docName).addEditor(receiver);
  315. rec.addInstaInvites(docName);
  316. rec.addToEditableDocs(docName);
  317. if(Configurations.DEBUG)
  318. System.out.println(sender + " ha invitato (live) " + receiver + " come editor del documento " + docName);
  319. }
  320.  
  321. else { // L'utente è offline, salvo per quando si connetterà (pending invite)
  322. docs.get(docName).addEditor(receiver);
  323. rec.addPendingInvite(docName);
  324. rec.addToEditableDocs(docName);
  325. if(Configurations.DEBUG)
  326. System.out.println(sender + " ha invitato (pending) " + receiver + " come editor del documento " + docName);
  327. }
  328. }
  329. return 0;
  330. }
  331.  
  332.  
  333. // *** Richiesta dei documenti (List) ***
  334. static String getDocs(String username) {
  335.  
  336. String res = null;
  337. int antiBug = 0;
  338.  
  339. synchronized(updateDB) {
  340. User u = database.get(username);
  341. Object[] uDocs = u.getDocs().toArray();
  342. antiBug = uDocs.length; // Se è != 0 -> c'è almeno un documento
  343. res = "Nessun documento."; // Se non dovesse entrare nel ciclo, restituirà questa frase di default
  344.  
  345. for(int i = 0; i < uDocs.length; i++) {
  346.  
  347. Document d = docs.get(uDocs[i]); // Prende il doc
  348. String edtr = d.getEditors(); // Prende gli editors
  349. if(edtr == null)
  350. edtr = "Nessuno."; // Evito di mostrare "null"
  351.  
  352. if(res.equals("Nessun documento.")) // Se è la prima iterazione..
  353. res = "Nome documento: " + d.getName() +"\nCreatore: " + d.getCreator() + "\nCollaboratori: " + edtr;
  354. else
  355. res = res + "\nNome documento: " + d.getName() +"\nCreatore: " + d.getCreator() + "\nCollaboratori: " + edtr;
  356.  
  357. res = res + '\n'; // Per dare una newline tra info di un documento ed un altro
  358. }
  359. }
  360.  
  361. if(antiBug != 0) // Senza questo, la richiesta di List fa comparire un "null" nella finestra del Client
  362. res = res + '\n';
  363.  
  364. return res;
  365. }
  366.  
  367.  
  368. // *** Richiesta di modifica sezione di documento ***
  369. static String editDoc(String username, String docName, int section, SocketChannel clientChannel) throws IOException {
  370.  
  371. Document d = null;
  372. String res = null;
  373.  
  374. synchronized(updateDB) {
  375. d = docs.get(docName);
  376.  
  377. // Se non esiste l'user o il doc richiedente/richiesto (anche qui, l'user dovrebbe essere sempre valido ma rimane il controllo extra)
  378. if(database.get(username) == null || d == null) {
  379. if(Configurations.DEBUG)
  380. System.err.println("Turing [EditRequest]: User non esistente || Documento inesistente");
  381. return "NULL";
  382. }
  383.  
  384. // Se il richiedente non è né creatore né collaboratore del documento
  385. if(!d.isCreator(username) && !d.isEditor(username)) {
  386. if(Configurations.DEBUG)
  387. System.err.println("Turing [EditRequest]:" + username + " non può modificare questo documento.");
  388. return "UNABLE";
  389. }
  390.  
  391. // Se la sezione richiesta è maggiore delle sezioni massime del documento
  392. if(d.getSize() <= section)
  393. return "OOB";
  394.  
  395. // Se qualcun altro sta già lavorando sulla stessa sezione del documento
  396. if(d.isLocked(section)) {
  397. if(Configurations.DEBUG)
  398. System.err.println("Turing [EditRequest]: qualcuno sta già lavorando sulla sezione " + section + " di " + docName);
  399. return "LOCK";
  400. }
  401.  
  402. // Provo effettivamente a prendere la lock su tale documento (non dovrebbe fallire per via della synchronized, ma check comunque)
  403. if(!d.editSection(section))
  404. return "TRYLOCK";
  405.  
  406. // Apro in lettura il file richiesto
  407. FileChannel inChannel = FileChannel.open(Paths.get("Documents/" + docName + "/" + docName + section + ".txt"), StandardOpenOption.READ);
  408. // Ed alloco un buffer per inviarlo al client
  409. ByteBuffer buffer = ByteBuffer.allocateDirect(1024*1024);
  410.  
  411. boolean stop = false;
  412.  
  413. // Classica gestione NIO
  414. while (!stop) {
  415. int bytesRead=inChannel.read(buffer);
  416. if (bytesRead==-1)
  417. stop=true;
  418.  
  419. buffer.flip();
  420. while (buffer.hasRemaining())
  421. clientChannel.write(buffer);
  422. buffer.clear();
  423. }
  424. clientChannel.close(); // Chiudo il channel verso il client per far sapere che è finita la fase di trasferimento
  425. inChannel.close(); // Chiudo il channel della lettura del file richiesto
  426.  
  427. res = d.getAddr().toString(); // Ottengo l'indirizzo di multicast del documento
  428. }
  429. if(Configurations.DEBUG)
  430. System.out.println(username + " sta ora modificando la sezione " + section + " di " + docName);
  431.  
  432. res = (String) res.subSequence(1, res.length()); // L'indirizzo di multicast ha uno / iniziale, lo tolgo
  433. return res;
  434. }
  435.  
  436.  
  437. // *** Richiesta di upload della sezione di documento modificata ***
  438. static String endEdit(String username, String docName, int section, SocketChannel clientChannel) throws IOException {
  439.  
  440. Document d = null;
  441. synchronized(updateDB) {
  442. d = docs.get(docName);
  443.  
  444. File x = new File("Documents/" + docName, docName + section + ".txt");
  445. if(x.exists()) // Se esiste (dovrebbe in quanto è stato scaricato!)
  446. x.delete(); // Cancello il file precedente in quanto non aggiornato
  447. x.createNewFile(); // Crea il nuovo file su cui andrò a scrivere ciò che ricevo dal client
  448.  
  449. // Apro in scrittura il file target
  450. FileChannel outChannel = FileChannel.open(Paths.get("Documents/" + docName + "/" + docName + section + ".txt"), StandardOpenOption.WRITE);
  451. // Ed alloco un buffer per ricevere dal client
  452. ByteBuffer buffer = ByteBuffer.allocateDirect(1024*1024);
  453.  
  454. boolean stop=false;
  455.  
  456. // Tutto come prima
  457. while (!stop) {
  458. int bytesRead=clientChannel.read(buffer);
  459. if (bytesRead==-1)
  460. stop=true;
  461.  
  462. buffer.flip();
  463. while (buffer.hasRemaining())
  464. outChannel.write(buffer);
  465. buffer.clear();
  466. }
  467. clientChannel.close();
  468. outChannel.close();
  469.  
  470. d.unlockSection(section); // Unlock della sezione così è di nuovo modificabile
  471.  
  472. }
  473.  
  474. return "SUCCESS";
  475. }
  476.  
  477.  
  478. // *** Richiesta di visualizzazione di sezione di un documento ***
  479. static int getFile(String username, String docName, int section, SocketChannel clientChannel) throws IOException {
  480. int res = 0;
  481.  
  482. synchronized(updateDB) {
  483.  
  484. // Se il documento non esiste mi fermo
  485. if(!docs.containsKey(docName))
  486. return -1;
  487. // Se il documento sta venendo modificato, salvo l'informazione
  488. else if(docs.get(docName).isLocked(section))
  489. res = 1;
  490.  
  491. // Stessa gestione NIO di poco fa
  492. FileChannel inChannel = FileChannel.open(Paths.get("Documents/" + docName + "/" + docName + section + ".txt"), StandardOpenOption.READ);
  493. ByteBuffer buffer = ByteBuffer.allocateDirect(1024*1024);
  494. boolean stop = false;
  495.  
  496. while (!stop) {
  497. int bytesRead=inChannel.read(buffer);
  498. if (bytesRead==-1)
  499. stop=true;
  500.  
  501. buffer.flip();
  502. while (buffer.hasRemaining())
  503. clientChannel.write(buffer);
  504. buffer.clear();
  505. }
  506. clientChannel.close();
  507. inChannel.close();
  508. }
  509.  
  510. return res; // Di default è 0, che significa "nessuno ci sta lavorando". Se -1, il documento non esiste. Se 1, "qualcuno ci sta lavorando"
  511. }
  512.  
  513.  
  514. // *** Richiesta di visualizzazione di intero documento ***
  515. static String getDocument(String username, String docName, SocketChannel clientChannel) throws IOException {
  516. String res = "SUCCESS";
  517.  
  518. synchronized(updateDB) {
  519.  
  520. // Se il documento non esiste
  521. if(!docs.containsKey(docName))
  522. return "NO_EXIST";
  523.  
  524. Document d = docs.get(docName);
  525. for(int i = 0; i < d.getSize(); i++) {
  526. if(d.isLocked(i)) { // Controllo se almeno una persona sta modificando
  527. if(res.equals("SUCCESS"))
  528. res = "Sezioni sotto modifica: ";
  529. res = res + i + " "; // Creo la stringa delle sezioni modificate
  530. }
  531. }
  532.  
  533. ByteBuffer buffer = ByteBuffer.allocateDirect(1024*1024);
  534. // Gestione NIO come le precedenti, ma più FileChannels di lettura
  535. // In pratica, avviene una concatenazione dei file txt in un unico txt
  536. for(int i = 0; i < d.getSize(); i++) {
  537.  
  538. FileChannel inChannel = FileChannel.open(Paths.get("Documents/" + docName + "/" + docName + i + ".txt"), StandardOpenOption.READ);
  539. boolean stop = false;
  540.  
  541. while (!stop) {
  542. int bytesRead=inChannel.read(buffer);
  543. if (bytesRead==-1)
  544. stop=true;
  545.  
  546. buffer.flip();
  547. while (buffer.hasRemaining())
  548. clientChannel.write(buffer);
  549. buffer.clear();
  550. }
  551. inChannel.close();
  552. }
  553. clientChannel.close();
  554. }
  555. return res;
  556. }
  557.  
  558.  
  559. // Semplice unlock di una sezione
  560. static void unlock(String nameServed, String docServed, int sectionDoc) {
  561.  
  562. synchronized(updateDB) {
  563. Document d = docs.get(docServed);
  564. d.unlockSection(sectionDoc);
  565. }
  566.  
  567. }
  568.  
  569.  
  570. // Controllo dei permessi e simili per un user, un doc e la sua size
  571. static int checkFile(String username, String docName, int section) {
  572.  
  573. Document d = null;
  574. User u = null;
  575.  
  576. synchronized(updateDB) {
  577. d = docs.get(docName);
  578. u = database.get(username);
  579. }
  580.  
  581. if(d == null)
  582. return -1;
  583.  
  584. if(u == null)
  585. return -2;
  586.  
  587. if(!d.isEditor(username))
  588. return -3;
  589.  
  590. if(!u.isEditor(docName))
  591. return -4;
  592.  
  593. if(section >= d.getSize() && section <= Configurations.MAX_SECTIONS)
  594. return -5;
  595.  
  596. return d.getSize();
  597. }
  598.  
  599.  
  600. // Controllo simile al precedente, ma senza considerare la size
  601. static boolean checkPermissions(String username, String docName) {
  602. synchronized(updateDB) {
  603. User u = database.get(username);
  604. Document d = docs.get(docName);
  605.  
  606. if(u==null || d ==null)
  607. return false;
  608.  
  609. if(!u.isEditor(docName))
  610. return false;
  611.  
  612. if(!d.isEditor(username))
  613. return false;
  614. }
  615. return true;
  616. }
  617.  
  618.  
  619. // Controllo di supporto per sapere se un utente è online
  620. static boolean isOnline(String nameServed) {
  621. return usersOnline.contains(nameServed);
  622. }
  623. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement