DIPARTIMENTO   DI   INFORMATICA
Università di Torino

Programmazione in Rete e Laboratorio
Corso di Studi in Informatica
a.a. 2001/2002

Prof. Alberto Martelli
Dr. Matteo Baldoni

IMPORTANTE: il testo del laboratorio verrà spesso aggiornato, sia per l'aggiunta delle parti ancora mancanti sia come conseguenza delle note e commenti emersi durante le lezioni. Le parti nuove, le note ed i commenti piu` recenti verranno indicate dal seguente simbolo:.

Ultimo aggiornamento in data ---> Nov 05, 2002

L'esame del 9 dicembre 2002, ore 9:00, in laboratorio, sarà l'ultimo valido con il presente laboratorio. Dalla sessione successiva l'esame dovrà essere sostenuto con il programma ed il testo di laboratorio che verrà presentato durante il corso di Programmazione in Rete e Laboratorio dell'anno accademico 2002/2003.
Per chi deve sostenere l'esame nella sessione di dicembre è obbligatorio presentare anche la parte nona.

PER SOSTENERE L'ESAME RICORDASI DI ISCRIVERSI ON-LINE ---> servizi riservati

NEWSGROUP ---> dip.edu.oo-languages

Laboratorio

Si ricorda che per poter svolgere il laboratorio è necessario prima di tutto iscriversi compilando l'apposito form di iscrizione al laboratorio nel quale vanno indicati i componenti del gruppo, che possono essere al più due (limite rigido!), e i loro numeri di matricola.

Alla consegna del laboratorio è necessario allegare:
  1. un floppy disk contenente il codice sorgente e compilato del progetto svolto con tutte le istruzioni necessarie ad un facile utilizzo del software prodotto
  2. il listato stampato del codice sorgente
  3. la documentazione generata mediante javadoc (sul floppy disk)
  4. i diagrammi di classe UML ed una relazione sul lavoro svolto
Altri dettagli si trovano nella sezione modalità d'esame.

Parte Prima

Definire una classe Indirizzo le cui istanze contengono il seguente campo:
  • indirizzo: di tipo String che rappresenta un indirizzo (es. indirizzo email "baldoni@di.unito.it")
Definire una classe Utente le cui istanze contengono i seguenti campi:
  • nome: di tipo String contenente il nome dell'utente
  • cognome: di tipo String contenente il cognome dell'utente
  • indirizzo: di tipo Indirizzo che rappresenta l'ndirizzo dell'utente
Definire poi una classe Messaggio le cui istanze contenengono i seguenti campi:
  • da: di tipo Indirizzo contenente l'indirizzo del mittente del messaggio
  • a: di tipo Indirizzo[] contenente la lista (un array) degli indirizzi dei destinatari del messaggio
  • data: di tipo Data contenente la data di composizione del messaggio
  • oggetto: di tipo String contenente l'oggetto del messaggio
  • corpo: di tipo String contenente il corpo del messaggio
Per ogni classe si definiscano gli opportuni costruttori ed un metodo di tipo get ed uno di tipo set, per ogni campo presente in una classe; ad esempio, per il campo da della classe Messaggio:
  • getDa(): restituisce l'oggetti di tipo Indirizzo rappresentante l'indirizzo del mittente del messaggio contenuto nel campo da
  • setDa(Indirizzo ind): inizializza il campo da con il valore contenuto nel parametro ind
Si definiscano inoltre i metodi
  • getDestinatari() (per la classe Messaggio): restituisce la lista degli indirizzi dei destinatari del messaggio
  • toString() (per ogni classe): per convertire l'oggetto in stringa. Ad esempio, sia mes un messaggio allora mes.toString() restituirà una stringa che stampata dovrebbe risultare qualcosa del tipo:
    ----------------------------
    Da: baldoni@di.unito.it
    A: st9...@educ.di.unito.it,st9...@educ.di.unito.it
    Data: ...
    Oggetto: Laboratorio di Prog. in Rete
    Contenuto:
    Gentili Sig.ri ...,
    si comunica che l'esame di Programmazione in Rete e Laboratorio
    si terrà il giorno ...
    ----------------------------
  • equals(...) (per le classe Utente e Indirizzo): che restituisce true se l'oggetto passato come parametro è uguale all'oggetto su cui è invocato il metodo
Infine, per ogni campo, metodo e classe si scelga il tipo di visibilità (private? protected? public? default?).

Si definisca poi, in una classe ProvaMessaggio, un metodo main che effettui il test delle classi precedentemente definite, creando un certo numero di utenti e messaggi, e si testino i vari metodi.

Parte Seconda

Estendere la classe Messaggio in modo da permettere anche la specifica di destinatari in "carbon copy" (campo cc di tipo Indirizzo[]) di un messaggio. Si chiami tale classe MessaggioEsteso.
Ridefinire quindi i metodi getDestinatari() e toString() per la nuova classe.

Estendere la classe MessaggioEsteso in modo da permettere la possibilità di includere degli allegati ad un messaggio.
Per realizzare questo si definisca una interfaccia Allegato contenente il metodo public String toString() e quindi si definisca la classe MessaggioConAllegato che estende la classe MessaggioEsteso aggiungendo il campo allegato di tipo Allegato[]. Si ridefinisca nuovamente nella nuova classe i metodi opportuni.

Fare in modo che un oggetto di tipo Messaggio, MessaggioEsteso o MessaggioConAllegato possa essere a sua volta un allegato di un altro messaggio.

Si modifichi poi, nella classe ProvaMessaggio, il metodo main in modo da effettuare il test delle nuove classi. In particolare si crei un array di tipo Messaggio[] in cui vengono memorizzati vari tipi di messaggio (normali, di tipo esteso e con allegato) quindi si scriva un ciclo for per stamparli ed evidenziare di ognuno i destinatari utilizzando rispettivamente il metodo toString e getDestinatari.

Parte Terza

Si definisca una classe CasellaPostale che contenga i campi
  • proprietario: di tipo Utente che specifica il proprietario della casella postale
  • casella: di tipo List (si scelga l'implementazione preferita, ArrayList, LinkedList, Vector ma il campo casella deve essere di tipo List - java.util.List -) che rappresenta la lista dei messaggi ricevuti dall'utente proprietario della casella
ed i metodi
  • Utente getProprietario() che restituisce l'utente proprietario della casella
  • String listaMessaggi() che restituisce in una stringa le intestazioni dei messaggi contenuti nella casella attribuendo ad ognuno un numero progressivo, ad esempio, se stampata questa stringa dovrebbe essere qualcosa del tipo:
    ...
    66 elcvia@cvc.uab.es Wed Jan 30 19:50 "Call for papers ELCVIA"
    67 baroglio@di.unito.it Thu Jan 31 08:57 "Re: submission to UMUA"
    68 costa@di.unito.it Thu Jan 31 09:15 "Tabella dimostrativa d"
    69 garavell@di.unito.it Thu Jan 31 10:03 "aggiunta punto all'Odg"
    ...
    ossia, il numero prograssivo, l'indirizzo del mittente, la data, l'oggetto del messaggio.
  • String visualizzaMessaggio(int i) che restituisce in una stringa l'i-esimo messaggio se questo esiste o solleva un'opportuna eccezione in caso contrario
  • void cancellaMessaggio(int i) che cancella i-esimo messaggio se questo esiste o solleva un'opportuna eccezione in caso contrario
  • void inserisciMessaggio(Messaggio mes) che inserisce nella casella il messaggio mes
Infine, si cambi il tipo di implementazione per il campo casella (ad esempio, ArrayList anzichè Vector) e si verifichi che ancora tutto funzioni.

Parte Quarta

Si definisca un'interfaccia Comparabile che specifichi il seguente metodo:
  • int compara(Comparabile ob), le cui implementazioni si intende confrontino con this l'oggetto confrontabile ob restituendo -1, 0 o +1 a seconda che this sia minore, uguale o maggiore di ob, oppure generi un'eccezione (di tipo ClassCastException) se il confronto non è possibile
Si modifichi poi la definizione della classe Messaggio in modo che essa implementi l'interfaccia Comparabile, ad esempio confrontando rispetto al valore del campo da, oppure rispetto al campo data. Si definisca in una nuova classe (ad esempio SortArrays) un semplice metodo di ordinamento void sort(Comparabile[]) (statico?). Si aggiunga, quindi, alla classe CasellaPostale un metodo
  • public void ordina()
per ordinare i messaggi presenti in una casella. Il metodo ordina dovrà fare uso del metodo sort di SortArrays. Si noti che nelle libbrerie delle classi degli oggetti di tipo List sono presenti dei metodi per costruire un array da un oggetto di tipo List (si veda l'API Book) ma è altrettanto semplice definira da soli. Alternativamente, e forse è la soluzione più semplice, si può pensare che il metodo sort di SortArrays abbia come parametro un oggetto di tipo List i cui elementi sono di tipo Comparabile.

Si modifichi la definizione della classe Utente in modo che anch'essa implementi l'interfaccia Comparabile, ad esempio confrontando rispetto al nome e cognome di un utente e si applichi il metodo di ordinamento sort anche ad un array (o un oggetto di tipo List) di Utenti opportunamente creato.

Si esegua nuovamente il presente esercizio utilizzando l'interfaccia Comparable e la classe java.util.Arrays (oppure java.util.Collections nel caso si voglia utilizzare direttamente gli oggetti di tipo List) fornite con il Development Kit di Java al posto dell'interfaccia Comparabile e della classe SortArrays precedentemente usate.

Parte Quinta

Si definisca una classe Posta contenente il campo
  • casellePostali di tipo List che rappresenta la lista delle caselle postali presenti nel server
ed i metodi
  • Utente[] getUtenti(), restituisce un'array di tipo Utente contenente tutti gli attuali utenti della posta
  • CasellaPostale getCasellaPostale(Utente utente), restituisce la casella postale dell'utente specificato nel parametro
  • void addCasellaPostale(Utente utente), aggiunge una casella postale per l'utente specificato nel parametro
  • void deleteCasellaPostale(Utente utente), cancella la casella postale dell'utente specificato nel parametro
Ogni metodo solleva l'opportuna eccezione se l'operazione non è eseguibile. Si definisca poi una interfaccia ComandoMBTP che richieda l'implementazione del metodo
  • public String esegui(Posta posta) throws ComandoMBTPException;
Quindi si definiscano le seguenti classi che implementano l'interfaccia ComandoMBTP, una per ogni possibile comando eseguibile su di un oggetto di tipo Posta (si veda l'esempio nella sezione note e commenti).
  1. SendCommand, che ridefinisce esegui in modo da inserire l'oggetto di tipo Messaggio contenuto in un campo messaggio di tipo Messaggio (definito durante la creazione dell'oggetto di tipo SendCommand) nelle caselle postali dei destinatari del messaggio stesso
  2. ListCommand, che ridefinisce esegui in modo da restituire una stringa contenente l'intestazione dei messaggi nella casella postale dell'utente contenuto nel campo utente di tipo Utente
  3. ViewCommand, che ridefinisce esegui in modo da restituire una stringa contenente il messaggio che occupa la posizione definita dal campo posizione di tipo int della casella postale dell'utente del campo utente di tipo Utente
  4. DeleteCommand, che ridefinisce esegui in modo da cancellare il messaggio che occupa la posizione definita da posizione di tipo int della casella postale dell'utente definito dal campo utente di tipo Utente
  5. SubscribeCommand, che ridefinisce esegui in modo da creare una nuova casella di posta il cui proprietario è l'utente definito dal campo utente di tipo Utente
  6. UnsubscribeCommand, che ridefinisce esegui in modo da cancellare la casella di posta dell'utente definito dal campo utente di tipo Utente
  7. SortCommand, che ridefinisce esegui in modo da ordinare la casella di posta dell'utente definito dal campo utente di tipo Utente
Infine definire una semplice classe SimpleServer che crea un oggetto di tipo Posta ed invoca su di esso alcuni comandi in un metodo main (si veda l'esempio nella sezione note e commenti).

Parte Sesta

Si realizzi un server che metta in ascolto un socket server su una porta specificata per la gestione di un oggetto di tipo Posta. Tale server deve essere in grado di gestire più client contemporaneamente. I client inviano al server oggetti di tipo CommandMBTP (mediante stream di oggetti), su tali oggetti il server invoca il medoto esegui, passandogli l'oggetto di tipo Posta memorizzato presso di esso ed invia al client un oggetto di tipo String. Questo può essere il risultato della corretta esecuzione del metodo esegui oppure il messaggio restituito da una possibile eccezione (vedi Figura 1). Si noti che è importante gestire le eccezioni in modo da non bloccare il server in caso di errore e nello stesso tempo informare il client del malfunzionamento.

Un altro aspetto fondamentale è gestire correttamente l'accesso alle risorse condivise. In particolare, l'inserimento o la cancellazione di un utente (quindi la cancellazione e l'inserimento nella lista di oggetti di tipo CasellePostale) deve essere fatto in mutua esclusione. L'inserimento, la cancellazione, l'ordinamento dei messaggi in una casella postale deve anche essere fatto in mutua esclusione (due client potrebbero contemporaneamente richiedere la stessa operazione sulla stessa casella, come l'inserimento di un messaggio). A tal fine si modifichino le opportune classi in modo da dichiarare synchronized gli opportuni metodi (ma solo quelli necessari!).

Parte Settima

Si realizzi un'intefaccia grafica del tipo mostrata in Figura 2. Queste deve prevedere la possibilità di attivare un'altra finistra del tipo mostrata in Figura 3 per comporre ed inviare inviare un messaggio. La configurazione del client di posta (IP, porta del server, informazioni dell'utente) dovrebbe essere fatta mediante un'opportuna finestra di tipo "dialog" del tipo mostrata in Figura 4.

Parte Ottava

Si ripeta l'esercizio della parte sesta in modo da sostituire il server di posta basato su scambi di comandi via socket con un server basato su RMI.
Si realizzi cioè una classe ServerPostaRMIImpl che implementa un'interfaccia ServerPostaRMI del tipo

public interface ServerPostaRMI extends java.rmi.Remote {
  public String inviaEdEseguiCommandMBTP(CommandMBTP command)
    throws java.io.IOException, java.rmi.RemoteException;
}
L'implementazione del metodo inviaEdEseguiCommandMBTP riceve un oggetto di tipo CommandMBTP, quindi su di esso invoca il metodo esegui passandogli l'oggetto Posta memorizzato presso di esso e restituisce l'oggetto di tipo String risultato dell'esecuzione di esegui.
I client inviano al server oggetti di tipo CommandMBTP invocando sull'oggetto di tipo ServerPostaRMI remoto il metodo inviaEdEseguiCommandMBTP e ottengono la risposta dell'esecuzione del comando come valore di ritorno. Si noti che anche in questo caso è importante gestire le eccezioni in modo da non bloccare il server in caso di errore e nello stesso tempo informare il client del malfunzionamento. A differenza del server basato su socket si sfrutti la possibilità di restituire delle eccezioni sollevate remotamente offerta dal meccanismo RMI. Si discuta come un meccanismo analogo potrebbe essere adottato anche per il server basato su socket.

Parte Nona

Si estenda l'interfaccia grafica descritta nella parte settima introducendo le seguenti nuove funzionalità:
  • la possibilità di salvare un messaggio come oggetto in un file;
  • la possibilità di inviare messaggi con allegato un altro messaggio letto da un file.

Note e Commenti

  • Per maggiore semplicità d'uso, definire per la classe Utente e Messaggio più di un costruttore che accetti parametri di diverso tipo. In particolare, per a classe Utente si definiscano i costruttori:
    1. public Utente(String nm, String cg, Indirizzo ind)
    2. public Utente(string nm, String cg, String ind)
    La 2., a differenza della 1., dovrà occuparsi di creare al suo interno l'oggetto di tipo Indirizzo utilizzando il parametro di tipo String ind passato al costruttore.
    Per la classe Messaggio si definiscano:
    1. public Messaggio(Indirizzo from, Indirizzo[] to, Data dt, String subject, String body)
    2. public Messaggio(Utente from, Utente[] to, Data dt, String subject, String body)
    3. public Messaggio(String from, String[] to, Data dt, String subject, String body)
    La 2. dovrà occuparsi di estrarre l'indirizzo dall'oggetto di tipo Utente passato come parametro (si usi getIndirizzo() di Utente), la 3. dovrà invece creare l'indirizzo utilizzando le stringhe passate come parametro.
    Attenzione aggiungere questi costruttori non significa che possiamo aggiungere dei campi da e a di tipo Utente o String ma che utilizzeremo i parametri passati per inizializzare questi nella maniera opportuna. Ad esempio se usiamo la 3. allora l'inizializzazione di a (all'interno del costruttore stesso) avverrà nel seguente modo
    da = new Indirizzo(from);
    a = new Indirizzo[to.lenght];
    for (int i=0; i < a.lenght; i++)
         a[i] = new Indirizzo(to[i]);
    mentre se uso la 2.
    da = from.getIndirizzo();
    a = new Indirizzo[to.lenght];
    for (int i=0; i < a.lenght; i++)
         a[i] = to[i].getIndirizzo();
  • Anche per i campi cc di MessaggioEsteso e allegato di MessaggioConAllegato si definiscano i metodi di tipo set e get.
  • In modo analogo alla classe Messaggio anche per la classe MessaggioEsteso e MessaggioConAllegato è una buona idea avere più costruttori. Per esempio, per la classe MessaggioEsteso:
    1. public MessaggioEsteso(Indirizzo from, Indirizzo[] to, Indirizzo[] carbonCopy, Data dt, String subject, String body)
    2. public Messaggio(Utente from, Utente[] to, Utente[] carbonCopy, Data dt, String subject, String body)
    3. public Messaggio(String from, String[] to, String[] carbonCopy, Data dt, String subject, String body)
    Su come utilizzare carbonCoby per inizzializzare cc si faccia riferimento alla nota precedente.
  • Un'alternativa alla ridefinizione del metodo toString() nella classe MessaggioEsteso è di definire in Messaggio un metodo del tipo
    public String destinatariToString()
    che restituisce una scritta del tipo
    A: st9...@educ.di.unito.it, st9...@educ.di.unito.it
    che viene utilizzata nella toString() al posto di prelevare direttamente il valore del campo a e convertirlo in una stringa, quindi ridefinire il metodo destinatariToString() in MessaggioEsteso in modo da restituire una scritta del tipo
    A: st9...@educ.di.unito.it, st9...@educ.di.unito.it
    CC: st9...@educ.di.unito.it, st9...@educ.di.unito.it
    e continuare ad utilizzare il precedente metodo toString().
  • Per esempio, la classe ViewCommand potrebbe essere realizzata nel seguente modo:
    public class ViewCommand implements ComandoMBTP {
      private Utente utente;
      private int posizione;
      public ViewCommand(Utente ut, int pos) {
        utente = ut;
        posizione = pos;
      }
      public ... get...
      public void set...
      public String esegui(Posta pt) throws ComandoMBTPException {
        CasellaPostale cp = pt.getCasellaPostale(utente);
        return cp.visualizzaMessaggio(posizione);
        // oppure
        // return (pt.getCasellaPostale(utente)).visualizzaMessaggio(posizione);
      }
    }
    Le altre cinque classi sono del tutto analoghe.
  • Come utilizzare i comandi in SimpleServer? Supponiamo di voler creare due utenti (matteo e st8714600) e quindi di iscriverli al servizio di posta (posta) e di spedire un messaggio da matteo a st8714600. Poi l'utente st8714600 guarda la propria lista di messaggi e quindi legge il primo della lista. Il metodo main di SimpleServer dovrebbe contenere tra le altre le seguenti istruzioni:
    ...
    Posta posta = new Posta();
    ...
    // creazione di due utenti in posta
    Utente matteo = new Utente( ... );
    Utente st8714600 = new Utente( ... );
    ...
    // creazione dei comandi SUBSCRIBE
    ComandoMBTP subscribeMatteo = new SubscribeCommand(matteo);
    ComandoMBTP subscribeST8714600 = new SubscribeCommand(st8714600);
    ...
    // esecuzione dei comandi SUBSCRIBE
    subscribeMatteo.esegui(posta);
    subscribeST8714600.esegui(posta);
    ...
    // creazione di un messaggio Messaggio mes1 = new Messaggio(matteo, new Utente[] {st8714600}, " oggetto ", " testo " );
    ...
    // creazione del comando di invio di un messaggio
    ComandoMBTP inviaMes1 = new SendCommand(mes1)
    ...
    // esecuzione dell'invio di inviaMes1
    inviaMes1.esegui(posta);
    ...
    // creazione del comando di visualizzazione lista messaggi
    ComandoMBTP leggiMessaggi = new ListCommand(st8714600);
    ...
    // esecuzione del comando leggiMessaggi e stampa del risultato
    System.out.println(leggiMessaggi.esegui(posta));
    ...
    // creazione del comando di visualizzazione del primo messaggio della lista
    ComandoMBTP leggiIlPrimo = new ViewCommand(st8714600, 1);
    ...
    // esecuzione del comando leggiIlPrimo e stampa del risultato
    System.out.println(leggiIlPrimo.esegui(posta));
    ...
    La necessità di passare come parametro attuale del metodo esegui l'oggetto posta sarà chiara nella parte sesta del laboratorio.
  • Il metodo CasellaPostale getCasellaPostale(Utente utente) nella classe Posta è utilizzato per realizzare il comando di send di un messaggio e necessita come parametro un oggetto di tipo Utente. Ora in un messaggio i destinatari sono rappresentati da un oggetto array di indirizzi e non di utenti e non è quindi possibile recuperare l'utente proprietario di quell'indirizzo per estrarne la sua casella postale. Per ovviare a questo problema si può semplicemente "indebolire" la nozione di uguaglianza tra due utenti e basarla escusivamente su quella dell'indirizzo:
    public class Utente {
      private Indirizzo indirizzo;
      ...
      public boolean equals(Utente ut) {
        return indirizzo.equals(ut.getIndirizzo());
      }
      ...
    }
    quindi prima di utilizzare il metodo getCasellaPostale creare un utente fittizio:
      ...
      CasellaPostale cp = posta.getCasellaPostale(new Utente("","",indirizzo));
      ...
    dove indirizzo è un indirizzo di un destinatario estratto dal messaggio che si desidera inviare.
    Questa soluzione ha il vantaggio di rendere "uniforme" l'implementazione ovunque venga usato il test di uguaglianza tra due utenti, si pensi, ad esempio, al controllo se un utente è già iscritto ad un servizio di posta prima di iscriverlo nuovamente nel comando SubscribeCommand.
  • È bene far sempre restituire un oggetto di tipo String da tutte le implementazioni del metodo esegui dei vari comandi. Ad esempio per il comando di invio la stringa restituita potrebbe essere "Mail Sent!".
  • Un esempio di classe client per la parte sesta potrebbe essere la seguente. Si noti che questa è una parafrasi della classe SimpleServer usata in un precedente note e commenti.
    Supponiamo di voler creare due utenti (matteo e st8714600) e quindi di iscriverli al servizio di posta (posta) e di spedire un messaggio da matteo a st8714600. Poi l'utente st8714600 guarda la propria lista di messaggi e quindi legge il primo della lista. Il metodo main di SimpleSocketClient dovrebbe contenere tra le altre le seguenti istruzioni:
    ...
    // Connessione al server Socket socket = new Socket("130.192.239.194", 2001); ObjectOutput out = new ObjectOutputStream(socket.getOutputStream());; ObjectInputStream in = new ObjectInputStream(socket.getInputStream()); ...
    // creazione di due utenti in posta
    Utente matteo = new Utente( ... );
    Utente st8714600 = new Utente( ... );
    ...
    // creazione dei comandi SUBSCRIBE
    ComandoMBTP subscribeMatteo = new SubscribeCommand(matteo);
    ComandoMBTP subscribeST8714600 = new SubscribeCommand(st8714600);
    ...
    // invio dei comandi SUBSCRIBE
    out.writeObject(subscribeMatteo);
    System.out.println(in.readobject());
    out.writeObject(subscribeST8714600);
    System.out.println(in.readobject());
    ...
    // creazione di un messaggio Messaggio mes1 = new Messaggio(matteo, new Utente[] {st8714600}, " oggetto ", " testo " );
    ...
    // creazione del comando di invio di un messaggio
    ComandoMBTP inviaMes1 = new SendCommand(mes1)
    ...
    // invio del comando inviaMes1
    out.writeObject(inviaMes1);
    System.out.println(in.readobject());
    ...
    // creazione del comando di visualizzazione lista messaggi
    ComandoMBTP leggiMessaggi = new ListCommand(st8714600);
    ...
    // invio del comando leggiMessaggi e stampa del risultato
    out.writeObject(leggiMessaggi);
    System.out.println(in.readObject());
    ...
    // creazione del comando di visualizzazione del primo messaggio della lista
    ComandoMBTP leggiIlPrimo = new ViewCommand(st8714600, 1);
    ...
    // invio del comando leggiIlPrimo e stampa del risultato
    out.writeObject(leggiIlPrimo);
    System.out.println(in.readObject(leggiIlPrimo));
    ...
    Più possono essere creati scrivento più classi del tipo di SimpleSocketClient.
  • Si noti che per trasmettere via socket un oggetto di tipo CommandMBTP è necessario che questo sia anche di tipo java.io.Serializable (cioè implementi tale interfaccia) come anche tutti gli oggetti referenziati da un oggetto di questo tipo.
  • È importante aggiungere un nuovo comando di tipo CommandMBTP per indicare al server che un client desidera chiudere una connessione. Seguendo il suggerimento di un vostro collega vi suggerisco la seguente soluzione.
    Si scriva un nuovo comando di tipo COmmandMBTP che possiamo chiamare EndCommand la cui implementazione di esegui restituisce sempre la stringa tipo "END CONNECTION". A questo punto sarà sufficiente testare se la stringa restituita durante l'esecuzione (invocazione di esegui) di un comando restituisce quuella stringa per interrompere il loop di un while.
    ...
    boolean go_on = true;
    while (go_on) {
      ...
      CommandMBTP command = (CommandMBTP)in.readObject();
      String result = command.esegui(posta);

      if (result.equals("END CONNECTION"))
        go_on = false;
      out.writeObject(result); // COSÌ L'UTENTE VEDE CHE LA CONNESSIONE VIENE CHIUSA COME RICHIESTO

    ... 
    // chiusura del socket 
    La classe EndCommand potrebbe essere realizzata nel seguente modo:
    public class EndCommand implements ComandoMBTP {
      public String esegui(Posta pt) throws ComandoMBTPException {
        return "END CONNECTION";
      }
    }
  • Si noti che nel server da realizzare nella parte ottava, a differenza del server della parte sesta, non sarà necessario realizzare alcun ciclo per la gestione di più comandi nel server non essendo più necessario mettere il server in attesa sullo stream di input.
  • FInestre di tipo dialog con più caselle di input possono essere realizzate mediante la classe javax.swing.JOptionPane. Si faccia riferimento al Java 2 Platform API Specification per un esempio di uso.
  • Nell'interfaccia grafica è presente il pulsante "List Users". Per realizzare tale funzionalità si aggiunga un nuovo comando, diciamo ListCommand che restituisca la lista degli utenti registrati in un dato momento presso il server di posta (metodo getUtenti della classe Posta).
  • La Figura 3 è stata aggiornata con la casella per l'inserimento dell'oogetto del messaggio da inviare.


[Matteo Baldoni's HOME] [Corso di Laurea e Diploma in Informatica] [Department's HOME]

Last update: Nov 05, 2002