GUAC-1512: Add absolute connection limit properties for MySQL/PostgreSQL.

Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/0a95e161
Tree: 
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/0a95e161
Diff: 
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/0a95e161

Branch: refs/heads/master
Commit: 0a95e16151e953f7b9bf9abb25c07e89a5b43b5e
Parents: b0d890d
Author: Michael Jumper <[email protected]>
Authored: Thu Mar 17 00:51:29 2016 -0700
Committer: Michael Jumper <[email protected]>
Committed: Thu Mar 17 00:56:43 2016 -0700

----------------------------------------------------------------------
 .../guacamole/auth/jdbc/JDBCEnvironment.java    | 14 +++++
 .../RestrictedGuacamoleTunnelService.java       | 63 ++++++++++++++++++++
 .../net/auth/mysql/MySQLEnvironment.java        | 13 ++++
 .../auth/mysql/MySQLGuacamoleProperties.java    | 13 ++++
 .../auth/postgresql/PostgreSQLEnvironment.java  | 13 ++++
 .../PostgreSQLGuacamoleProperties.java          | 13 ++++
 6 files changed, 129 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/0a95e161/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCEnvironment.java
----------------------------------------------------------------------
diff --git 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCEnvironment.java
 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCEnvironment.java
index f7a3a6f..fe944f9 100644
--- 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCEnvironment.java
+++ 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCEnvironment.java
@@ -45,6 +45,20 @@ public abstract class JDBCEnvironment extends 
LocalEnvironment {
     }
 
     /**
+     * Returns the maximum number of concurrent connections to allow overall.
+     * As this limit applies globally (independent of which connection is in
+     * use or which user is using it), this setting cannot be overridden at the
+     * connection level. Zero denotes unlimited.
+     *
+     * @return
+     *     The maximum allowable number of concurrent connections.
+     *
+     * @throws GuacamoleException
+     *     If an error occurs while retrieving the property.
+     */
+    public abstract int getAbsoluteMaxConnections() throws GuacamoleException;
+
+    /**
      * Returns the default maximum number of concurrent connections to allow 
to 
      * any one connection, unless specified differently on an individual 
      * connection. Zero denotes unlimited.

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/0a95e161/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/RestrictedGuacamoleTunnelService.java
----------------------------------------------------------------------
diff --git 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/RestrictedGuacamoleTunnelService.java
 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/RestrictedGuacamoleTunnelService.java
index 7f5f283..b57649e 100644
--- 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/RestrictedGuacamoleTunnelService.java
+++ 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/RestrictedGuacamoleTunnelService.java
@@ -23,15 +23,18 @@
 package org.glyptodon.guacamole.auth.jdbc.tunnel;
 
 import com.google.common.collect.ConcurrentHashMultiset;
+import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
 import org.glyptodon.guacamole.GuacamoleClientTooManyException;
 import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser;
 import org.glyptodon.guacamole.auth.jdbc.connection.ModeledConnection;
 import org.glyptodon.guacamole.GuacamoleException;
 import org.glyptodon.guacamole.GuacamoleResourceConflictException;
+import org.glyptodon.guacamole.auth.jdbc.JDBCEnvironment;
 import 
org.glyptodon.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup;
 
 
@@ -48,6 +51,12 @@ public class RestrictedGuacamoleTunnelService
     extends AbstractGuacamoleTunnelService {
 
     /**
+     * The environment of the Guacamole server.
+     */
+    @Inject
+    private JDBCEnvironment environment;
+
+    /**
      * Set of all currently-active user/connection pairs (seats).
      */
     private final ConcurrentHashMultiset<Seat> activeSeats = 
ConcurrentHashMultiset.<Seat>create();
@@ -68,6 +77,12 @@ public class RestrictedGuacamoleTunnelService
     private final ConcurrentHashMultiset<String> activeGroups = 
ConcurrentHashMultiset.<String>create();
 
     /**
+     * The total number of active connections within this instance of
+     * Guacamole.
+     */
+    private final AtomicInteger totalActiveConnections = new AtomicInteger(0);
+
+    /**
      * Attempts to add a single instance of the given value to the given
      * multiset without exceeding the specified maximum number of values. If
      * the value cannot be added without exceeding the maximum, false is
@@ -113,10 +128,54 @@ public class RestrictedGuacamoleTunnelService
 
     }
 
+    /**
+     * Attempts to increment the given AtomicInteger without exceeding the
+     * specified maximum value. If the AtomicInteger cannot be incremented
+     * without exceeding the maximum, false is returned.
+     *
+     * @param counter
+     *     The AtomicInteger to attempt increment.
+     *
+     * @param max
+     *     The maximum value that the given AtomicInteger should contain, or
+     *     zero if no limit applies.
+     *
+     * @return
+     *     true if the AtomicInteger was successfully incremented without
+     *     exceeding the specified maximum, false if the AtomicInteger could
+     *     not be incremented.
+     */
+    private boolean tryIncrement(AtomicInteger counter, int max) {
+
+        // Repeatedly attempt to increment the given AtomicInteger until we
+        // explicitly succeed or explicitly fail
+        while (true) {
+
+            // Get current value
+            int count = counter.get();
+
+            // Bail out if the maximum has already been reached
+            if (count >= max && max != 0)
+                return false;
+
+            // Attempt to increment
+            if (counter.compareAndSet(count, count+1))
+                return true;
+
+            // Try again if unsuccessful
+
+        }
+
+    }
+
     @Override
     protected ModeledConnection acquire(AuthenticatedUser user,
             List<ModeledConnection> connections) throws GuacamoleException {
 
+        // Do not acquire connection unless within overall limits
+        if (!tryIncrement(totalActiveConnections, 
environment.getAbsoluteMaxConnections()))
+            throw new GuacamoleResourceConflictException("Cannot connect. 
Overall maximum connections reached.");
+
         // Get username
         String username = user.getUser().getIdentifier();
 
@@ -160,6 +219,9 @@ public class RestrictedGuacamoleTunnelService
 
         }
 
+        // Acquire failed
+        totalActiveConnections.decrementAndGet();
+
         // Too many connections by this user
         if (userSpecificFailure)
             throw new GuacamoleClientTooManyException("Cannot connect. 
Connection already in use by this user.");
@@ -174,6 +236,7 @@ public class RestrictedGuacamoleTunnelService
     protected void release(AuthenticatedUser user, ModeledConnection 
connection) {
         activeSeats.remove(new Seat(user.getUser().getIdentifier(), 
connection.getIdentifier()));
         activeConnections.remove(connection.getIdentifier());
+        totalActiveConnections.decrementAndGet();
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/0a95e161/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLEnvironment.java
----------------------------------------------------------------------
diff --git 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLEnvironment.java
 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLEnvironment.java
index 90a42af..4cf474b 100644
--- 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLEnvironment.java
+++ 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLEnvironment.java
@@ -52,6 +52,12 @@ public class MySQLEnvironment extends JDBCEnvironment {
     private static final int DEFAULT_PORT = 3306;
 
     /**
+     * The default value for the maximum number of connections to be
+     * allowed to the Guacamole server overall.
+     */
+    private final int ABSOLUTE_MAX_CONNECTIONS = 0;
+
+    /**
      * The default value for the default maximum number of connections to be
      * allowed per user to any one connection. Note that, as long as the
      * legacy "disallow duplicate" and "disallow simultaneous" properties are
@@ -165,6 +171,13 @@ public class MySQLEnvironment extends JDBCEnvironment {
     }
 
     @Override
+    public int getAbsoluteMaxConnections() throws GuacamoleException {
+        return 
getProperty(MySQLGuacamoleProperties.MYSQL_ABSOLUTE_MAX_CONNECTIONS,
+            ABSOLUTE_MAX_CONNECTIONS
+        );
+    }
+
+    @Override
     public int getDefaultMaxConnections() throws GuacamoleException {
         return getProperty(
             MySQLGuacamoleProperties.MYSQL_DEFAULT_MAX_CONNECTIONS,

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/0a95e161/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLGuacamoleProperties.java
----------------------------------------------------------------------
diff --git 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLGuacamoleProperties.java
 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLGuacamoleProperties.java
index cb0c35b..7a81daf 100644
--- 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLGuacamoleProperties.java
+++ 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLGuacamoleProperties.java
@@ -116,6 +116,19 @@ public class MySQLGuacamoleProperties {
     };
 
     /**
+     * The maximum number of concurrent connections to allow overall. Zero
+     * denotes unlimited.
+     */
+    public static final IntegerGuacamoleProperty
+            MYSQL_ABSOLUTE_MAX_CONNECTIONS =
+            new IntegerGuacamoleProperty() {
+
+        @Override
+        public String getName() { return "mysql-absolute-max-connections"; }
+
+    };
+
+    /**
      * The maximum number of concurrent connections to allow to any one
      * connection. Zero denotes unlimited.
      */

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/0a95e161/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLEnvironment.java
----------------------------------------------------------------------
diff --git 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLEnvironment.java
 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLEnvironment.java
index 83c139e..a87ffec 100644
--- 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLEnvironment.java
+++ 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLEnvironment.java
@@ -51,6 +51,12 @@ public class PostgreSQLEnvironment extends JDBCEnvironment {
     private static final int DEFAULT_PORT = 5432;
 
     /**
+     * The default value for the maximum number of connections to be
+     * allowed to the Guacamole server overall.
+     */
+    private final int ABSOLUTE_MAX_CONNECTIONS = 0;
+
+    /**
      * The default value for the default maximum number of connections to be
      * allowed per user to any one connection. Note that, as long as the
      * legacy "disallow duplicate" and "disallow simultaneous" properties are
@@ -164,6 +170,13 @@ public class PostgreSQLEnvironment extends JDBCEnvironment 
{
     }
 
     @Override
+    public int getAbsoluteMaxConnections() throws GuacamoleException {
+        return 
getProperty(PostgreSQLGuacamoleProperties.POSTGRESQL_ABSOLUTE_MAX_CONNECTIONS,
+            ABSOLUTE_MAX_CONNECTIONS
+        );
+    }
+
+    @Override
     public int getDefaultMaxConnections() throws GuacamoleException {
         return getProperty(
             PostgreSQLGuacamoleProperties.POSTGRESQL_DEFAULT_MAX_CONNECTIONS,

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/0a95e161/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLGuacamoleProperties.java
----------------------------------------------------------------------
diff --git 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLGuacamoleProperties.java
 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLGuacamoleProperties.java
index cf41038..8b87ce0 100644
--- 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLGuacamoleProperties.java
+++ 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLGuacamoleProperties.java
@@ -125,6 +125,19 @@ public class PostgreSQLGuacamoleProperties {
     };
 
     /**
+     * The maximum number of concurrent connections to allow overall. Zero
+     * denotes unlimited.
+     */
+    public static final IntegerGuacamoleProperty
+            POSTGRESQL_ABSOLUTE_MAX_CONNECTIONS =
+            new IntegerGuacamoleProperty() {
+
+        @Override
+        public String getName() { return 
"postgresql-absolute-max-connections"; }
+
+    };
+
+    /**
      * The maximum number of concurrent connections to allow to any one
      * connection. Zero denotes unlimited.
      */

Reply via email to