org/sonews/daemon/Connections.java
author cli
Sun, 29 Aug 2010 17:03:21 +0200
changeset 33 f9bf183447d1
parent 28 15d14b110240
permissions -rw-r--r--
Article(javax.mail.Message) now has a safe method to read the byte body from the given message object (fixes #16).
     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   
    54   private final List<NNTPConnection> connections 
    55     = new ArrayList<NNTPConnection>();
    56   private final Map<SocketChannel, NNTPConnection> connByChannel 
    57     = new HashMap<SocketChannel, NNTPConnection>();
    58   
    59   private Connections()
    60   {
    61     setName("Connections");
    62   }
    63   
    64   /**
    65    * Adds the given NNTPConnection to the Connections management.
    66    * @param conn
    67    * @see org.sonews.daemon.NNTPConnection
    68    */
    69   public void add(final NNTPConnection conn)
    70   {
    71     synchronized(this.connections)
    72     {
    73       this.connections.add(conn);
    74       this.connByChannel.put(conn.getSocketChannel(), conn);
    75     }
    76   }
    77   
    78   /**
    79    * @param channel
    80    * @return NNTPConnection instance that is associated with the given
    81    * SocketChannel.
    82    */
    83   public NNTPConnection get(final SocketChannel channel)
    84   {
    85     synchronized(this.connections)
    86     {
    87       return this.connByChannel.get(channel);
    88     }
    89   }
    90 
    91   int getConnectionCount(String remote)
    92   {
    93     int cnt = 0;
    94     synchronized(this.connections)
    95     {
    96       for(NNTPConnection conn : this.connections)
    97       {
    98         assert conn != null;
    99         assert conn.getSocketChannel() != null;
   100 
   101         Socket socket = conn.getSocketChannel().socket();
   102         if(socket != null)
   103         {
   104           InetSocketAddress sockAddr = (InetSocketAddress)socket.getRemoteSocketAddress();
   105           if(sockAddr != null)
   106           {
   107             if(sockAddr.getHostName().equals(remote))
   108             {
   109               cnt++;
   110             }
   111           }
   112         } // if(socket != null)
   113       }
   114     }
   115     return cnt;
   116   }
   117   
   118   /**
   119    * Run loops. Checks periodically for timed out connections and purged them
   120    * from the lists.
   121    */
   122   @Override
   123   public void run()
   124   {
   125     while(isRunning())
   126     {
   127       int timeoutMillis = 1000 * Config.inst().get(Config.TIMEOUT, 180);
   128       
   129       synchronized (this.connections)
   130       {
   131         final ListIterator<NNTPConnection> iter = this.connections.listIterator();
   132         NNTPConnection conn;
   133 
   134         while (iter.hasNext())
   135         {
   136           conn = iter.next();
   137           if((System.currentTimeMillis() - conn.getLastActivity()) > timeoutMillis
   138               && conn.getBuffers().isOutputBufferEmpty())
   139           {
   140             // A connection timeout has occurred so purge the connection
   141             iter.remove();
   142 
   143             // Close and remove the channel
   144             SocketChannel channel = conn.getSocketChannel();
   145             connByChannel.remove(channel);
   146             
   147             try
   148             {
   149               assert channel != null;
   150               assert channel.socket() != null;
   151       
   152               // Close the channel; implicitely cancels all selectionkeys
   153               channel.close();
   154               Log.get().info("Disconnected: " + channel.socket().getRemoteSocketAddress() +
   155                 " (timeout)");
   156             }
   157             catch(IOException ex)
   158             {
   159               Log.get().warning("Connections.run(): " + ex);
   160             }
   161 
   162             // Recycle the used buffers
   163             conn.getBuffers().recycleBuffers();
   164             
   165             Stats.getInstance().clientDisconnect();
   166           }
   167         }
   168       }
   169 
   170       try
   171       {
   172         Thread.sleep(10000); // Sleep ten seconds
   173       }
   174       catch(InterruptedException ex)
   175       {
   176         Log.get().warning("Connections Thread was interrupted: " + ex.getMessage());
   177       }
   178     }
   179   }
   180   
   181 }