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