1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/src/org/sonews/daemon/Connections.java Sun Aug 29 17:28:58 2010 +0200
1.3 @@ -0,0 +1,181 @@
1.4 +/*
1.5 + * SONEWS News Server
1.6 + * see AUTHORS for the list of contributors
1.7 + *
1.8 + * This program is free software: you can redistribute it and/or modify
1.9 + * it under the terms of the GNU General Public License as published by
1.10 + * the Free Software Foundation, either version 3 of the License, or
1.11 + * (at your option) any later version.
1.12 + *
1.13 + * This program is distributed in the hope that it will be useful,
1.14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.16 + * GNU General Public License for more details.
1.17 + *
1.18 + * You should have received a copy of the GNU General Public License
1.19 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
1.20 + */
1.21 +
1.22 +package org.sonews.daemon;
1.23 +
1.24 +import org.sonews.config.Config;
1.25 +import org.sonews.util.Log;
1.26 +import org.sonews.util.Stats;
1.27 +import java.io.IOException;
1.28 +import java.net.InetSocketAddress;
1.29 +import java.net.Socket;
1.30 +import java.nio.channels.SocketChannel;
1.31 +import java.util.ArrayList;
1.32 +import java.util.HashMap;
1.33 +import java.util.List;
1.34 +import java.util.ListIterator;
1.35 +import java.util.Map;
1.36 +
1.37 +/**
1.38 + * Daemon thread collecting all NNTPConnection instances. The thread
1.39 + * checks periodically if there are stale/timed out connections and
1.40 + * removes and purges them properly.
1.41 + * @author Christian Lins
1.42 + * @since sonews/0.5.0
1.43 + */
1.44 +public final class Connections extends AbstractDaemon
1.45 +{
1.46 +
1.47 + private static final Connections instance = new Connections();
1.48 +
1.49 + /**
1.50 + * @return Active Connections instance.
1.51 + */
1.52 + public static Connections getInstance()
1.53 + {
1.54 + return Connections.instance;
1.55 + }
1.56 +
1.57 + private final List<NNTPConnection> connections
1.58 + = new ArrayList<NNTPConnection>();
1.59 + private final Map<SocketChannel, NNTPConnection> connByChannel
1.60 + = new HashMap<SocketChannel, NNTPConnection>();
1.61 +
1.62 + private Connections()
1.63 + {
1.64 + setName("Connections");
1.65 + }
1.66 +
1.67 + /**
1.68 + * Adds the given NNTPConnection to the Connections management.
1.69 + * @param conn
1.70 + * @see org.sonews.daemon.NNTPConnection
1.71 + */
1.72 + public void add(final NNTPConnection conn)
1.73 + {
1.74 + synchronized(this.connections)
1.75 + {
1.76 + this.connections.add(conn);
1.77 + this.connByChannel.put(conn.getSocketChannel(), conn);
1.78 + }
1.79 + }
1.80 +
1.81 + /**
1.82 + * @param channel
1.83 + * @return NNTPConnection instance that is associated with the given
1.84 + * SocketChannel.
1.85 + */
1.86 + public NNTPConnection get(final SocketChannel channel)
1.87 + {
1.88 + synchronized(this.connections)
1.89 + {
1.90 + return this.connByChannel.get(channel);
1.91 + }
1.92 + }
1.93 +
1.94 + int getConnectionCount(String remote)
1.95 + {
1.96 + int cnt = 0;
1.97 + synchronized(this.connections)
1.98 + {
1.99 + for(NNTPConnection conn : this.connections)
1.100 + {
1.101 + assert conn != null;
1.102 + assert conn.getSocketChannel() != null;
1.103 +
1.104 + Socket socket = conn.getSocketChannel().socket();
1.105 + if(socket != null)
1.106 + {
1.107 + InetSocketAddress sockAddr = (InetSocketAddress)socket.getRemoteSocketAddress();
1.108 + if(sockAddr != null)
1.109 + {
1.110 + if(sockAddr.getHostName().equals(remote))
1.111 + {
1.112 + cnt++;
1.113 + }
1.114 + }
1.115 + } // if(socket != null)
1.116 + }
1.117 + }
1.118 + return cnt;
1.119 + }
1.120 +
1.121 + /**
1.122 + * Run loops. Checks periodically for timed out connections and purged them
1.123 + * from the lists.
1.124 + */
1.125 + @Override
1.126 + public void run()
1.127 + {
1.128 + while(isRunning())
1.129 + {
1.130 + int timeoutMillis = 1000 * Config.inst().get(Config.TIMEOUT, 180);
1.131 +
1.132 + synchronized (this.connections)
1.133 + {
1.134 + final ListIterator<NNTPConnection> iter = this.connections.listIterator();
1.135 + NNTPConnection conn;
1.136 +
1.137 + while (iter.hasNext())
1.138 + {
1.139 + conn = iter.next();
1.140 + if((System.currentTimeMillis() - conn.getLastActivity()) > timeoutMillis
1.141 + && conn.getBuffers().isOutputBufferEmpty())
1.142 + {
1.143 + // A connection timeout has occurred so purge the connection
1.144 + iter.remove();
1.145 +
1.146 + // Close and remove the channel
1.147 + SocketChannel channel = conn.getSocketChannel();
1.148 + connByChannel.remove(channel);
1.149 +
1.150 + try
1.151 + {
1.152 + assert channel != null;
1.153 + assert channel.socket() != null;
1.154 +
1.155 + // Close the channel; implicitely cancels all selectionkeys
1.156 + channel.close();
1.157 + Log.get().info("Disconnected: " + channel.socket().getRemoteSocketAddress() +
1.158 + " (timeout)");
1.159 + }
1.160 + catch(IOException ex)
1.161 + {
1.162 + Log.get().warning("Connections.run(): " + ex);
1.163 + }
1.164 +
1.165 + // Recycle the used buffers
1.166 + conn.getBuffers().recycleBuffers();
1.167 +
1.168 + Stats.getInstance().clientDisconnect();
1.169 + }
1.170 + }
1.171 + }
1.172 +
1.173 + try
1.174 + {
1.175 + Thread.sleep(10000); // Sleep ten seconds
1.176 + }
1.177 + catch(InterruptedException ex)
1.178 + {
1.179 + Log.get().warning("Connections Thread was interrupted: " + ex.getMessage());
1.180 + }
1.181 + }
1.182 + }
1.183 +
1.184 +}