12. diel - Java server - Propagácia lokálnou sieťou (2. časť)
V minulej lekcii, Java server - Propagácia lokálnou sieťou (1. časť) , sme rozpracovali zviditeľnenie nášho chat servera v Jave klientom v lokálnej sieti. Dnes budeme pokračovať a upravíme zvyšné triedy, ktorým sme upravili ich rozhrania.
Úprava existujúcich tried
Začneme úpravou triedy ParameterFactory
. V prvej časti sme
pridali do rozhrania bezparametrickou metódu getParameters()
,
ktorú teraz implementujeme:
@Singleton public class ParameterFactory implements IParameterFactory { private IParameterProvider parameterProvider; @Override public IParameterProvider getParameters() { if (parameterProvider == null) { throw new IllegalStateException("Je potřeba nejdříve inicializovat parametry."); } return parameterProvider; } @Override public IParameterProvider getParameters(String[] args) { if (parameterProvider == null) { parameterProvider = new CmdParser(args); } return parameterProvider; } }
Do triedy sme pridali jednu inštančný premennú
parameterProvider
typu IParameterProvider
. Túto
premennú budeme inicializovať v parametrické metóde
getParameters()
. Toto riešenie nie je najideálnejšie, zato je
najjednoduchšie. Jediné, čo musíme dodržať, je, že sa najskôr zavolá
parametrická verzia metódy getParameters()
, a až potom môžeme
volať jej bezparametrickou verzii.
Ďalšia na poradie je trieda ConnectionManager
, ktoré sme do
rozhrania pridali dve nové metódy: getConnectedClientCount()
a
getMaxClients()
. Implementácia metód je nasledovné:
@Override public int getConnectedClientCount() { return clients.size(); } @Override public int getMaxClients() { return maxClients; }
Počet pripojených klientov získame z veľkosti kolekcie
clients
. Maximálny počet klientov je uložený v inštančný
konštante maxClients
.
Tretia trieda, ktorú treba upraviť, je ServerThread
, ktorej
rozhranie teraz dedí z rozhrania ServerInfoProvider
. Toto
rozhranie vyžaduje implementáciu metódy
getServerStatusMessage()
:
@Override public IMessage getServerStatusMessage() { return null; }
Telo metódy necháme zatiaľ prázdne. Neskôr sa k tejto metóde vrátime a doplníme ho.
Spustenie multicast sender
U triedy ServerThread
ešte chvíľku zostaneme, pretože
odtiaľ budeme spúšťať triedu MulticastSender
. Do triedy
pridáme inštančný konštantu typu IMulticastSender
, ktorú
budeme inicializovať v konštruktory z parametra:
private final IMulticastSender multicastSender; @Inject ServerThread(IConnectionManager connectionManager, IMulticastSenderFactory multicastSenderFactory, int port) { super("ServerThread"); this.connectionManager = connectionManager; this.multicastSender = multicastSenderFactory.getMulticastSender(this); this.port = port; }
Do konstruktoru servera nepridáme priamo rozhranie
IMulticastSender
, ale iba jeho továreň. V konstruktoru
inicializujeme multicastSender
z továrne metódou
getMulticastSender()
, ktoré odovzdáme ako parameter
this
, čo je inštancia ServerThread
, ktorá
implementuje rozhranie ServerInfoProvider
.
Spustenie a zastavenie MulticastSender
u vykonáme v metóde
run()
:
@Override public void run() { // Začátek metody multicastSender.start(); // Spuštění multicast senderu connectionManager.onServerStart(); // ... // konec metody multicastSender.shutdown(); connectionManager.onServerStop(); }
Tým, že sme upravili konštruktor triedy ServerThread
, sme si
rozbili továreň pre instanciování tejto triedy. Poďme to napraviť. Triede
ServerThreadFactory
pridáme novú inštančný konštantu typu
IMulticastSenderFactory
, ktorú budeme inicializovať v
konstruktoru triedy z parametra:
private final IMulticastSenderFactory multicastSenderFactory; @Inject public ServerThreadFactory(IConnectionManagerFactory connectionManagerFactory, IMulticastSenderFactory multicastSenderFactory) { this.connectionManagerFactory = connectionManagerFactory; this.multicastSenderFactory = multicastSenderFactory; }
V metóde getServerThread()
len pridáme továreň na správne
miesto ako parameter:
return new ServerThread(connectionManagerFactory.getConnectionManager(maxClients, waitingQueueSize), multicastSenderFactory, port);
ServerStatusMessage
Teraz vytvoríme správu, ktorá bude obsahovať informácie o stave servera. Správa bude obsahovať celkom 6 atribútov:
serverID
- id serveraserverStatus
- stav servera (prázdny, plný, má miesto)clientCount
- počet pripojených klientovmaxClients
- maximálny počet klientovserverName
- názov serveraport
- port, na ktorom server počúva
Triedu nazvime ServerStatusMessage
a necháme ju implementovať
rozhranie IMessage
:
public class ServerStatusMessage implements IMessage { private static final long serialVersionUID = -1429760060957272567L; public static final String MESSAGE_TYPE = "server-status"; private final ServerStatusData statusData; public ServerStatusMessage(ServerStatusData statusData) { this.statusData = statusData; } @Override public String getType() {return MESSAGE_TYPE;} @Override public Object getData() {return statusData;} @Override public String toString() {return String.valueOf(getData());} public static final class ServerStatusData implements Serializable { private static final long serialVersionUID = -4288671744361722044L; public enum ServerStatus {EMPTY, HAVE_SPACE, FULL} public final UUID serverID; public final ServerStatus serverStatus; public final int clientCount; public final int maxClients; public final String serverName; public final int port; public ServerStatusData(UUID serverID, int clientCount, int maxClients, String serverName, int port) { this.serverID = serverID; this.serverStatus = serverStatus; this.clientCount = clientCount; this.maxClients = maxClients; this.serverName = serverName; this.port = port; final int delta = maxClients - clientCount; ServerStatus status = ServerStatus.EMPTY; if (delta == 0) { status = ServerStatus.FULL; } else if (delta > 0 && delta < maxClients) { status = ServerStatus.HAVE_SPACE; } this.serverStatus = status; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ServerStatusData that = (ServerStatusData) o; return clientCount == that.clientCount && maxClients == that.maxClients && port == that.port && Objects.equals(serverID, that.serverID) && serverStatus == that.serverStatus && Objects.equals(serverName, that.serverName); } @Override public int hashCode() { return Objects.hash(serverID, serverStatus, clientCount, maxClients, serverName, port); } @Override public String toString() { return String.format("%s: %d/%d - %s; port=%d", serverName, clientCount, maxClients, serverStatus, port); } } }
Rozhranie IMessage
nám predpisuje implementovať metódy
getType()
a getData()
. Metóda getType()
vracia konštantu MESSAGE_TYPE
. Metóda getData()
vracia objekt ServerStatusData
, ktorý obsahuje samotné
informácie o serveri. Trieda ServerStatusData
je prepravka, to
znamená, že iba zapuzdrí informácie pod jednu triedu. Trieda musí
obsahovať také dátové typy, ktoré sú serializovatelný, inak by
sa vyvolala výnimka pri posielaní objektu po sieti.
Tvorba správy s informáciami o serveri
Teraz sa vrátime k metóde getServerStatusMessage()
a doplníme
ju o telo:
@Override public IMessage getServerStatusMessage() { final int connectedClients = connectionManager.getConnectedClientCount(); final int maxClients = connectionManager.getMaxClients(); return new ServerStatusMessage(new ServerStatusData( ID, connectedClients, maxClients, serverName, port)); }
V metóde získame z ConnectionManager
u informácie o počte
aktuálne pripojených klientov a maximálny počet pripojených klientov.
Statickú konštantu ID
musíme vytvoriť. Umiestnite ju do triedy
ServerThread
medzi definíciu ostatných inštančných
konštánt:
private static final UUID ID = UUID.randomUUID();
Inštančný konštantu name
tiež musíme vytvoriť. Bude sa
inicializovať v konstruktoru triedy z parametra:
private final String serverName; @Inject ServerThread(IConnectionManager connectionManager, IMulticastSenderFactory multicastSenderFactory, String serverName, int port) { super("ServerThread"); this.connectionManager = connectionManager; this.multicastSender = multicastSenderFactory.getMulticastSender(this); this.serverName = serverName; this.port = port; }
Inštančný konštanta port
už bola prítomná.
Opäť sme zmenili konštruktor triedy ServerThread
, takže
musíme upraviť aj jej továreň:
@Override public IServerThread getServerThread(IParameterProvider parameters) { final String serverName = parameters.getString(CmdParser.SERVER_NAME, DEFAULT_SERVER_NAME); final int port = parameters.getInteger(CmdParser.PORT, DEFAULT_SERVER_PORT); final int maxClients = parameters.getInteger(CmdParser.CLIENTS, DEFAULT_MAX_CLIENTS); final int waitingQueueSize = parameters.getInteger(CmdParser.MAX_WAITING_QUEUE, DEFAULT_WAITING_QUEUE_SIZE); return new ServerThread(connectionManagerFactory.getConnectionManager(maxClients, waitingQueueSize), multicastSenderFactory, serverName, port); }
V továrni do metódy getServerThread()
pridáme premennú
serverName
, ktorú inicializujeme z parametrov. Túto premennú
pridáme na správne miesto pri vytváraní novej inštancie.
Tymto by sme boli hotoví s implementáciou propagácie servera v lokálnej sieti. V poslednej časti, Java server - Propagácia lokálnou sieťou (3. časť) , si vytvoríme na klientskej strane triedu, ktorá bude čítať tieto informácie. V budúcnosti potom túto triedu použijeme pri implementácii chatu.