Listen.java
//Progetto Ingegneria del Software
//Autore: Ignazio Coco
//Matricola: 616/000075
import java.io.*;
import java.net.*;
/**
 * Questa classe implementa il lato server dell"applicazione.
 * Si occupa di creare un server in ascolto sulla porta 7777 per accettare
 * le chiamate esterne
 * @author Ezio
 */
public class Listen {
    /**
     * Questo parametro permette di impostare il programma in modalità
     * di debug. Ciò comporta la visualizzazione di tutti i messaggi
     * durante la fase di esecuzione
     */    
    private static boolean debug=false;
    /**
     * Attributo che identifica la porta su cui il server attende le
     * connessioni degli utenti
     */    
    static int porta=7777;
    /**
     * Attributo che identifica l"Ip del server dei nomi
     */    
    static String indIp;
    /** Creates a new instance of Listen */
    public Listen() {
    }
    /**
     * Metodo per la connessione dell"utente al server dei nomi.
     * Questo metodo registra il nome dell"utente sul server dei nomi
     * @param nome Indica il nome dell"utente che vuole ricevere messaggi
     * @return Ritorna 0, -1, -2 nei casi in cui la connessione abbia
     * avuto esito positivo, si abbia un errore generico, l"utente
     * è già registrato sul server
     */    
    public static int connect(String nome) {
        Socket socket;
        DataOutputStream streamConnOut;
        if (debug) System.out.println("Start connect - server nomi");
        if (debug) System.out.println("Avvio socket - server nomi...");
        try {
            socket = new Socket(indIp, 5555);
        }
        catch (UnknownHostException e) { 
            if (debug) System.out.println("Errore host sconosciuto - server nomi");
            if (debug) System.out.println(e);
            return -1;
        }
        catch (IOException e) { 
            if (debug) System.out.println("Errore I/O su socket - server nomi");
            if (debug) System.out.println(e);
            return -1;
        }
        if (debug) System.out.println("OK");
        if (debug) System.out.println("Apertura stream - server nomi...");
        try {
            streamConnOut = new DataOutputStream(socket.getOutputStream());
            streamConnOut.writeBytes("connect:"+nome+ "\n");
            if (debug) System.out.println("OK");
            if (debug) System.out.println("Chiusura socket e stream - server nomi...");
            streamConnOut.close();
            socket.close();
            if (debug) System.out.println("OK");
        }
        catch (IOException e) { 
            if (debug) System.out.println("Errore I/O su stream - server nomi");
            if (debug) System.out.println(e);
            return -1;
        }
        if (debug) System.out.println("Socket: "+socket);
        if (debug) System.out.println("Stream: "+streamConnOut);
        if (debug) System.out.println("OK");
        if (debug) System.out.println("Fine connect - server nomi");
        return 0;
    }
    /**
     * Il main avvia la connessione al server dei nomi, attente le chiamate
     * in ingresso ed avvia un thread per ogni connessione
     * @param args Array dei parametri passati da riga di comando.
     * Bisogna passare nell"ordine: nome utente, indirizzo IP
     * del server dei nomi
     */    
    public static void main(String args[]) {
        String nome = args[0];
        indIp = args[1];
        ServerSocket serverSocket=null;
        if (debug) System.out.println("Avvio connect...");
        if (connect(nome)==0) {
            if (debug) System.out.println("Connect avvenuta con successo");
        }
        else {
            if (debug) System.out.println("Errore su connect al server dei nomi");
            System.exit(-1);
        }
        if (debug) System.out.println("OK");
        try { 
            if (debug) System.out.println("Tentativo di connessione...");
            if (debug) System.out.println("sulla porta : "+porta);
            serverSocket = new ServerSocket(porta);
            if (debug) System.out.println("OK");
            if (debug) System.out.println(serverSocket);
        }
        catch (IOException e) {
            if (debug) System.out.println("Errore avvio socket");
            if (debug) System.out.println(e);
            System.exit(-1);
        }
        
        while (true) {
                if (debug) System.out.println("In attesa di connessioni...");
                Socket socket=null;
                try {
                    socket = serverSocket.accept();
                }
                catch (IOException e) {
                       if (debug) System.out.println("Errore durante l"accept di una socket");
                       System.exit(-1);
                }
                if (debug) System.out.println("Ho ricevuto una chiamata di apertura da:\n" + socket);
                ListenThread listenThread = new ListenThread(socket, indIp);
                listenThread.start();
        }
        
    }
}
/**
 * Classe per l"implentazione di un thread che gestisce una connessione
 * remota
 */
class ListenThread extends Thread  {
    /**
     * Attributo che identifica la socket in ascolto delle nuove connessioni
     */    
    private Socket socket;
    /**
     * Questo parametro permette di impostare il programma in modalità
     * di debug. Ciò comporta la visualizzazione di tutti i messaggi
     * durante la fase di esecuzione
     */    
    public boolean debug=false;
    /**
     * Attributo che identifica l"Ip del server dei nomi
     */    
    String serverLocation;
    /**
     * Costruttore del thread ListenThread
     * @param socket La socket su cui deve operare il thread
     * @param serverLocation locazione del server dei nomi
     */    
    public ListenThread (Socket socket, String serverLocation) {
        this.socket = socket;
        this.serverLocation = serverLocation;
    }
    /**
     * Metodo che implementa il ciclo di esecuzione del thread
     */    
    public void run() {
        try {
            DataInputStream is = new DataInputStream(socket.getInputStream());
            InetAddress ind = socket.getInetAddress();
            String ip = ind.getHostAddress();
            String nome=retrieveName(ip);
            if (nome.substring(0,3).equals("###")) nome = "<ERR:no-name>";
            String mess;
            while (true) {
                mess = is.readLine();
                if (mess==null) break;
                System.out.println(nome+"::"+mess);
            }
            is.close();
            socket.close();
            if (debug) System.out.println("Socket Chiuso");
         }
         catch (IOException e) {
             if (debug) System.out.println("Errore su metodo run classe serverthread");
             if (debug) System.out.println("IOException: " + e);
        }
    }
    /**
     * Metodo per determinare il nome associato all"indirizzo Ip dell"utente
     * che si collega a questo server
     * @param ip l"Ip di cui determinare il nome
     * @return Il nome dell"utente. In caso di errore viene restituita
     * una stringa che inizia con ### seguita dalla descrizione
     * dell"errore
     */    
    public String retrieveName (String ip) {
        DataOutputStream streamConnOut;
        Socket socketForName;
        String nome;
        int porta;
        if (debug) System.out.println("Inizio connect server nomi");
        if (debug) System.out.println("Instaurazione socket - server nomi");
        try {
            socketForName = new Socket(serverLocation, 5555);
        }
        catch (UnknownHostException e) { 
            if (debug) System.out.println("Errore host sconosciuto");
            if (debug) System.out.println(e);
            return "###errore server nomi";
        }
        catch (IOException e) { 
            System.out.println("Errore I/O su socket - server nomi");
            if (debug) System.out.println(e);
            return "###errore server nomi";
        }
        if (debug) System.out.println("Apertura stream - server nomi");
        try {
            streamConnOut = new DataOutputStream(socketForName.getOutputStream());
            streamConnOut.writeBytes("retrieveName:"+ip + "\n");
            DataInputStream is = new DataInputStream(socketForName.getInputStream());
            String mess = is.readLine();
            String tmp[] = mess.split(":");
            if (debug) System.out.println("Messaggio ricevuto: "+mess);
            nome=tmp[0];
            porta=Integer.parseInt(tmp[2]);
            is.close();
            streamConnOut.close();
            socketForName.close();
            return nome;
        }
        catch (IOException e) { 
            if (debug) System.out.println("Errore I/O su stream - server nomi");
            if (debug) System.out.println(e);
            return "###Errore server nomi";
        }

     }
}