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 --->
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 --->
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:
-
un floppy disk contenente il codice sorgente e compilato del progetto svolto con tutte le
istruzioni necessarie ad un facile utilizzo del software prodotto
-
il listato stampato del codice sorgente
-
la documentazione generata mediante javadoc (sul floppy disk)
-
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
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).
- 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
- 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
- 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
- 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
- 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
- UnsubscribeCommand, che ridefinisce esegui
in modo da cancellare la casella di posta dell'utente
definito dal campo utente di tipo Utente
- 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:
- public Utente(String nm, String cg, Indirizzo ind)
- 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:
- public Messaggio(Indirizzo from, Indirizzo[] to, Data dt, String subject, String body)
- public Messaggio(Utente from, Utente[] to, Data dt, String subject, String body)
- 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:
- public MessaggioEsteso(Indirizzo from, Indirizzo[] to, Indirizzo[] carbonCopy, Data dt, String subject, String body)
- public Messaggio(Utente from, Utente[] to, Utente[] carbonCopy, Data dt, String subject, String body)
- 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.
|