NOVINKA - Online rekvalifikační kurz Java programátor. Oblíbená a studenty ověřená rekvalifikace - nyní i online.
NOVINKA – Víkendový online kurz Software tester, který tě posune dál. Zjisti, jak na to!

Lekce 12 - Java server - Propagace lokální sítí (2. část)

V minulé lekci, Java server - Propagace lokální sítí (1. část), jsme rozpracovali zviditelnění našeho chat serveru v Javě klientům v lokální síti.

Dnes budeme pokračovat a upravíme zbylé třídy, kterým jsme upravili jejich rozhraní.

Úprava stávajících tříd

Začneme úpravou třídy ParameterFactory. V první části jsme přidali do rozhraní bezparametrickou metodu getParameters(), kterou nyní 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 třídy jsme přidali jednu instanční proměnnou parameterProvider typu IParameterProvider. Tuto proměnnou budeme inicializovat v parametrické metodě getParameters(). Toto řešení není nejideálnější, zato je nejjednodušší. Jediné, co musíme dodržet, je, že se nejdříve zavolá parametrická verze metody getParameters(), a až poté můžeme volat její bezparametrickou verzi.

Další na pořadí je třída ConnectionManager, které jsme do rozhraní přidali dvě nové metody: getConnectedClientCount() a getMaxClients(). Implementace metod je následující:

@Override
public int getConnectedClientCount() {
    return clients.size();
}

@Override
public int getMaxClients() {
    return maxClients;
}

Počet připojených klientů získáme z velikosti kolekce clients. Maximální počet klientů je uložen v instanční konstantě maxClients.

Třetí třída, kterou je třeba upravit, je ServerThread, jejíž rozhraní nyní dědí z rozhraní ServerInfoProvider. Toto rozhraní vyžaduje implementaci metody getServerStatusMessage():

@Override
public IMessage getServerStatusMessage() {
    return null;
}

Tělo metody necháme zatím prázdné. Později se k této metodě vrátíme a doplníme jej.

Spuštění multicast senderu

U třídy ServerThread ještě chvilku zůstaneme, protože odtud budeme spouštět třídu MulticastSender. Do třídy přidáme instanční konstantu typu IMulticastSender, kterou budeme inicializovat v konstruktoru z parametru:

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 serveru nepřidáme přímo rozhraní IMulticastSender, ale pouze jeho továrnu. V konstruktoru inicializujeme multicastSender z továrny metodou getMulticastSender(), které předáme jako parametr this, což je instance ServerThread, která implementuje rozhraní ServerInfoProvider.

Spuštění a zastavení MulticastSenderu provedeme v metodě 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 jsme upravili konstruktor třídy ServerThread, jsme si rozbili továrnu pro instanciování této třídy. Pojďme to napravit. Třídě ServerThreadFactory přidáme novou instanční konstantu typu IMulticastSenderFactory, kterou budeme inicializovat v konstruktoru třídy z parametru:

private final IMulticastSenderFactory multicastSenderFactory;
@Inject
public ServerThreadFactory(IConnectionManagerFactory connectionManagerFactory,
    IMulticastSenderFactory multicastSenderFactory) {
    this.connectionManagerFactory = connectionManagerFactory;
    this.multicastSenderFactory = multicastSenderFactory;
}

V metodě getServerThread() pouze přidáme továrnu na správné místo jako parametr:

return new ServerThread(connectionManagerFactory.getConnectionManager(maxClients, waitingQueueSize),
    multicastSenderFactory, port);

ServerStatusMessage

Nyní vytvoříme zprávu, která bude obsahovat informace o stavu serveru. Zpráva bude obsahovat celkem 6 atributů:

  • serverID - id serveru
  • serverStatus - stav serveru (prázdný, plný, má místo)
  • clientCount - počet připojených klientů
  • maxClients - maximální počet klientů
  • serverName - název serveru
  • port - port, na kterém server naslouchá

Třídu nazvěme ServerStatusMessage a necháme ji implementovat rozhraní 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);
        }
    }
}

Rozhraní IMessage nám předepisuje implementovat metody getType() a getData(). Metoda getType() vrací konstantu MESSAGE_TYPE. Metoda getData() vrací objekt ServerStatusData, který obsahuje samotné informace o serveru. Třída ServerStatusData je přepravka, to znamená, že pouze zapouzdří informace pod jednu třídu. Třída musí obsahovat takové datové typy, které jsou serializovatelné, jinak by se vyvolala vyjímka při posílání objektu po síti.

Tvorba zprávy s informacemi o serveru

Nyní se vrátíme k metodě getServerStatusMessage() a doplníme ji o tělo:

@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 metodě získáme z ConnectionManageru informace o počtu aktuálně připojených klientů a maximální počet připojených klientů. Statickou konstantu ID musíme vytvořit. Umístěte ji do třídy ServerThread mezi definici ostatních instančních konstant:

private static final UUID ID = UUID.randomUUID();

Instanční konstantu name také musíme vytvořit. Bude se inicializovat v konstruktoru třídy z parametru:

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;
}

Instanční konstanta port již byla přítomna.

Opět jsme změnili konstruktor třídy ServerThread, takže musíme upravit i její továrnu:

@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árně do metody getServerThread() přidáme proměnnou serverName, kterou inicializujeme z parametrů. Tuto proměnnou přidáme na správné místo při vytváření nové instance.

Timto bychom byli hotovi s implementací propagace serveru v lokální síti.

V poslední části, Java server - Propagace lokální sítí (3. část), si vytvoříme na klientské straně třídu, která bude číst tyto informace. V budoucnu pak tuto třídu použijeme při implementaci chatu.


 

Předchozí článek
Java server - Propagace lokální sítí (1. část)
Všechny články v sekci
Server pro klientské aplikace v Javě
Přeskočit článek
(nedoporučujeme)
Java server - Propagace lokální sítí (3. část)
Článek pro vás napsal Petr Štechmüller
Avatar
Uživatelské hodnocení:
Ještě nikdo nehodnotil, buď první!
Autor se věnuje primárně programování v Javě, ale nebojí se ani webových technologií.
Aktivity