From 8fbc693a998a994e6e1e4cb5823ab8a1a699b104 Mon Sep 17 00:00:00 2001 From: bvn13 Date: Sat, 29 Jan 2022 02:42:11 +0300 Subject: [PATCH] implemented CommandServer --- README.md | 2 +- build.gradle | 2 +- .../java/me/bvn13/sewy/CommandServer.java | 100 ++++++++++++++++++ src/main/java/me/bvn13/sewy/Server.java | 23 ++-- src/test/java/me/bvn13/sewy/ServerTest.java | 6 +- 5 files changed, 114 insertions(+), 19 deletions(-) create mode 100644 src/main/java/me/bvn13/sewy/CommandServer.java diff --git a/README.md b/README.md index e768436..61570f8 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Sewy.register(PongCommand.class); 3. Start server with `CommandClientListener` implementing response creation logic ```java -Server server = new Server("localhost", port, (socket) -> new CommandClientListener(socket) { +CommandServer server = new CommandServer("localhost", port, (socket) -> new CommandClientListener(socket) { @Override public AbstractCommand onCommand(AbstractCommand command) { if (command instanceof PingCommand) { diff --git a/build.gradle b/build.gradle index e6cac96..0cbc48b 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { } group 'me.bvn13' -version '1.0' +version '1.1' repositories { mavenCentral() diff --git a/src/main/java/me/bvn13/sewy/CommandServer.java b/src/main/java/me/bvn13/sewy/CommandServer.java new file mode 100644 index 0000000..ac7cad8 --- /dev/null +++ b/src/main/java/me/bvn13/sewy/CommandServer.java @@ -0,0 +1,100 @@ +/* + Copyright 2022 Vyacheslav Boyko + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +package me.bvn13.sewy; + +import me.bvn13.sewy.command.AbstractCommand; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Function; + +import static java.lang.String.format; +import static me.bvn13.sewy.ClientListenerFactory.createClientListenerConstructor; + +/** + * TCP Server. + * Works with command protocol. + * Create the instance of this class to connect to {@link Client} + */ +public class CommandServer extends Server { + + /** + * @param host host to bind in order to start listen to clients + * @param port port to start listen to + */ + public CommandServer(String host, int port) { + this(host, port, CommandClientListener.class); + } + + /** + * @param host host to bind in order to start listen to clients + * @param port port to start listen to + * @param clientListenerClass client listen class to be used for communication + */ + public CommandServer(String host, int port, Class clientListenerClass) { + this(host, port, createClientListenerConstructor(clientListenerClass)); + } + + /** + * + * @param host host to bind in order to start listen to clients + * @param port port to start listen to + * @param clientListenerConstructor to provide constructor for client listener (see {@link CommandServer#CommandServer(String, int, Class)}) + */ + @SuppressWarnings("unchecked") + public CommandServer(String host, int port, Function clientListenerConstructor) { + log.debug("Starting server"); + executor.execute(() -> { + try (final ServerSocket server = new ServerSocket(port, 0, InetAddress.getByName(host))) { + + socket = server; + + while (!server.isClosed()) { + final Socket client = server.accept(); + final CommandClientListener clientListener = clientListenerConstructor.apply(client); + executor.execute(clientListener); + clients.add(clientListener); + } + + } catch (IOException e) { + log.error(format("Error while conversation with %s:%d", host, port), e); + } + }); + } + + /** + * Sends command to every client + * @param command command to be sent + * @param generic type + */ + public void send(T command) { + log.debug("Start to send command: " + command); + for (CommandClientListener client : clients) { + client.send(command); + } + } + +} diff --git a/src/main/java/me/bvn13/sewy/Server.java b/src/main/java/me/bvn13/sewy/Server.java index c4a50bb..943f3e6 100644 --- a/src/main/java/me/bvn13/sewy/Server.java +++ b/src/main/java/me/bvn13/sewy/Server.java @@ -37,21 +37,16 @@ import static me.bvn13.sewy.ClientListenerFactory.createClientListenerConstructo * TCP Server. * Create the instance of this class to connect to {@link Client} */ -public class Server { +public class Server { - private final Logger log = LoggerFactory.getLogger(this.getClass()); + protected final Logger log = LoggerFactory.getLogger(this.getClass()); - private final ExecutorService executor = Executors.newCachedThreadPool(); - private final List clients = Collections.synchronizedList(new ArrayList<>()); + protected final ExecutorService executor = Executors.newCachedThreadPool(); + protected final List clients = Collections.synchronizedList(new ArrayList<>()); - private ServerSocket socket; + protected ServerSocket socket; - /** - * @param host host to bind in order to start listen to clients - * @param port port to start listen to - */ - public Server(String host, int port) { - this(host, port, CommandClientListener.class); + protected Server() { } /** @@ -70,7 +65,7 @@ public class Server { * @param clientListenerConstructor to provide constructor for client listener (see {@link me.bvn13.sewy.Server#Server(java.lang.String, int, java.lang.Class)}) */ @SuppressWarnings("unchecked") - public Server(String host, int port, Function clientListenerConstructor) { + public Server(String host, int port, Function clientListenerConstructor) { log.debug("Starting server"); executor.execute(() -> { try (final ServerSocket server = new ServerSocket(port, 0, InetAddress.getByName(host))) { @@ -79,7 +74,7 @@ public class Server { while (!server.isClosed()) { final Socket client = server.accept(); - final AbstractClientListener clientListener = clientListenerConstructor.apply(client); + final T clientListener = clientListenerConstructor.apply(client); executor.execute(clientListener); clients.add(clientListener); } @@ -97,7 +92,7 @@ public class Server { */ public void stop() { log.debug("Stopping server"); - final Iterator iterator = clients.iterator(); + final Iterator iterator = clients.iterator(); while (iterator.hasNext()) { final AbstractClientListener client = iterator.next(); client.stop(); diff --git a/src/test/java/me/bvn13/sewy/ServerTest.java b/src/test/java/me/bvn13/sewy/ServerTest.java index 9354af1..6443532 100644 --- a/src/test/java/me/bvn13/sewy/ServerTest.java +++ b/src/test/java/me/bvn13/sewy/ServerTest.java @@ -31,7 +31,7 @@ public class ServerTest { @ParameterizedTest @ValueSource(ints = START_PORT + 1) void testServerStarts(int port) throws InterruptedException { - Server server = new Server("localhost", port); + Server server = new Server("localhost", port, SimpleClientListener.class); Thread.sleep(1000); Assertions.assertTrue(server.isListening()); server.stop(); @@ -40,7 +40,7 @@ public class ServerTest { @ParameterizedTest @ValueSource(ints = START_PORT + 2) void givenServerRunning_whenClientConnects_thenServerCanStopClientListener(int port) throws InterruptedException { - Server server = new Server("localhost", port); + Server server = new Server("localhost", port, SimpleClientListener.class); Client client = new Client<>("localhost", port, SimpleClientListener.class); Thread.sleep(1000); Assertions.assertTrue(server.isListening()); @@ -85,7 +85,7 @@ public class ServerTest { Sewy.register(PingCommand.class); Sewy.register(PongCommand.class); - Server server = new Server("localhost", port, (socket) -> new CommandClientListener(socket) { + CommandServer server = new CommandServer("localhost", port, (socket) -> new CommandClientListener(socket) { @Override public AbstractCommand onCommand(AbstractCommand command) { if (command instanceof PingCommand) {