implemented CommandServer

This commit is contained in:
bvn13 2022-01-29 02:42:11 +03:00
parent e0fe404832
commit 8fbc693a99
5 changed files with 114 additions and 19 deletions

View File

@ -51,7 +51,7 @@ Sewy.register(PongCommand.class);
3. Start server with `CommandClientListener` implementing response creation logic 3. Start server with `CommandClientListener` implementing response creation logic
```java ```java
Server server = new Server("localhost", port, (socket) -> new CommandClientListener(socket) { CommandServer server = new CommandServer("localhost", port, (socket) -> new CommandClientListener(socket) {
@Override @Override
public AbstractCommand onCommand(AbstractCommand command) { public AbstractCommand onCommand(AbstractCommand command) {
if (command instanceof PingCommand) { if (command instanceof PingCommand) {

View File

@ -5,7 +5,7 @@ plugins {
} }
group 'me.bvn13' group 'me.bvn13'
version '1.0' version '1.1'
repositories { repositories {
mavenCentral() mavenCentral()

View File

@ -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<CommandClientListener> {
/**
* @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<Socket, CommandClientListener> 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 <T> generic type
*/
public <T extends AbstractCommand> void send(T command) {
log.debug("Start to send command: " + command);
for (CommandClientListener client : clients) {
client.send(command);
}
}
}

View File

@ -37,21 +37,16 @@ import static me.bvn13.sewy.ClientListenerFactory.createClientListenerConstructo
* TCP Server. * TCP Server.
* Create the instance of this class to connect to {@link Client} * Create the instance of this class to connect to {@link Client}
*/ */
public class Server { public class Server<T extends AbstractClientListener> {
private final Logger log = LoggerFactory.getLogger(this.getClass()); protected final Logger log = LoggerFactory.getLogger(this.getClass());
private final ExecutorService executor = Executors.newCachedThreadPool(); protected final ExecutorService executor = Executors.newCachedThreadPool();
private final List<AbstractClientListener> clients = Collections.synchronizedList(new ArrayList<>()); protected final List<T> clients = Collections.synchronizedList(new ArrayList<>());
private ServerSocket socket; protected ServerSocket socket;
/** protected Server() {
* @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);
} }
/** /**
@ -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)}) * @param clientListenerConstructor to provide constructor for client listener (see {@link me.bvn13.sewy.Server#Server(java.lang.String, int, java.lang.Class)})
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public Server(String host, int port, Function<Socket, CommandClientListener> clientListenerConstructor) { public Server(String host, int port, Function<Socket, T> clientListenerConstructor) {
log.debug("Starting server"); log.debug("Starting server");
executor.execute(() -> { executor.execute(() -> {
try (final ServerSocket server = new ServerSocket(port, 0, InetAddress.getByName(host))) { try (final ServerSocket server = new ServerSocket(port, 0, InetAddress.getByName(host))) {
@ -79,7 +74,7 @@ public class Server {
while (!server.isClosed()) { while (!server.isClosed()) {
final Socket client = server.accept(); final Socket client = server.accept();
final AbstractClientListener clientListener = clientListenerConstructor.apply(client); final T clientListener = clientListenerConstructor.apply(client);
executor.execute(clientListener); executor.execute(clientListener);
clients.add(clientListener); clients.add(clientListener);
} }
@ -97,7 +92,7 @@ public class Server {
*/ */
public void stop() { public void stop() {
log.debug("Stopping server"); log.debug("Stopping server");
final Iterator<AbstractClientListener> iterator = clients.iterator(); final Iterator<T> iterator = clients.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
final AbstractClientListener client = iterator.next(); final AbstractClientListener client = iterator.next();
client.stop(); client.stop();

View File

@ -31,7 +31,7 @@ public class ServerTest {
@ParameterizedTest @ParameterizedTest
@ValueSource(ints = START_PORT + 1) @ValueSource(ints = START_PORT + 1)
void testServerStarts(int port) throws InterruptedException { void testServerStarts(int port) throws InterruptedException {
Server server = new Server("localhost", port); Server server = new Server("localhost", port, SimpleClientListener.class);
Thread.sleep(1000); Thread.sleep(1000);
Assertions.assertTrue(server.isListening()); Assertions.assertTrue(server.isListening());
server.stop(); server.stop();
@ -40,7 +40,7 @@ public class ServerTest {
@ParameterizedTest @ParameterizedTest
@ValueSource(ints = START_PORT + 2) @ValueSource(ints = START_PORT + 2)
void givenServerRunning_whenClientConnects_thenServerCanStopClientListener(int port) throws InterruptedException { void givenServerRunning_whenClientConnects_thenServerCanStopClientListener(int port) throws InterruptedException {
Server server = new Server("localhost", port); Server server = new Server("localhost", port, SimpleClientListener.class);
Client<SimpleClientListener> client = new Client<>("localhost", port, SimpleClientListener.class); Client<SimpleClientListener> client = new Client<>("localhost", port, SimpleClientListener.class);
Thread.sleep(1000); Thread.sleep(1000);
Assertions.assertTrue(server.isListening()); Assertions.assertTrue(server.isListening());
@ -85,7 +85,7 @@ public class ServerTest {
Sewy.register(PingCommand.class); Sewy.register(PingCommand.class);
Sewy.register(PongCommand.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 @Override
public AbstractCommand onCommand(AbstractCommand command) { public AbstractCommand onCommand(AbstractCommand command) {
if (command instanceof PingCommand) { if (command instanceof PingCommand) {