Class CallMsgManager

java.lang.Object
  |
  +--java.lang.Thread
        |
        +--CallMsgManager

public class CallMsgManager
extends java.lang.Thread

Thread che si occupa della gestione delle connessioni con i FE rispondendo ai cosiddetti call messages. Gestisce il protocollo di comunicazione con il FE e si occupa di eseguire le operazioni richieste.

Author:
Mauro Barbieri
See Also:
CallMsgServer

Field Summary
private static boolean DEBUG
          Flag per abilitare stampe di debugging su standard output e standard error.
private  boolean GOSSIP_BY_NEED
          Flag che indica che il CallMsgManager deve effettuare gossip by need.
private  int GOSSIP_SLEEP
          Rappresenta il numero di millisecondi che un thread CallMsgManager attende (se il flag GOSSIP_BY_NEED e' settato a false) prima di ritestare se puo' eseguire una query la cui esecuzione deve essere ritardata fino a che lo stato del RM non e' aggiornato tanto quanto quello del FE che l'ha richiesta.
private  java.io.ObjectInputStream in
          Per la lettura dei dati inviati dai FE e' usato un oggetto java.io.ObjectInputStream poiche' implementa l'interfaccia java.io.DataInput usabile anche per i tipi primitivi di java.
private  java.io.ObjectOutputStream out
          Per l'invio delle risposte ai FE e' usato un oggetto java.io.ObjectOutputStream poiche' implementa l'interfaccia java.io.DataOutput usabile anche per i tipi primitivi di java.
private  java.net.Socket socket
          Socket per la connessione con il FE.
private  StatoRM statoRM
          Riferimento alle variabili che rappresentano lo stato del Replica Manager di cui si deve gestire il call message.
 
Fields inherited from class java.lang.Thread
contextClassLoader, daemon, eetop, group, inheritedAccessControlContext, MAX_PRIORITY, MIN_PRIORITY, name, NORM_PRIORITY, priority, single_step, stillborn, stopThreadPermission, target, threadInitNumber, threadQ, values
 
Constructor Summary
CallMsgManager(java.net.Socket socket, StatoRM stato)
          Crea un'istanza di CallMsgManager data la socket di connessione con il FE e un riferimento allo stato del RM.
CallMsgManager(java.net.Socket socket, StatoRM stato, java.lang.String name)
          Crea un'istanza di CallMsgManager data la socket di connessione con il FE, un riferimento allo stato del RM e una stringa che rappresenta il nome del thread.
 
Method Summary
 boolean getGossipByNeed()
          Restituisce il valore del flag GOSSIP_BY_NEED : se true indica che il CallMsgManager effettua gossip by need.
 int getGossipSleep()
          Restituisce il numero di millisecondi che un thread CallMsgManager attende (se il flag GOSSIP_BY_NEED e' settato a false) prima di ritestare se puo' eseguire una query la cui esecuzione deve essere ritardata fino a che lo stato del RM non e' aggiornato tanto quanto quello del FE che l'ha richiesta.
private  void println(java.lang.String str)
          Stampa nella finestra di output Output il nome dello thread corrente seguito dalla stringa passata come argomento e va a capo.
private  void processAck()
          Esegue il comando ACK.
private  void processGet(MultipartTimestamp prev)
          Esegue il comando GET.
private  void processList(MultipartTimestamp prev)
          Esegue il comando LIST.
private  void processLookup()
          Esegue il comando LOOKUP.
private  void processNum()
          Esegue il comando NUM.
private  void processPut(MultipartTimestamp prev)
          Esegue il comando PUT.
 void run()
          Ridefinisce l'omonimo metodo della superclasse java.lang.Thread implementando il protocollo di comunicazione con il FE ed effettuando le operazioni che gli vengono richieste.
 void setGossipByNeed(boolean flag)
          Setta il flag GOSSIP_BY_NEED : se settato a true indica che il CallMsgManager deve effettuare gossip by need.
 void setGossipSleep(int millis)
          Setta il numero di millisecondi che un thread CallMsgManager attende (se il flag GOSSIP_BY_NEED e' settato a false) prima di ritestare se puo' eseguire una query la cui esecuzione deve essere ritardata fino a che lo stato del RM non e' aggiornato tanto quanto quello del FE che l'ha richiesta.
private  void testQuery(MultipartTimestamp prev)
          Verifica se puo' eseguire la query : se lo stato del RM non e' aggiornato tanto quanto quello del FE la query non puo' essere eseguita.
 
Methods inherited from class java.lang.Thread
, activeCount, checkAccess, countStackFrames, currentThread, destroy, dumpStack, enumerate, exit, getContextClassLoader, getName, getPriority, getThreadGroup, init, interrupt, interrupt0, interrupted, isAlive, isDaemon, isInterrupted, isInterrupted, join, join, join, nextThreadNum, registerNatives, resume, resume0, setContextClassLoader, setDaemon, setName, setPriority, setPriority0, sleep, sleep, start, stop, stop, stop0, suspend, suspend0, toString, yield
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Field Detail

statoRM

private StatoRM statoRM
Riferimento alle variabili che rappresentano lo stato del Replica Manager di cui si deve gestire il call message.

socket

private java.net.Socket socket
Socket per la connessione con il FE.

out

private java.io.ObjectOutputStream out
Per l'invio delle risposte ai FE e' usato un oggetto java.io.ObjectOutputStream poiche' implementa l'interfaccia java.io.DataOutput usabile anche per i tipi primitivi di java.

in

private java.io.ObjectInputStream in
Per la lettura dei dati inviati dai FE e' usato un oggetto java.io.ObjectInputStream poiche' implementa l'interfaccia java.io.DataInput usabile anche per i tipi primitivi di java.

GOSSIP_BY_NEED

private boolean GOSSIP_BY_NEED
Flag che indica che il CallMsgManager deve effettuare gossip by need. Se settato a true, nel caso una query non possa essere eseguita perche' lo stato del FE e' piu' aggiornato di quello del RM, il thread CallMsgManager richiede esplicitamente un aggiornamento agli altri RM mediante il protocollo di gossip. Se settato a false, nel caso una query non possa essere eseguita perche' lo stato del FE e' piu' aggiornato di quello del RM, il thread CallMsgManager si sospende per GOSSIP_SLEEP millisecondi in attesa che altri thread aggiornino lo stato del RM. Terminata l'attesa riverifica se puo' eseguire la query, in caso contrario ripete il procedimento di attesa e test. Il valore di default e' true.
See Also:
GOSSIP_SLEEP, setGossipByNeed(boolean), getGossipByNeed()

GOSSIP_SLEEP

private int GOSSIP_SLEEP
Rappresenta il numero di millisecondi che un thread CallMsgManager attende (se il flag GOSSIP_BY_NEED e' settato a false) prima di ritestare se puo' eseguire una query la cui esecuzione deve essere ritardata fino a che lo stato del RM non e' aggiornato tanto quanto quello del FE che l'ha richiesta. Il valore di default e' 1000 millisecondi.
See Also:
GOSSIP_BY_NEED, setGossipSleep(int), getGossipSleep()

DEBUG

private static final boolean DEBUG
Flag per abilitare stampe di debugging su standard output e standard error.
Constructor Detail

CallMsgManager

public CallMsgManager(java.net.Socket socket,
                      StatoRM stato)
Crea un'istanza di CallMsgManager data la socket di connessione con il FE e un riferimento allo stato del RM.
Parameters:
socket - socket di connessione con il FE.
stato - riferimento allo stato del RM.

CallMsgManager

public CallMsgManager(java.net.Socket socket,
                      StatoRM stato,
                      java.lang.String name)
Crea un'istanza di CallMsgManager data la socket di connessione con il FE, un riferimento allo stato del RM e una stringa che rappresenta il nome del thread.
Parameters:
socket - socket di connessione con il FE.
stato - riferimento allo stato del RM.
name - nome da assegnare al thread.
Method Detail

setGossipByNeed

public void setGossipByNeed(boolean flag)
Setta il flag GOSSIP_BY_NEED : se settato a true indica che il CallMsgManager deve effettuare gossip by need. Nel caso una query non possa essere eseguita perche' lo stato del FE e' piu' aggiornato di quello del RM, il thread CallMsgManager richiede esplicitamente un aggiornamento agli altri RM mediante il protocollo di gossip. Se settato a false, nel caso una query non possa essere eseguita perche' lo stato del FE e' piu' aggiornato di quello del RM, il thread CallMsgManager si sospende per GOSSIP_SLEEP millisecondi in attesa che altri thread aggiornino lo stato del RM. Terminata l'attesa riverifica se puo' eseguire la query, in caso contrario ripete il procedimento di attesa e test. Il valore di default del flag e' true.
Parameters:
flag - true per abilitare il gossip by need, false per disabilitarlo.
See Also:
GOSSIP_BY_NEED, GOSSIP_SLEEP, getGossipByNeed()

getGossipByNeed

public boolean getGossipByNeed()
Restituisce il valore del flag GOSSIP_BY_NEED : se true indica che il CallMsgManager effettua gossip by need. Nel caso una query non possa essere eseguita perche' lo stato del FE e' piu' aggiornato di quello del RM, il thread CallMsgManager richiede esplicitamente un aggiornamento agli altri RM mediante il protocollo di gossip. Se false, nel caso una query non possa essere eseguita perche' lo stato del FE e' piu' aggiornato di quello del RM, il thread CallMsgManager si sospende per GOSSIP_SLEEP millisecondi in attesa che altri thread aggiornino lo stato del RM. Terminata l'attesa riverifica se puo' eseguire la query, in caso contrario ripete il procedimento di attesa e test.
Returns:
true se il gossip by need e' abilitato.
See Also:
GOSSIP_BY_NEED, GOSSIP_SLEEP, setGossipByNeed(boolean)

setGossipSleep

public void setGossipSleep(int millis)
Setta il numero di millisecondi che un thread CallMsgManager attende (se il flag GOSSIP_BY_NEED e' settato a false) prima di ritestare se puo' eseguire una query la cui esecuzione deve essere ritardata fino a che lo stato del RM non e' aggiornato tanto quanto quello del FE che l'ha richiesta.
Parameters:
millis - numero di millisecondi di attesa.
See Also:
GOSSIP_BY_NEED, GOSSIP_SLEEP, getGossipSleep()

getGossipSleep

public int getGossipSleep()
Restituisce il numero di millisecondi che un thread CallMsgManager attende (se il flag GOSSIP_BY_NEED e' settato a false) prima di ritestare se puo' eseguire una query la cui esecuzione deve essere ritardata fino a che lo stato del RM non e' aggiornato tanto quanto quello del FE che l'ha richiesta.
Returns:
il numero di millisecondi di attesa.
See Also:
GOSSIP_BY_NEED, GOSSIP_SLEEP, setGossipSleep(int)

run

public void run()
Ridefinisce l'omonimo metodo della superclasse java.lang.Thread implementando il protocollo di comunicazione con il FE ed effettuando le operazioni che gli vengono richieste. Processa i comandi inviati dal FE fino a che quest'ultimo non chiude la connessione oppure non e' stato possibile portare a termine l'esecuzione di un comando per problemi gravi. In tal caso i metodi che processano i comandi sollevano un'eccezione che viene catturata e nella cui gestione si liberano le risorse impegnate per la comunicazione con il FrontEnd.
Overrides:
run in class java.lang.Thread

testQuery

private void testQuery(MultipartTimestamp prev)
Verifica se puo' eseguire la query : se lo stato del RM non e' aggiornato tanto quanto quello del FE la query non puo' essere eseguita. Se il flag GOSSIP_BY_NEED e' settato a true, il thread richiede esplicitamente un aggiornamento del suo stato da altri RM mediante il protocollo gossip. Se il flag GOSSIP_BY_NEED e' settato a false, il thread si sospende per GOSSIP_SLEEP millisecondi, dopodiche' ripete indefinitamente test ed eventuale sospensione.
Parameters:
prev - multipart timestamp del FE che ha richiesto il comando.
See Also:
GOSSIP_BY_NEED, GOSSIP_SLEEP

processList

private void processList(MultipartTimestamp prev)
                  throws java.lang.Exception
Esegue il comando LIST.
Parameters:
prev - multipart timestamp del FE che ha richiesto il comando.

processGet

private void processGet(MultipartTimestamp prev)
                 throws java.lang.Exception
Esegue il comando GET.
Parameters:
prev - multipart timestamp del FE che ha richiesto il comando.

processAck

private void processAck()
                 throws java.lang.Exception
Esegue il comando ACK.

Esecuzione delle operazioni di ack :
e' necessario incrementare di uno l'elemento del local timestamp rep_ts corrispondente al RM ed aggiungere un record di ack al log. Tale record deve contenere una "copia" del multipart timestamp rep_ts : e' necessario quindi un metodo per ottenere un nuovo multipart timestamp a partire da uno esistente. Affinche' l'ack record rifletta il valore esatto di rep_ts bisognerebbe garantire che questo non venga alterato da altri thread tra l'operazione di incremento e quella di copia.
Soluzioni :

a) il metodo incElementAt(int) di MultipartTimestamp potrebbe restituire una copia del multipart timestamp. Tale metodo si potrebbe avvalere di un ulteriore metodo
	protected MultipartTimestamp clone2() 
della classe MultipartTimestamp per la copia. Si puo' ridefinire il metodo clone() di java.lang.Object (non basta quello della superclasse Object perche' fa copie shallow) con la differenza che restituisce un java.lang.Object (necessita' di casting), lancia l'eccezione java.lang.CloneNotSupportedException e la classe MultipartTimestamp deve implementare l'interfaccia java.lang.Cloneable.

b) aggiunta di un metodo
	public synchronized MultipartTimestamp clone2() 
alla classe MultipartTimestamp che restituisce un nuovo oggetto MultipartTimestamp copia esatta di quello su cui e' invocato il metodo. Vedi sopra per ridefinire clone() di java.lang.Object. Questo metodo consente di copiare in modo thread-safe rep_ts. In maniera asincrona, poi, si possono incrementa rep_ts e la copia prima di includere quest'ultima nell'ack record.

c) come la b) solo che il metodo clone() ridefinisce quello di Object.

La soluzione a) e' apparentemente piu' efficiente tuttavia non c'e' nessun motivo compatibile con la semantica di un MultipartTimestamp per far ritornare al metodo incElementAt(int) un nuovo oggetto. La soluzione b) sembra piu' elegante e non obbliga la classe MultipartTimestamp ad avere metodi "strani" dovuti all'uso che altre classi vogliono fare della struttura dati che implementa. E' piu' vicina all'idea di "meccanismo" sul quale basare varie "politiche". L'approccio "ad hoc" della soluzione a) richiederebbe nuovi metodi per nuove possibili esigenze di sincronizzazione.
Per la soluzione c) valgono le stesse considerazioni della b), inoltre :
- la ridefinizione di un metodo di Object e' piu' "object oriented"
- il metodo clone di java.lang.Object() restituisce un Object e quindi e' necessario un cast esplicito
- il metodo clone di Object lancia una java.lang.CloneNotSupportedException
- MultipartTimestamp deve implementare java.lang.Cloneable
La b) e' piu' efficiente della c) non gestendo l'eccezione e non richiedendo casting.

In conclusione si e' scelta la soluzione c (e' immediato passare alla b) Il metodo clone() puo' eventualmente essere riusato per passare alla a).
DOPO AVER ESAMINATO MEGLIO IL PROBLEMA :
Anche la gestione di un comando di update (metodo processPut) richiede di effettuare in modo atomico le operazioni :
- incrementa il local timestamp
- ottieni una copia del local timestamp
La soluzione a) prima scartata viene quindi rivalutata facendone discendere una nuova soluzione :

d) aggiunta del metodo
	public synchronized MultipartTimestamp advanceTimestamp(int) 
alla classe MultipartTimestamp che incrementa l'elemento specificato di uno e restituisce una copia del nuovo stato del MultipartTimestamp, il tutto in maniera atomica.
Anche nella classe StatoRM e' aggiunto un metodo
	public MultipartTimestamp advanceAndCloneLocalTimestamp() 
che invoca il precedente sulla variabile rep_ts.
Conclusione, scelta della soluzione d :

- il metodo advanceLocalTimestamp() di StatoRM resta tale e quale
- il metodo incElementAt(int) di MultipartTimestamp resta tale e quale
- aggiunta del metodo advanceTimestamp(int) alla classe MultipartTimestamp da usare al posto di clone() che comunque non viene per ora eliminato
- aggiunta anche del metodo clone2() per migliorare l'efficienza. - aggiunta del metodo advanceAndCloneLocalTimestamp() che ritorna un nuovo MultipartTimestamp alla classe StatoRM

Parameters:
prev - multipart timestamp del FE che ha richiesto il comando.

processPut

private void processPut(MultipartTimestamp prev)
                 throws java.lang.Exception
Esegue il comando PUT. Per i problemi di sincronizzazione vedi processAck()
Parameters:
prev - multipart timestamp del FE che ha richiesto il comando.
See Also:
processAck()

processNum

private void processNum()
                 throws java.lang.Exception
Esegue il comando NUM.

processLookup

private void processLookup()
                    throws java.lang.Exception
Esegue il comando LOOKUP.

println

private void println(java.lang.String str)
Stampa nella finestra di output Output il nome dello thread corrente seguito dalla stringa passata come argomento e va a capo. Ha lo stesso effetto dell'invocazione Output.println(Thread.currentThread().getName()+" : "+str).
Parameters:
str - stringa da stampare nella finestra di output Output.
See Also:
Output, Output.println(String)