/* Copyright (c) 2011 Danish Maritime Authority. * * 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 dk.dma.ais.bus.tcp; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.Collections; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import dk.dma.ais.bus.AisBusComponent; /** * Base class for TCP servers. Spawns and handles TCP clients. */ public abstract class TcpServer extends Thread implements IClientStoppedListener { private static final Logger LOG = LoggerFactory.getLogger(TcpServer.class); protected final AtomicReference<ServerSocket> serverSocket = new AtomicReference<>(); protected TcpServerConf serverConf = new TcpServerConf(); protected TcpClientConf clientConf = new TcpClientConf(); private Semaphore semaphore; protected final Set<TcpClient> clients = Collections.newSetFromMap(new ConcurrentHashMap<TcpClient, Boolean>()); public TcpServer() { } /** * Inheriting classes must be able to provide a new client * * @param socket * @return */ protected abstract TcpClient newClient(Socket socket); /** * Clients notify them self when they are done * * @param client */ @Override public void clientStopped(TcpClient client) { clients.remove(client); semaphore.release(); } @Override public void run() { // Initialize semaphore semaphore = new Semaphore(serverConf.getMaxClients()); // Setup server socket try { serverSocket.set(new ServerSocket(serverConf.getPort())); } catch (IOException e) { LOG.error("Failed to setup server socket: " + e.getMessage()); return; } // Accept incoming connections while (true) { Socket socket = null; try { // Maybe wait if max connections is exceeded try { semaphore.acquire(); } catch (InterruptedException e) { break; } LOG.info("Waiting for connections on port " + serverConf.getPort()); socket = serverSocket.get().accept(); socket.setKeepAlive(true); LOG.info("Accepting connection from " + socket.getRemoteSocketAddress()); } catch (IOException e) { if (!isInterrupted()) { LOG.info(getName() + ": " + e.getMessage()); } if (socket != null) { try { socket.close(); } catch (IOException ignored) { } } semaphore.release(); continue; } // Register and start client TcpClient client = newClient(socket); clients.add(client); client.start(); } // Stop clients for (TcpClient client : clients) { client.cancel(); } LOG.info("Stopped"); } public void cancel() { this.interrupt(); if (serverSocket.get() != null) { try { serverSocket.get().close(); } catch (IOException ignored) { } } try { this.join(AisBusComponent.THREAD_STOP_WAIT_MAX); } catch (InterruptedException e) { e.printStackTrace(); } } public TcpClientConf getClientConf() { return clientConf; } public void setClientConf(TcpClientConf clientConf) { this.clientConf = clientConf; } public TcpServerConf getServerConf() { return serverConf; } public void setServerConf(TcpServerConf serverConf) { this.serverConf = serverConf; } public Set<TcpClient> getClients() { return clients; } }