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

This commit is contained in:
Michael Jumper
2016-03-17 00:51:29 -07:00
parent b0d890d0ac
commit 0a95e16151
6 changed files with 129 additions and 0 deletions

View File

@@ -44,6 +44,20 @@ public abstract class JDBCEnvironment extends LocalEnvironment {
super();
}
/**
* 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

View File

@@ -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;
@@ -47,6 +50,12 @@ import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup;
public class RestrictedGuacamoleTunnelService
extends AbstractGuacamoleTunnelService {
/**
* The environment of the Guacamole server.
*/
@Inject
private JDBCEnvironment environment;
/**
* Set of all currently-active user/connection pairs (seats).
*/
@@ -67,6 +76,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
@@ -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

View File

@@ -51,6 +51,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
@@ -164,6 +170,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(

View File

@@ -115,6 +115,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.

View File

@@ -50,6 +50,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
@@ -163,6 +169,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(

View File

@@ -124,6 +124,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.