Componenti:
L’implementazione della soluzione è stata realizzata mediante il linguaggio Scala, in particolare utilizzando la libreria Akka, con attori tipizzati e behaviors.
La soluzione proposta per la versione CLI è ispirata alla strategia divide et impera, esplorando la directory in modo ricorsivo. Vengono utilizzati i seguenti attori:
La soluzione proposta per la versione GUI estende quella precedentemente descritta nell’approccio CLI. Dal momento che si vogliono osservare le statistiche in tempo reale e una leaderboard dei file più grandi, sono state aggiunte altre tipologie di attori. Inoltre, gli attori già descritti, cambiano la modalità di aggregazione dei risultati, non utilizzato più una strategia divide et impera.
Vengono utilizzati i seguenti attori:
La soluzione proposta si basa sull’utilizzo di un Message Oriented Middleware, in particolare è stato utilizzato RabbitMQ.
Vi sono diversi eventi che richiedono uno scambio di messagi tra peers. Per ciascuno di essi ogni client possiede una coda di ricezione. Tali code sono collegate ad un exchange (specifico di un particolare tipo di evento). Sono stati utilizzati exchange di tipo fanout, in modo che, ogni qualvolta un client invia un messaggio relativo ad un evento su quell’exchange, tutti gli altri utenti collegati ad esso possono riceverne una copia. Inoltre, anche il client stesso riceve il proprio evento: questo garatisce uno stato consistente tra i vari client.
Gli exchange, con i rispettivi tipi di messaggi, sono:
PIXEL_COLOR_EXCHANGE
: per notificare che un pixel del griglia è stato colorato.
PixelColor {
user: UUID
x: Int,
y: Int,
}
MOUSE_MOVE_EXCHANGE
: per notificare le posizioni dei puntatori dei vari utenti.
MouseMove {
user: UUID
x: Int,
y: Int,
}
COLOR_CHANGE_EXCHANGE
: per notificare il cambio di colore dei puntatori degli utenti.
ColorChange {
uuid: UUID,
color: Int
}
USER_EXIT_EXCHANGE
: per notificare l’abbandono della sessione di un utente.
UserExit {
uuid: UUID
}
L’implentazione di questa funzionalità si basa sul pattern RPC. In particolare, un utente intenzionato a unirsi alla sessione, invia un messaggio di richiesta di join su una coda dedicata, sulla quale, tutti gli utenti che già partecipano alla sessione, sono in ascolto. Dato che, in generale, la ricezione di un messaggio da parte di un client su una coda, fa sì che il messaggio venga consumato, la richiesta di join verrà presa in carico da un solo utente (in particolare RabbitMQ, adotta una politica Round-robin, si veda qui). Dunque, l’utente che riceve la richiesta di join, non farà altro che inviare lo stato della propria griglia insieme alla lista degli utenti che partecipano alla sessione.
StateRequest { }
StateReply {
grid: PixelGrid,
users: Map<UUID, Int>
}
Dato che la richiesta dello stato non comporta lo stop dei processi, è importante per il client che è intenzionato a partecipare alla sessione, collegarsi preventivamente con le proprie code agli exchange degli eventi (in particolare pixel_color e color_change) in modo tale che, quando riceve lo stato della griglia, possa applicare gli eventi ricevuti e bufferizzati, evitanto di trovarsi in uno stato inconsistente.
In RabbitMQ, le callback di ricezione di un messaggio vengono eseguite da un pool di thread. Per questo motivo
si è reso necessario l’utilizzo di strutture dati concorrenti (ovvero una TrieMap
e una BlockingQueue
).
Inoltre, la classe PixelGrid
è stata resa un monitor. Tutto ciò permette di evitare corse critiche.
La soluzione proposta è stata implementata utilizzando Java Remote Method Invocation (RMI
), mediante il linguaggio Scala.
L’architettura prevede l’interazione tra ogni client e il ModelService, ma anche un collegamento peer-to-peer tra i partecipanti. Il ModelService è l’entità che si occupa di mantenere lo stato consistente della griglia, durante una sessione di disegno.
Pixel coloring
: un client, per colorare un pixel della griglia, invia un messaggio al ModelService, il quale si occuperà di propagare l’evento a tutti gli altri partecipanti. Questo meccanismo garantisce uno stato consistente tra tutti i client che concorrono alla modifica della griglia.
Brush change color & mouse move
: un client, per cambiare il colore del proprio pennello o per notificare il movimento del proprio mouse, non sfrutterà più il ModelService per fare broadcasting dei messaggi, bensì farà una chiamata remota tutti gli altri client.
Un client, per poter partecipare alla sessione, deve conoscere un rifertimento ad un altro utente già connesso al sistema. In questo modo, può richiedere a quest’ultuimo l’istanza del ModelService della sessione, che consentirà di ottenere lo stato della griglia e la lista degli utenti connessi. Inoltre, il ModelService notificherà a tutti gli altri client la presenza del nuovo utente.