org/sonews/daemon/Connections.java
author chris <chris@marvin>
Fri, 26 Jun 2009 16:48:50 +0200
changeset 1 6fceb66e1ad7
child 3 2fdc9cc89502
permissions -rw-r--r--
Hooray... sonews/0.5.0 final

HG: Enter commit message. Lines beginning with 'HG:' are removed.
HG: Remove all lines to abort the collapse operation.
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@1
    21
import org.sonews.util.Log;
chris@1
    22
import java.io.IOException;
chris@1
    23
import java.net.InetSocketAddress;
chris@1
    24
import java.net.Socket;
chris@1
    25
import java.nio.channels.SocketChannel;
chris@1
    26
import java.util.ArrayList;
chris@1
    27
import java.util.HashMap;
chris@1
    28
import java.util.List;
chris@1
    29
import java.util.ListIterator;
chris@1
    30
import java.util.Map;
chris@1
    31
import org.sonews.util.Stats;
chris@1
    32
chris@1
    33
/**
chris@1
    34
 * Daemon thread collecting all NNTPConnection instances. The thread
chris@1
    35
 * checks periodically if there are stale/timed out connections and
chris@1
    36
 * removes and purges them properly.
chris@1
    37
 * @author Christian Lins
chris@1
    38
 * @since sonews/0.5.0
chris@1
    39
 */
chris@1
    40
final class Connections extends AbstractDaemon
chris@1
    41
{
chris@1
    42
chris@1
    43
  private static final Connections instance = new Connections();
chris@1
    44
  
chris@1
    45
  /**
chris@1
    46
   * @return Active Connections instance.
chris@1
    47
   */
chris@1
    48
  public static Connections getInstance()
chris@1
    49
  {
chris@1
    50
    return Connections.instance;
chris@1
    51
  }
chris@1
    52
  
chris@1
    53
  private final List<NNTPConnection> connections 
chris@1
    54
    = new ArrayList<NNTPConnection>();
chris@1
    55
  private final Map<SocketChannel, NNTPConnection> connByChannel 
chris@1
    56
    = new HashMap<SocketChannel, NNTPConnection>();
chris@1
    57
  
chris@1
    58
  private Connections()
chris@1
    59
  {
chris@1
    60
    setName("Connections");
chris@1
    61
  }
chris@1
    62
  
chris@1
    63
  /**
chris@1
    64
   * Adds the given NNTPConnection to the Connections management.
chris@1
    65
   * @param conn
chris@1
    66
   * @see org.sonews.daemon.NNTPConnection
chris@1
    67
   */
chris@1
    68
  public void add(final NNTPConnection conn)
chris@1
    69
  {
chris@1
    70
    synchronized(this.connections)
chris@1
    71
    {
chris@1
    72
      this.connections.add(conn);
chris@1
    73
      this.connByChannel.put(conn.getChannel(), conn);
chris@1
    74
    }
chris@1
    75
  }
chris@1
    76
  
chris@1
    77
  /**
chris@1
    78
   * @param channel
chris@1
    79
   * @return NNTPConnection instance that is associated with the given
chris@1
    80
   * SocketChannel.
chris@1
    81
   */
chris@1
    82
  public NNTPConnection get(final SocketChannel channel)
chris@1
    83
  {
chris@1
    84
    synchronized(this.connections)
chris@1
    85
    {
chris@1
    86
      return this.connByChannel.get(channel);
chris@1
    87
    }
chris@1
    88
  }
chris@1
    89
chris@1
    90
  int getConnectionCount(String remote)
chris@1
    91
  {
chris@1
    92
    int cnt = 0;
chris@1
    93
    synchronized(this.connections)
chris@1
    94
    {
chris@1
    95
      for(NNTPConnection conn : this.connections)
chris@1
    96
      {
chris@1
    97
        assert conn != null;
chris@1
    98
        assert conn.getChannel() != null;
chris@1
    99
chris@1
   100
        Socket socket = conn.getChannel().socket();
chris@1
   101
        if(socket != null)
chris@1
   102
        {
chris@1
   103
          InetSocketAddress sockAddr = (InetSocketAddress)socket.getRemoteSocketAddress();
chris@1
   104
          if(sockAddr != null)
chris@1
   105
          {
chris@1
   106
            if(sockAddr.getHostName().equals(remote))
chris@1
   107
            {
chris@1
   108
              cnt++;
chris@1
   109
            }
chris@1
   110
          }
chris@1
   111
        } // if(socket != null)
chris@1
   112
      }
chris@1
   113
    }
chris@1
   114
    return cnt;
chris@1
   115
  }
chris@1
   116
  
chris@1
   117
  /**
chris@1
   118
   * Run loops. Checks periodically for timed out connections and purged them
chris@1
   119
   * from the lists.
chris@1
   120
   */
chris@1
   121
  @Override
chris@1
   122
  public void run()
chris@1
   123
  {
chris@1
   124
    while(isRunning())
chris@1
   125
    {
chris@1
   126
      int timeoutMillis = 1000 * Config.getInstance().get(Config.TIMEOUT, 180);
chris@1
   127
      
chris@1
   128
      synchronized (this.connections)
chris@1
   129
      {
chris@1
   130
        final ListIterator<NNTPConnection> iter = this.connections.listIterator();
chris@1
   131
        NNTPConnection conn;
chris@1
   132
chris@1
   133
        while (iter.hasNext())
chris@1
   134
        {
chris@1
   135
          conn = iter.next();
chris@1
   136
          if((System.currentTimeMillis() - conn.getLastActivity()) > timeoutMillis)
chris@1
   137
          {
chris@1
   138
            // A connection timeout has occurred so purge the connection
chris@1
   139
            iter.remove();
chris@1
   140
chris@1
   141
            // Close and remove the channel
chris@1
   142
            SocketChannel channel = conn.getChannel();
chris@1
   143
            connByChannel.remove(channel);
chris@1
   144
            
chris@1
   145
            try
chris@1
   146
            {
chris@1
   147
              // Close the channel; implicitely cancels all selectionkeys
chris@1
   148
              channel.close();
chris@1
   149
              Log.msg("Disconnected: " + channel.socket().getRemoteSocketAddress() +
chris@1
   150
                " (timeout)", true);
chris@1
   151
            }
chris@1
   152
            catch(IOException ex)
chris@1
   153
            {
chris@1
   154
              Log.msg("Connections.run(): " + ex, false);
chris@1
   155
            }
chris@1
   156
chris@1
   157
            // Recycle the used buffers
chris@1
   158
            conn.getBuffers().recycleBuffers();
chris@1
   159
            
chris@1
   160
            Stats.getInstance().clientDisconnect();
chris@1
   161
          }
chris@1
   162
        }
chris@1
   163
      }
chris@1
   164
chris@1
   165
      try
chris@1
   166
      {
chris@1
   167
        Thread.sleep(10000); // Sleep ten seconds
chris@1
   168
      }
chris@1
   169
      catch(InterruptedException ex)
chris@1
   170
      {
chris@1
   171
        Log.msg("Connections Thread was interrupted: " + ex.getMessage(), false);
chris@1
   172
      }
chris@1
   173
    }
chris@1
   174
  }
chris@1
   175
  
chris@1
   176
}