You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by no...@apache.org on 2006/05/10 05:21:50 UTC

svn commit: r405611 - in /james/server/trunk/src: conf/james-config.xml java/org/apache/james/util/connection/ServerConnection.java java/org/apache/james/util/connection/SimpleConnectionManager.java

Author: norman
Date: Tue May  9 20:21:46 2006
New Revision: 405611

URL: http://svn.apache.org/viewcvs?rev=405611&view=rev
Log:
new feature max-connection-per-ip. see JAMES-484

Modified:
    james/server/trunk/src/conf/james-config.xml
    james/server/trunk/src/java/org/apache/james/util/connection/ServerConnection.java
    james/server/trunk/src/java/org/apache/james/util/connection/SimpleConnectionManager.java

Modified: james/server/trunk/src/conf/james-config.xml
URL: http://svn.apache.org/viewcvs/james/server/trunk/src/conf/james-config.xml?rev=405611&r1=405610&r2=405611&view=diff
==============================================================================
--- james/server/trunk/src/conf/james-config.xml (original)
+++ james/server/trunk/src/conf/james-config.xml Tue May  9 20:21:46 2006
@@ -1178,9 +1178,17 @@
    <!-- Resource limitations imposed by other components (i.e. max # of threads) may -->
    <!-- serve to limit the number of open connections. -->
    <!-- -->
+   <!-- The max-connections-per-ip parameter specifies the default maximum number of client -->
+   <!-- connections per ip that this connection manager will allow per managed server socket. -->
+   <!-- This value can be overridden by each individual service. -->
+   <!-- If no value is specified, the value defaults to 0 ( disabled) . -->
+   <!-- -->
    <connections>
       <idle-timeout>300000</idle-timeout>
       <max-connections>30</max-connections>
+      <!--
+      <max-connections-per-ip>0</max-connections-per-ip>
+      -->
    </connections>
 
    <!-- The Socket Manager block -->

Modified: james/server/trunk/src/java/org/apache/james/util/connection/ServerConnection.java
URL: http://svn.apache.org/viewcvs/james/server/trunk/src/java/org/apache/james/util/connection/ServerConnection.java?rev=405611&r1=405610&r2=405611&view=diff
==============================================================================
--- james/server/trunk/src/java/org/apache/james/util/connection/ServerConnection.java (original)
+++ james/server/trunk/src/java/org/apache/james/util/connection/ServerConnection.java Tue May  9 20:21:46 2006
@@ -23,6 +23,7 @@
 import java.net.Socket;
 import java.net.SocketException;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.Iterator;
 
 import org.apache.avalon.cornerstone.services.connection.ConnectionHandler;
@@ -96,12 +97,23 @@
      * connection will allow.
      */
     private int maxOpenConn;
+    
+    /**
+     * The maximum number of open client connections per IP that this server
+     * connection will allow.
+     */
+    private int maxOpenConnPerIP;
 
     /**
      * A collection of client connection runners.
      */
     private final ArrayList clientConnectionRunners = new ArrayList();
-
+    
+    /**
+     * A HashMap of clientip and connections
+     */
+    private final HashMap connectionPerIP = new HashMap();
+    
     /**
      * The thread used to manage this server connection.
      */
@@ -117,17 +129,20 @@
      * @param timeout the client idle timeout for this ServerConnection's client connections
      * @param maxOpenConn the maximum number of open client connections allowed for this
      *                    ServerConnection
+     * @param maxOpenConnPerIP the maximum number of open client connections allowed for this
+     *                    ServerConnection per IP
      */
     public ServerConnection(ServerSocket serverSocket,
                             ConnectionHandlerFactory handlerFactory,
                             ThreadPool threadPool,
                             int timeout,
-                            int maxOpenConn) {
+                            int maxOpenConn, int maxOpenConnPerIP) {
         this.serverSocket = serverSocket;
         this.handlerFactory = handlerFactory;
         connThreadPool = threadPool;
         socketTimeout = timeout;
         this.maxOpenConn = maxOpenConn;
+        this.maxOpenConnPerIP = maxOpenConnPerIP;
     }
 
     /**
@@ -159,7 +174,9 @@
                 serverConnectionThread = null;
                 thread.interrupt();
                 try {
+                    
                     serverSocket.close();
+                    
                 } catch (IOException ie) {
                     // Ignored - we're doing this to break out of the
                     // accept.  This minimizes the time required to
@@ -191,6 +208,8 @@
                 runner = null;
             }
             clientConnectionRunners.clear();
+            clearConnectionPerIP();
+            
         }
 
         getLogger().debug("Cleaned up clients - " + this.toString());
@@ -243,6 +262,57 @@
     }
 
     /**
+     * Raise the connectionCount for the given ipAdrress
+     * @param ip raise the connectionCount for the given ipAddress
+     */
+    private synchronized void addConnectionPerIP (String ip) {
+        connectionPerIP.put(ip,Integer.toString(getConnectionPerIP(ip) +1));
+    }
+    
+    /**
+     * Get the count of connections for the given ip
+     * @param ip the ipAddress to get the connections for. 
+     * @return count
+     */
+    private synchronized int getConnectionPerIP(String ip) {
+        int count = 0;
+        String curCount = null;
+        Object c = connectionPerIP.get(ip);
+        
+        if (c != null) {
+            curCount = c.toString();
+            
+            if (curCount != null) {
+                return Integer.parseInt(curCount);
+            }
+        }
+        return count;
+    }
+    
+    /**
+     * Set the connection count for the given ipAddress
+     * @param ip ipAddres for which we want to set the count
+     */
+    private synchronized void removeConnectionPerIP (String ip) {
+
+        int count = getConnectionPerIP(ip);
+        if (count > 1) {
+            connectionPerIP.put(ip,Integer.toString(count -1));
+        } else {
+            // not need this entry any more
+            connectionPerIP.remove(ip);
+        }
+
+    }
+    
+    /**
+     * Clear the connection count map
+     */
+    private synchronized void clearConnectionPerIP () {
+        connectionPerIP.clear();
+    }
+    
+    /**
      * Provides the body for the thread of execution for a ServerConnection.
      * Connections made to the server socket are passed to an appropriate,
      * newly created, ClientConnectionRunner
@@ -304,10 +374,26 @@
                             // We ignore this exception, as we already have an error condition.
                         }
                         continue;
+                    } else if ((maxOpenConnPerIP > 0) && (getConnectionPerIP(clientSocket.getInetAddress().getHostAddress()) >= maxOpenConnPerIP)) {
+                        getLogger().info("Maximum number of open connections per IP exceeded - refusing connection.  Current number of connections is " + getConnectionPerIP(clientSocket.getInetAddress().getHostAddress()));
+                            
+                        if (getLogger().isWarnEnabled()) {
+                                
+                            getLogger().warn("Maximum number of open connections per IP exceeded - refusing connection.  Current number of connections is " + getConnectionPerIP(clientSocket.getInetAddress().getHostAddress()));
+                        }
+                        try {
+                            clientSocket.close();
+                        } catch (IOException ignored) {
+                            // We ignore this exception, as we already have an error condition.
+                        }
+                        continue;
+                        
                     } else {
+                        addConnectionPerIP(clientSocket.getInetAddress().getHostAddress());
                         clientSocket.setSoTimeout(socketTimeout);
                         runner = addClientConnectionRunner();
                         runner.setSocket(clientSocket);
+                        
                     }
                 }
                 setupLogger( runner );
@@ -320,6 +406,7 @@
                     getLogger().error("Internal error - insufficient threads available to service request.  " +
                                       Thread.activeCount() + " threads in service request pool.", e);
                     try {
+                        removeConnectionPerIP(clientSocket.getInetAddress().getHostAddress());
                         clientSocket.close();
                     } catch (IOException ignored) {
                         // We ignore this exception, as we already have an error condition.
@@ -368,7 +455,7 @@
 
         public ClientConnectionRunner() {
         }
-
+        
         /**
          * The dispose operation that terminates the runner.  Should only be
          * called by the ServerConnection that owns the ClientConnectionRunner
@@ -430,6 +517,9 @@
                 getLogger().error( "Error handling connection", e );
             } finally {
 
+                // remove this connection from map!
+                removeConnectionPerIP(clientSocket.getInetAddress().getHostAddress());
+                
                 // Close the underlying socket
                 try {
                     if (clientSocket != null) {

Modified: james/server/trunk/src/java/org/apache/james/util/connection/SimpleConnectionManager.java
URL: http://svn.apache.org/viewcvs/james/server/trunk/src/java/org/apache/james/util/connection/SimpleConnectionManager.java?rev=405611&r1=405610&r2=405611&view=diff
==============================================================================
--- james/server/trunk/src/java/org/apache/james/util/connection/SimpleConnectionManager.java (original)
+++ james/server/trunk/src/java/org/apache/james/util/connection/SimpleConnectionManager.java Tue May  9 20:21:46 2006
@@ -56,6 +56,12 @@
      */
     private static final int DEFAULT_MAX_CONNECTIONS = 30;
     /**
+     * The default value for the maximum number of allowed client
+     * connections.
+     */
+    private static final int DEFAULT_MAX_CONNECTIONS_PER_IP = 0;
+    
+    /**
      * The map of connection name / server connections managed by this connection
      * manager.
      */
@@ -69,6 +75,10 @@
      */
     protected int maxOpenConn = 0;
     /**
+     * The maximum number of client connections allowed per server connection per IP.
+     */
+    protected int maxOpenConnPerIP = 0;
+    /**
      * The ThreadManager component that is used to provide a default thread pool.
      */
     private ThreadManager threadManager;
@@ -82,6 +92,7 @@
     public void configure(final Configuration configuration) throws ConfigurationException {
         timeout = configuration.getChild("idle-timeout").getValueAsInteger(DEFAULT_SOCKET_TIMEOUT);
         maxOpenConn = configuration.getChild("max-connections").getValueAsInteger(DEFAULT_MAX_CONNECTIONS);
+        maxOpenConnPerIP = configuration.getChild("max-connections-per-ip").getValueAsInteger(DEFAULT_MAX_CONNECTIONS_PER_IP);
         if (timeout < 0) {
             StringBuffer exceptionBuffer =
                 new StringBuffer(128).append("Specified socket timeout value of ").append(timeout).append(
@@ -95,6 +106,13 @@
                     " is not a legal value.");
             throw new ConfigurationException(exceptionBuffer.toString());
         }
+        if (maxOpenConnPerIP < 0) {
+            StringBuffer exceptionBuffer =
+                new StringBuffer(128).append("Specified maximum number of open connections per IP of ").append(
+                    maxOpenConnPerIP).append(
+                    " is not a legal value.");
+            throw new ConfigurationException(exceptionBuffer.toString());
+        }
         if (getLogger().isDebugEnabled()) {
             getLogger().debug(
                 "Connection timeout is " + (timeout == 0 ? "unlimited" : Long.toString(timeout)));
@@ -159,9 +177,12 @@
         }
         if (maxOpenConnections < 0) {
             throw new IllegalArgumentException("The maximum number of client connections per server socket cannot be less that zero.");
+        } 
+        if (maxOpenConnPerIP < 0) {
+            throw new IllegalArgumentException("The maximum number of client connections (per IP) per server socket cannot be less that zero.");
         }
         ServerConnection runner =
-            new ServerConnection(socket, handlerFactory, threadPool, timeout, maxOpenConnections);
+            new ServerConnection(socket, handlerFactory, threadPool, timeout, maxOpenConnections, maxOpenConnPerIP);
         setupLogger(runner);
         ContainerUtil.initialize(runner);
         connectionMap.put(name, runner);
@@ -207,6 +228,7 @@
      * @param socket the ServerSocket from which to
      * @param handlerFactory the factory from which to acquire handlers
      * @param maxOpenConnections the maximum number of open connections allowed for this server socket.
+     * @param maxOpenConnections the maximum number of open connections per IP allowed for this server socket.
      * @exception Exception if an error occurs
      */
     public void connect(
@@ -253,5 +275,16 @@
     public int getMaximumNumberOfOpenConnections() {
         return maxOpenConn;
     }
+    
+    /**
+     * Returns the default maximum number of open connections per IP supported by this
+     * SimpleConnectionManager
+     *
+     * @return the maximum number of connections
+     */
+    public int getMaximumNumberOfOpenConnectionsPerIP() {
+        return maxOpenConnPerIP;
+    }
+
 
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org