package com.jenjinstudios.server.net; import com.jenjinstudios.core.Connection; import java.io.IOException; import java.security.KeyPair; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; /** * The base Server class for implementation of the JGSA. It contains extensible execution functionality designed to be * used by Executable Messages from ClientHandlers. * * @author Caleb Brinkman */ @SuppressWarnings("SameParameterValue") public class Server extends Thread { /** The logger used by this class. */ public static final Logger LOGGER = Logger.getLogger(Server.class.getName()); /** The updates per second. */ protected final int UPS; /** The period of the update in milliseconds. */ protected final int PERIOD; /** The list of {@code ClientListener}s working for this server. */ private final ClientListener clientListener; /** The list of {@code ClientHandler}s working for this server. */ private final Map<Integer, ClientHandler> clientHandlers; private final KeyPair rsaKeyPair; /** * Construct a new Server without a SQLHandler. * * @param initInfo The initialization object to use when constructing the server. * * @throws java.io.IOException If there is an IO Error initializing the server. * @throws NoSuchMethodException If there is no appropriate constructor for the specified ClientHandler * constructor. */ @SuppressWarnings("unchecked") protected Server(ServerInit initInfo) throws IOException, NoSuchMethodException { super("Server"); LOGGER.log(Level.FINE, "Initializing Server."); UPS = initInfo.getUps(); PERIOD = 1000 / UPS; clientListener = new ClientListener(getClass(), initInfo.getHandlerClass(), initInfo.getPort()); clientHandlers = new TreeMap<>(); rsaKeyPair = initInfo.getKeyPair() == null ? Connection.generateRSAKeyPair() : initInfo.getKeyPair(); } /** * Add new clients that have connected to the client listeners. */ public void checkListenerForClients() { LinkedList<ClientHandler> nc = clientListener.getNewClients(); for (ClientHandler h : nc) { addClientHandler(h); h.start(); } } private void addClientHandler(ClientHandler h) { int nullIndex = 0; synchronized (clientHandlers) { while (clientHandlers.containsKey(nullIndex)) nullIndex++; clientHandlers.put(nullIndex, h); } h.setHandlerId(nullIndex); h.setRSAKeyPair(rsaKeyPair); } /** * Run all messages currently waiting in ClientHandler queues. */ public void runClientHandlerQueuedMessages() { synchronized (clientHandlers) { Collection<ClientHandler> handlers = clientHandlers.values(); handlers.stream(). filter(current -> current != null). forEach(c -> c.getExecutableMessageQueue().runQueuedExecutableMessages()); } } /** Broadcast all outgoing messages to clients. */ public void broadcast() { synchronized (clientHandlers) { clientHandlers.values().stream().forEach(c -> { if (c != null) { try { c.getMessageIO().writeAllMessages(); } catch (IOException e) { c.shutdown(); } } }); } } /** Update all clients before they sendAllMessages. */ public void update() { synchronized (clientHandlers) { Set<Integer> integers = clientHandlers.keySet(); for (int i : integers) { ClientHandler t = clientHandlers.get(i); if (t != null) { t.update(); } } } } /** Run the server. */ @Override public void run() { clientListener.startListening(this); } /** * Shutdown the server, forcing all client links to close. * * @throws IOException if there is an error shutting down a client. */ protected void shutdown() throws IOException { synchronized (clientHandlers) { Set<Integer> integers = clientHandlers.keySet(); for (int i : integers) { ClientHandler t = clientHandlers.get(i); if (t != null) { t.shutdown(); } } } clientListener.stopListening(); } /** * Schedule a client to be removed during the next update. * * @param handler The client handler to be removed. */ protected void removeClient(ClientHandler handler) { synchronized (clientHandlers) { clientHandlers.remove(handler.getHandlerId()); } } }