IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

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 servera
  • serverStatus - stav servera (prázdny, plný, má miesto)
  • clientCount - počet pripojených klientov
  • maxClients - maximálny počet klientov
  • serverName - názov servera
  • port - 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.


 

Predchádzajúci článok
Java server - Propagácia lokálnou sieťou (1. časť)
Všetky články v sekcii
Server pre klientskej aplikácie v Jave
Preskočiť článok
(neodporúčame)
Java server - Propagácia lokálnou sieťou (3. časť)
Článok pre vás napísal Petr Štechmüller
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Autor se věnuje primárně programování v Javě, ale nebojí se ani webových technologií.
Aktivity