sewy/src/main/java/me/bvn13/sewy/Server.java

113 lines
3.6 KiB
Java

/*
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 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.
* Create the instance of this class to connect to {@link Client}
*/
public class Server<T extends AbstractClientListener> {
protected final Logger log = LoggerFactory.getLogger(this.getClass());
protected final ExecutorService executor = Executors.newCachedThreadPool();
protected final List<T> clients = Collections.synchronizedList(new ArrayList<>());
protected ServerSocket socket;
protected Server() {
}
/**
* @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 Server(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 me.bvn13.sewy.Server#Server(java.lang.String, int, java.lang.Class)})
*/
@SuppressWarnings("unchecked")
public Server(String host, int port, Function<Socket, T> 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 T 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);
}
});
}
/**
* Stops server gracefully
* Disconnects from every client
*/
public void stop() {
log.debug("Stopping server");
final Iterator<T> iterator = clients.iterator();
while (iterator.hasNext()) {
final AbstractClientListener client = iterator.next();
client.stop();
iterator.remove();
}
executor.shutdown();
}
/**
* To check whether the server is ready for new connections
* @return
*/
public boolean isListening() {
return socket != null && socket.isBound();
}
}