/* * Andrea Caravano (www.andreacaravano.net) * * Esercizio 5: "Telegiornale" * Descrizione: La redazione di TGJava24 ha adottato un’infrastruttura di rete che prevede un numero variabile * di assistenti alla regia e redattori che hanno la necessità di comunicare con il giornalista in onda in tempo reale, * per informarlo su eventuali variazioni e/o correzioni alle notizie oggetto del telegiornale. * Si suppone che il primo client che si collega sia il giornalista in onda e tutti i successivi siano assistenti alla * regia e redattori. * Un possibile esempio di variazione alle notizie comunicata dai redattori può essere la seguente: * Traffico intenso sulla A14 * A causa delle recenti manifestazioni nell’area di Bologna, un’insolita coda si è formata all’altezza del * chilometro 721 dell’autostrada A14. * La struttura della notizia deve includere il titolo della notizia (su una sola linea) e il testo esteso della notizia, * multilinea. Il client del giornalista dovrà rimanere costantemente in ascolto di eventuali variazioni comunicate * dai redattori. * * Sviluppare la sola componente server. * * N.B.: L'esercizio scaturisce dalla sola fantasia dell'autore e intende rappresentare una applicazione didattica. * I dettagli in esso contenuti potrebbero non essere corrispondenti alla realtà e intendono valutare le abilità nella gestione delle strutture dati proposte. */ import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ServerTelegiornale { static final int PORTALISTEN = 9000; static int contaClient = 0; static ExecutorService esecutore = Executors.newCachedThreadPool(); static Lock mutexLettori = new ReentrantLock(); static Lock mutexScrittori = new ReentrantLock(); static Semaphore semLettura = new Semaphore(1); static Semaphore sincro = new Semaphore(1); static int processiDentroScrittori = 0; static int processiDentroLettori = 0; static TreeMap<String, List<String>> notizie = new TreeMap<>(); public static void main(String[] args) { System.out.println("TGJava24 - REDAZIONE"); System.out.println("===================="); try (ServerSocket procServer = new ServerSocket(PORTALISTEN)) { System.out.format("Processo server avviato con il seguente indirizzo di socket: %s%n", procServer.getLocalSocketAddress()); while (true) { try { Socket tempClient = procServer.accept(); contaClient++; if (contaClient == 1) { esecutore.execute(() -> { try (Socket client = tempClient) { System.out.format("Thread ID = %d - Indirizzo di socket del client: %s%n", Thread.currentThread().getId(), client.getRemoteSocketAddress()); lettore(client); } catch (IOException e) { System.err.format("Errore di avvio della comunicazione: %s%n", e.getMessage()); } }); } else { esecutore.execute(() -> { try (Socket client = tempClient) { System.out.format("Thread ID = %d - Indirizzo di socket del client: %s%n", Thread.currentThread().getId(), client.getRemoteSocketAddress()); scrittore(client); } catch (IOException e) { System.err.format("Errore di avvio della comunicazione: %s%n", e.getMessage()); } }); } } catch (IOException e) { System.err.format("Errore nella creazione di nuovi socket: %s%n", e.getMessage()); } } } catch (IOException e) { System.err.format("Errore lato server: %s%n", e.getMessage()); } } private static void scrittore(Socket client) { try (BufferedReader BR = new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8")); PrintWriter PW = new PrintWriter(new OutputStreamWriter(client.getOutputStream(), "UTF-8"), true)) { System.out.println("Thread scrittore avviato."); PW.println("RUOLO = Scrittore"); while (true) { String titoloNotiziaLocale = BR.readLine(); List<String> testoNotiziaLocale = new ArrayList<>(); String singolaRiga; do { singolaRiga = BR.readLine(); if (singolaRiga.isEmpty() == false) testoNotiziaLocale.add(singolaRiga); } while (singolaRiga.isEmpty() == false); System.out.println("Presa in carico una nuova notizia."); PW.println("202"); // Tratto da HTTP: "Accepted" inizioScrittura(); notizie.put(titoloNotiziaLocale, testoNotiziaLocale); fineScrittura(); } } catch (UnsupportedEncodingException e) { System.out.format("Errore: codifica non supportata: %s", e.getMessage()); } catch (IOException e) { System.err.format("Errore di I/O: %s%n", e.getMessage()); } catch (InterruptedException e) { System.err.format("Errore di gestione dei meccanismi della programmazione concorrente: %s%n", e.getMessage()); } } private static void lettore(Socket client) { try (BufferedReader BR = new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8")); PrintWriter PW = new PrintWriter(new OutputStreamWriter(client.getOutputStream(), "UTF-8"), true)) { System.out.println("Thread lettore avviato."); PW.println("RUOLO = Lettore"); TreeMap<String, List<String>> notizieInviate = new TreeMap<>(); while (true) { inizioLettura(); for (Map.Entry<String, List<String>> n : notizie.entrySet()) { if (notizieInviate.containsKey(n.getKey()) == false) { PW.println(n.getKey()); List<String> righeNotizia = n.getValue(); for (String r : righeNotizia) { PW.println(r); } notizieInviate.put(n.getKey(), n.getValue()); } } fineLettura(); } } catch (UnsupportedEncodingException e) { System.out.format("Errore: codifica non supportata: %s", e.getMessage()); } catch (IOException e) { System.err.format("Errore di I/O: %s%n", e.getMessage()); } catch (InterruptedException e) { System.err.format("Errore di gestione dei meccanismi della programmazione concorrente: %s%n", e.getMessage()); } } private static void fineLettura() throws InterruptedException { mutexLettori.lock(); try { processiDentroLettori--; if (processiDentroLettori == 0) { sincro.release(); } } finally { mutexLettori.unlock(); } } private static void inizioLettura() throws InterruptedException { semLettura.acquire(); mutexLettori.lock(); try { processiDentroLettori++; if (processiDentroLettori == 1) { sincro.acquire(); } } finally { mutexLettori.unlock(); semLettura.release(); } } private static void inizioScrittura() throws InterruptedException { mutexScrittori.lock(); try { processiDentroScrittori++; if (processiDentroScrittori == 1) { semLettura.acquire(); } sincro.acquire(); } finally { mutexScrittori.unlock(); } } private static void fineScrittura() throws InterruptedException { mutexScrittori.lock(); try { sincro.release(); processiDentroScrittori--; if (processiDentroScrittori == 0) { semLettura.release(); } } finally { mutexScrittori.unlock(); } } }