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; 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: import org.sonews.util.Stats; 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: chris@1: private static final Connections instance = new Connections(); chris@1: chris@1: /** chris@1: * @return Active Connections instance. chris@1: */ chris@1: public static Connections getInstance() chris@1: { chris@1: return Connections.instance; chris@1: } chris@1: chris@1: private final List connections chris@1: = new ArrayList(); chris@1: private final Map connByChannel chris@1: = new HashMap(); chris@1: chris@1: private Connections() chris@1: { chris@1: setName("Connections"); chris@1: } chris@1: chris@1: /** chris@1: * Adds the given NNTPConnection to the Connections management. chris@1: * @param conn chris@1: * @see org.sonews.daemon.NNTPConnection chris@1: */ chris@1: public void add(final NNTPConnection conn) chris@1: { chris@1: synchronized(this.connections) chris@1: { chris@1: this.connections.add(conn); chris@3: this.connByChannel.put(conn.getSocketChannel(), conn); chris@1: } chris@1: } chris@1: chris@1: /** chris@1: * @param channel chris@1: * @return NNTPConnection instance that is associated with the given chris@1: * SocketChannel. chris@1: */ chris@1: public NNTPConnection get(final SocketChannel channel) chris@1: { chris@1: synchronized(this.connections) chris@1: { chris@1: return this.connByChannel.get(channel); chris@1: } chris@1: } chris@1: chris@1: int getConnectionCount(String remote) chris@1: { chris@1: int cnt = 0; chris@1: synchronized(this.connections) chris@1: { chris@1: for(NNTPConnection conn : this.connections) chris@1: { chris@1: assert conn != null; chris@3: assert conn.getSocketChannel() != null; chris@1: chris@3: Socket socket = conn.getSocketChannel().socket(); chris@1: if(socket != null) chris@1: { chris@1: InetSocketAddress sockAddr = (InetSocketAddress)socket.getRemoteSocketAddress(); chris@1: if(sockAddr != null) chris@1: { chris@1: if(sockAddr.getHostName().equals(remote)) chris@1: { chris@1: cnt++; chris@1: } chris@1: } chris@1: } // if(socket != null) chris@1: } chris@1: } chris@1: return cnt; chris@1: } chris@1: chris@1: /** chris@1: * Run loops. Checks periodically for timed out connections and purged them chris@1: * from the lists. chris@1: */ chris@1: @Override chris@1: public void run() chris@1: { chris@1: while(isRunning()) chris@1: { chris@3: int timeoutMillis = 1000 * Config.inst().get(Config.TIMEOUT, 180); chris@1: chris@1: synchronized (this.connections) chris@1: { chris@1: final ListIterator iter = this.connections.listIterator(); chris@1: NNTPConnection conn; chris@1: chris@1: while (iter.hasNext()) chris@1: { chris@1: conn = iter.next(); cli@25: if((System.currentTimeMillis() - conn.getLastActivity()) > timeoutMillis cli@25: && conn.getBuffers().isOutputBufferEmpty()) chris@1: { chris@1: // A connection timeout has occurred so purge the connection chris@1: iter.remove(); chris@1: chris@1: // Close and remove the channel chris@3: SocketChannel channel = conn.getSocketChannel(); chris@1: connByChannel.remove(channel); chris@1: chris@1: try chris@1: { chris@1: // Close the channel; implicitely cancels all selectionkeys chris@1: channel.close(); cli@15: Log.get().info("Disconnected: " + channel.socket().getRemoteSocketAddress() + cli@15: " (timeout)"); chris@1: } chris@1: catch(IOException ex) chris@1: { cli@15: Log.get().warning("Connections.run(): " + ex); chris@1: } chris@1: chris@1: // Recycle the used buffers chris@1: conn.getBuffers().recycleBuffers(); chris@1: chris@1: Stats.getInstance().clientDisconnect(); chris@1: } chris@1: } chris@1: } chris@1: chris@1: try chris@1: { chris@1: Thread.sleep(10000); // Sleep ten seconds chris@1: } chris@1: catch(InterruptedException ex) chris@1: { cli@15: Log.get().warning("Connections Thread was interrupted: " + ex.getMessage()); chris@1: } chris@1: } chris@1: } chris@1: chris@1: }