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: }