chris@1: /* chris@1: * SONEWS News Server chris@1: * see AUTHORS for the list of contributors chris@1: * chris@1: * This program is free software: you can redistribute it and/or modify chris@1: * it under the terms of the GNU General Public License as published by chris@1: * the Free Software Foundation, either version 3 of the License, or chris@1: * (at your option) any later version. chris@1: * chris@1: * This program is distributed in the hope that it will be useful, chris@1: * but WITHOUT ANY WARRANTY; without even the implied warranty of chris@1: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the chris@1: * GNU General Public License for more details. chris@1: * chris@1: * You should have received a copy of the GNU General Public License chris@1: * along with this program. If not, see . chris@1: */ chris@1: chris@1: package org.sonews.daemon; chris@1: chris@3: import org.sonews.config.Config; chris@1: import org.sonews.util.Log; cli@28: import org.sonews.util.Stats; chris@1: import java.io.IOException; chris@1: import java.net.InetSocketAddress; chris@1: import java.net.Socket; chris@1: import java.nio.channels.SocketChannel; chris@1: import java.util.ArrayList; chris@1: import java.util.HashMap; chris@1: import java.util.List; chris@1: import java.util.ListIterator; chris@1: import java.util.Map; chris@1: chris@1: /** chris@1: * Daemon thread collecting all NNTPConnection instances. The thread chris@1: * checks periodically if there are stale/timed out connections and chris@1: * removes and purges them properly. chris@1: * @author Christian Lins chris@1: * @since sonews/0.5.0 chris@1: */ chris@3: public final class Connections extends AbstractDaemon chris@1: { chris@1: cli@37: private static final Connections instance = new Connections(); chris@1: cli@37: /** cli@37: * @return Active Connections instance. cli@37: */ cli@37: public static Connections getInstance() cli@37: { cli@37: return Connections.instance; cli@37: } cli@37: private final List connections = new ArrayList(); cli@37: private final Map connByChannel = new HashMap(); chris@1: cli@37: private Connections() cli@37: { cli@37: setName("Connections"); cli@37: } chris@1: cli@37: /** cli@37: * Adds the given NNTPConnection to the Connections management. cli@37: * @param conn cli@37: * @see org.sonews.daemon.NNTPConnection cli@37: */ cli@37: public void add(final NNTPConnection conn) cli@37: { cli@37: synchronized (this.connections) { cli@37: this.connections.add(conn); cli@37: this.connByChannel.put(conn.getSocketChannel(), conn); cli@37: } cli@37: } chris@1: cli@37: /** cli@37: * @param channel cli@37: * @return NNTPConnection instance that is associated with the given cli@37: * SocketChannel. cli@37: */ cli@37: public NNTPConnection get(final SocketChannel channel) cli@37: { cli@37: synchronized (this.connections) { cli@37: return this.connByChannel.get(channel); cli@37: } cli@37: } chris@1: cli@37: int getConnectionCount(String remote) cli@37: { cli@37: int cnt = 0; cli@37: synchronized (this.connections) { cli@37: for (NNTPConnection conn : this.connections) { cli@37: assert conn != null; cli@37: assert conn.getSocketChannel() != null; chris@1: cli@37: Socket socket = conn.getSocketChannel().socket(); cli@37: if (socket != null) { cli@37: InetSocketAddress sockAddr = (InetSocketAddress) socket.getRemoteSocketAddress(); cli@37: if (sockAddr != null) { cli@37: if (sockAddr.getHostName().equals(remote)) { cli@37: cnt++; cli@37: } cli@37: } cli@37: } // if(socket != null) cli@37: } cli@37: } cli@37: return cnt; cli@37: } cli@37: cli@37: /** cli@37: * Run loops. Checks periodically for timed out connections and purged them cli@37: * from the lists. cli@37: */ cli@37: @Override cli@37: public void run() cli@37: { cli@37: while (isRunning()) { cli@37: int timeoutMillis = 1000 * Config.inst().get(Config.TIMEOUT, 180); cli@37: cli@37: synchronized (this.connections) { cli@37: final ListIterator iter = this.connections.listIterator(); cli@37: NNTPConnection conn; cli@37: cli@37: while (iter.hasNext()) { cli@37: conn = iter.next(); cli@37: if ((System.currentTimeMillis() - conn.getLastActivity()) > timeoutMillis cli@37: && conn.getBuffers().isOutputBufferEmpty()) { cli@37: // A connection timeout has occurred so purge the connection cli@37: iter.remove(); cli@37: cli@37: // Close and remove the channel cli@37: SocketChannel channel = conn.getSocketChannel(); cli@37: connByChannel.remove(channel); cli@37: cli@37: try { cli@37: assert channel != null; cli@37: assert channel.socket() != null; cli@37: cli@37: // Close the channel; implicitely cancels all selectionkeys cli@37: channel.close(); cli@37: Log.get().info("Disconnected: " + channel.socket().getRemoteSocketAddress() cli@37: + " (timeout)"); cli@37: } catch (IOException ex) { cli@37: Log.get().warning("Connections.run(): " + ex); cli@37: } cli@37: cli@37: // Recycle the used buffers cli@37: conn.getBuffers().recycleBuffers(); cli@37: cli@37: Stats.getInstance().clientDisconnect(); cli@37: } cli@37: } cli@37: } cli@37: cli@37: try { cli@37: Thread.sleep(10000); // Sleep ten seconds cli@37: } catch (InterruptedException ex) { cli@37: Log.get().warning("Connections Thread was interrupted: " + ex.getMessage()); cli@37: } cli@37: } cli@37: } chris@1: }