src/org/sonews/daemon/Connections.java
author cli
Sun Aug 29 18:17:37 2010 +0200 (2010-08-29)
changeset 37 74139325d305
parent 35 ed84c8bdd87b
permissions -rw-r--r--
Switch intent style to Original K&R / Linux / Kernel.
chris@1
     1
/*
chris@1
     2
 *   SONEWS News Server
chris@1
     3
 *   see AUTHORS for the list of contributors
chris@1
     4
 *
chris@1
     5
 *   This program is free software: you can redistribute it and/or modify
chris@1
     6
 *   it under the terms of the GNU General Public License as published by
chris@1
     7
 *   the Free Software Foundation, either version 3 of the License, or
chris@1
     8
 *   (at your option) any later version.
chris@1
     9
 *
chris@1
    10
 *   This program is distributed in the hope that it will be useful,
chris@1
    11
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
chris@1
    12
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
chris@1
    13
 *   GNU General Public License for more details.
chris@1
    14
 *
chris@1
    15
 *   You should have received a copy of the GNU General Public License
chris@1
    16
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
chris@1
    17
 */
chris@1
    18
chris@1
    19
package org.sonews.daemon;
chris@1
    20
chris@3
    21
import org.sonews.config.Config;
chris@1
    22
import org.sonews.util.Log;
cli@28
    23
import org.sonews.util.Stats;
chris@1
    24
import java.io.IOException;
chris@1
    25
import java.net.InetSocketAddress;
chris@1
    26
import java.net.Socket;
chris@1
    27
import java.nio.channels.SocketChannel;
chris@1
    28
import java.util.ArrayList;
chris@1
    29
import java.util.HashMap;
chris@1
    30
import java.util.List;
chris@1
    31
import java.util.ListIterator;
chris@1
    32
import java.util.Map;
chris@1
    33
chris@1
    34
/**
chris@1
    35
 * Daemon thread collecting all NNTPConnection instances. The thread
chris@1
    36
 * checks periodically if there are stale/timed out connections and
chris@1
    37
 * removes and purges them properly.
chris@1
    38
 * @author Christian Lins
chris@1
    39
 * @since sonews/0.5.0
chris@1
    40
 */
chris@3
    41
public final class Connections extends AbstractDaemon
chris@1
    42
{
chris@1
    43
cli@37
    44
	private static final Connections instance = new Connections();
chris@1
    45
cli@37
    46
	/**
cli@37
    47
	 * @return Active Connections instance.
cli@37
    48
	 */
cli@37
    49
	public static Connections getInstance()
cli@37
    50
	{
cli@37
    51
		return Connections.instance;
cli@37
    52
	}
cli@37
    53
	private final List<NNTPConnection> connections = new ArrayList<NNTPConnection>();
cli@37
    54
	private final Map<SocketChannel, NNTPConnection> connByChannel = new HashMap<SocketChannel, NNTPConnection>();
chris@1
    55
cli@37
    56
	private Connections()
cli@37
    57
	{
cli@37
    58
		setName("Connections");
cli@37
    59
	}
chris@1
    60
cli@37
    61
	/**
cli@37
    62
	 * Adds the given NNTPConnection to the Connections management.
cli@37
    63
	 * @param conn
cli@37
    64
	 * @see org.sonews.daemon.NNTPConnection
cli@37
    65
	 */
cli@37
    66
	public void add(final NNTPConnection conn)
cli@37
    67
	{
cli@37
    68
		synchronized (this.connections) {
cli@37
    69
			this.connections.add(conn);
cli@37
    70
			this.connByChannel.put(conn.getSocketChannel(), conn);
cli@37
    71
		}
cli@37
    72
	}
chris@1
    73
cli@37
    74
	/**
cli@37
    75
	 * @param channel
cli@37
    76
	 * @return NNTPConnection instance that is associated with the given
cli@37
    77
	 * SocketChannel.
cli@37
    78
	 */
cli@37
    79
	public NNTPConnection get(final SocketChannel channel)
cli@37
    80
	{
cli@37
    81
		synchronized (this.connections) {
cli@37
    82
			return this.connByChannel.get(channel);
cli@37
    83
		}
cli@37
    84
	}
chris@1
    85
cli@37
    86
	int getConnectionCount(String remote)
cli@37
    87
	{
cli@37
    88
		int cnt = 0;
cli@37
    89
		synchronized (this.connections) {
cli@37
    90
			for (NNTPConnection conn : this.connections) {
cli@37
    91
				assert conn != null;
cli@37
    92
				assert conn.getSocketChannel() != null;
chris@1
    93
cli@37
    94
				Socket socket = conn.getSocketChannel().socket();
cli@37
    95
				if (socket != null) {
cli@37
    96
					InetSocketAddress sockAddr = (InetSocketAddress) socket.getRemoteSocketAddress();
cli@37
    97
					if (sockAddr != null) {
cli@37
    98
						if (sockAddr.getHostName().equals(remote)) {
cli@37
    99
							cnt++;
cli@37
   100
						}
cli@37
   101
					}
cli@37
   102
				} // if(socket != null)
cli@37
   103
			}
cli@37
   104
		}
cli@37
   105
		return cnt;
cli@37
   106
	}
cli@37
   107
cli@37
   108
	/**
cli@37
   109
	 * Run loops. Checks periodically for timed out connections and purged them
cli@37
   110
	 * from the lists.
cli@37
   111
	 */
cli@37
   112
	@Override
cli@37
   113
	public void run()
cli@37
   114
	{
cli@37
   115
		while (isRunning()) {
cli@37
   116
			int timeoutMillis = 1000 * Config.inst().get(Config.TIMEOUT, 180);
cli@37
   117
cli@37
   118
			synchronized (this.connections) {
cli@37
   119
				final ListIterator<NNTPConnection> iter = this.connections.listIterator();
cli@37
   120
				NNTPConnection conn;
cli@37
   121
cli@37
   122
				while (iter.hasNext()) {
cli@37
   123
					conn = iter.next();
cli@37
   124
					if ((System.currentTimeMillis() - conn.getLastActivity()) > timeoutMillis
cli@37
   125
						&& conn.getBuffers().isOutputBufferEmpty()) {
cli@37
   126
						// A connection timeout has occurred so purge the connection
cli@37
   127
						iter.remove();
cli@37
   128
cli@37
   129
						// Close and remove the channel
cli@37
   130
						SocketChannel channel = conn.getSocketChannel();
cli@37
   131
						connByChannel.remove(channel);
cli@37
   132
cli@37
   133
						try {
cli@37
   134
							assert channel != null;
cli@37
   135
							assert channel.socket() != null;
cli@37
   136
cli@37
   137
							// Close the channel; implicitely cancels all selectionkeys
cli@37
   138
							channel.close();
cli@37
   139
							Log.get().info("Disconnected: " + channel.socket().getRemoteSocketAddress()
cli@37
   140
								+ " (timeout)");
cli@37
   141
						} catch (IOException ex) {
cli@37
   142
							Log.get().warning("Connections.run(): " + ex);
cli@37
   143
						}
cli@37
   144
cli@37
   145
						// Recycle the used buffers
cli@37
   146
						conn.getBuffers().recycleBuffers();
cli@37
   147
cli@37
   148
						Stats.getInstance().clientDisconnect();
cli@37
   149
					}
cli@37
   150
				}
cli@37
   151
			}
cli@37
   152
cli@37
   153
			try {
cli@37
   154
				Thread.sleep(10000); // Sleep ten seconds
cli@37
   155
			} catch (InterruptedException ex) {
cli@37
   156
				Log.get().warning("Connections Thread was interrupted: " + ex.getMessage());
cli@37
   157
			}
cli@37
   158
		}
cli@37
   159
	}
chris@1
   160
}