diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/pom.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/pom.xml
index 69107ba42..1556f21da 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/pom.xml
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/pom.xml
@@ -90,7 +90,14 @@
guice-multibindings
3.0
-
+
+
+
+ com.google.guava
+ guava
+ 18.0
+
+
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java
index 017e6c297..bc7580033 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java
@@ -82,10 +82,9 @@ public class JDBCAuthenticationProviderModule extends MyBatisModule {
private final Environment environment;
/**
- * The service class to use to provide GuacamoleSockets for each
- * connection.
+ * The service to use to provide GuacamoleTunnels for each connection.
*/
- private final Class extends GuacamoleTunnelService> tunnelServiceClass;
+ private final GuacamoleTunnelService tunnelService;
/**
* Creates a new JDBC authentication provider module that configures the
@@ -95,13 +94,13 @@ public class JDBCAuthenticationProviderModule extends MyBatisModule {
* @param environment
* The environment to use to configure injected classes.
*
- * @param tunnelServiceClass
- * The socket service to use to provide sockets for connections.
+ * @param tunnelService
+ * The tunnel service to use to provide tunnels sockets for connections.
*/
public JDBCAuthenticationProviderModule(Environment environment,
- Class extends GuacamoleTunnelService> tunnelServiceClass) {
+ GuacamoleTunnelService tunnelService) {
this.environment = environment;
- this.tunnelServiceClass = tunnelServiceClass;
+ this.tunnelService = tunnelService;
}
@Override
@@ -156,8 +155,8 @@ public class JDBCAuthenticationProviderModule extends MyBatisModule {
bind(UserPermissionService.class);
bind(UserService.class);
- // Bind provided socket service
- bind(GuacamoleTunnelService.class).to(tunnelServiceClass);
+ // Bind provided tunnel service
+ bind(GuacamoleTunnelService.class).toInstance(tunnelService);
}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/BalancedGuacamoleTunnelService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/BalancedGuacamoleTunnelService.java
deleted file mode 100644
index b2cfcc8d1..000000000
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/BalancedGuacamoleTunnelService.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2015 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.auth.jdbc.tunnel;
-
-import com.google.inject.Singleton;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-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.connectiongroup.ModeledConnectionGroup;
-
-
-/**
- * GuacamoleTunnelService implementation which allows only one user per
- * connection at any time, but does not disallow concurrent use of connection
- * groups. If a user attempts to use a connection group multiple times, they
- * will receive different underlying connections each time until the group is
- * exhausted.
- *
- * @author Michael Jumper
- */
-@Singleton
-public class BalancedGuacamoleTunnelService
- extends AbstractGuacamoleTunnelService {
-
- /**
- * The set of all active connection identifiers.
- */
- private final Set activeConnections =
- Collections.newSetFromMap(new ConcurrentHashMap());
-
- @Override
- protected ModeledConnection acquire(AuthenticatedUser user,
- List connections) throws GuacamoleException {
-
- // Return the first unused connection
- for (ModeledConnection connection : connections) {
- if (activeConnections.add(connection.getIdentifier()))
- return connection;
- }
-
- // Already in use
- throw new GuacamoleResourceConflictException("Cannot connect. This connection is in use.");
-
- }
-
- @Override
- protected void release(AuthenticatedUser user, ModeledConnection connection) {
- activeConnections.remove(connection.getIdentifier());
- }
-
- @Override
- protected void acquire(AuthenticatedUser user,
- ModeledConnectionGroup connectionGroup) throws GuacamoleException {
- // Do nothing
- }
-
- @Override
- protected void release(AuthenticatedUser user,
- ModeledConnectionGroup connectionGroup) {
- // Do nothing
- }
-
-}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ConfigurableGuacamoleTunnelService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ConfigurableGuacamoleTunnelService.java
new file mode 100644
index 000000000..a3f5b6d0e
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ConfigurableGuacamoleTunnelService.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.auth.jdbc.tunnel;
+
+import com.google.common.collect.ConcurrentHashMultiset;
+import com.google.inject.Singleton;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+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.connectiongroup.ModeledConnectionGroup;
+
+
+/**
+ * GuacamoleTunnelService implementation which restricts concurrency for each
+ * connection and group according to a maximum number of connections and
+ * maximum number of connections per user.
+ *
+ * @author James Muehlner
+ * @author Michael Jumper
+ */
+@Singleton
+public class ConfigurableGuacamoleTunnelService
+ extends AbstractGuacamoleTunnelService {
+
+ /**
+ * Set of all currently-active user/connection pairs (seats).
+ */
+ private final ConcurrentHashMultiset activeSeats = ConcurrentHashMultiset.create();
+
+ /**
+ * Set of all currently-active connections.
+ */
+ private final ConcurrentHashMultiset activeConnections = ConcurrentHashMultiset.create();
+
+ /**
+ * Set of all currently-active user/connection group pairs (seats).
+ */
+ private final ConcurrentHashMultiset activeGroupSeats = ConcurrentHashMultiset.create();
+
+ /**
+ * Set of all currently-active connection groups.
+ */
+ private final ConcurrentHashMultiset activeGroups = ConcurrentHashMultiset.create();
+
+ /**
+ * The maximum number of connections allowed per connection by default, or
+ * zero if no default limit applies.
+ */
+ private final int connectionDefaultMaxConnections;
+
+ /**
+ * The maximum number of connections a user may have to any one connection
+ * by default, or zero if no default limit applies.
+ */
+ private final int connectionDefaultMaxConnectionsPerUser;
+
+ /**
+ * The maximum number of connections allowed per connection group by
+ * default, or zero if no default limit applies.
+ */
+ private final int connectionGroupDefaultMaxConnections;
+
+ /**
+ * The maximum number of connections a user may have to any one connection
+ * group by default, or zero if no default limit applies.
+ */
+ private final int connectionGroupDefaultMaxConnectionsPerUser;
+
+ /**
+ * Creates a new ConfigurableGuacamoleTunnelService which applies the given
+ * limitations when new connections are acquired.
+ *
+ * @param connectionDefaultMaxConnections
+ * The maximum number of connections allowed per connection by default,
+ * or zero if no default limit applies.
+ *
+ * @param connectionDefaultMaxConnectionsPerUser
+ * The maximum number of connections a user may have to any one
+ * connection by default, or zero if no default limit applies.
+ *
+ * @param connectionGroupDefaultMaxConnections
+ * The maximum number of connections allowed per connection group by
+ * default, or zero if no default limit applies.
+ *
+ * @param connectionGroupDefaultMaxConnectionsPerUser
+ * The maximum number of connections a user may have to any one
+ * connection group by default, or zero if no default limit applies.
+ */
+ public ConfigurableGuacamoleTunnelService(
+ int connectionDefaultMaxConnections,
+ int connectionDefaultMaxConnectionsPerUser,
+ int connectionGroupDefaultMaxConnections,
+ int connectionGroupDefaultMaxConnectionsPerUser) {
+
+ // Set default connection limits
+ this.connectionDefaultMaxConnections = connectionDefaultMaxConnections;
+ this.connectionDefaultMaxConnectionsPerUser = connectionDefaultMaxConnectionsPerUser;
+
+ // Set default connection group limits
+ this.connectionGroupDefaultMaxConnections = connectionGroupDefaultMaxConnections;
+ this.connectionGroupDefaultMaxConnectionsPerUser = connectionGroupDefaultMaxConnectionsPerUser;
+
+ }
+
+ /**
+ * 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
+ * returned.
+ *
+ * @param
+ * The type of values contained within the multiset.
+ *
+ * @param multiset
+ * The multiset to attempt to add a value to.
+ *
+ * @param value
+ * The value to attempt to add.
+ *
+ * @param max
+ * The maximum number of each distinct value that the given multiset
+ * should hold, or zero if no limit applies.
+ *
+ * @return
+ * true if the value was successfully added without exceeding the
+ * specified maximum, false if the value could not be added.
+ */
+ private boolean tryAdd(ConcurrentHashMultiset multiset, T value, int max) {
+
+ // Repeatedly attempt to add a new value to the given multiset until we
+ // explicitly succeed or explicitly fail
+ while (true) {
+
+ // Get current number of values
+ int count = multiset.count(value);
+
+ // Bail out if the maximum has already been reached
+ if (count >= max || max == 0)
+ return false;
+
+ // Attempt to add one more value
+ if (multiset.setCount(value, count, count+1))
+ return true;
+
+ // Try again if unsuccessful
+
+ }
+
+ }
+
+ @Override
+ protected ModeledConnection acquire(AuthenticatedUser user,
+ List connections) throws GuacamoleException {
+
+ // Get username
+ String username = user.getUser().getIdentifier();
+
+ // Sort connections in ascending order of usage
+ ModeledConnection[] sortedConnections = connections.toArray(new ModeledConnection[connections.size()]);
+ Arrays.sort(sortedConnections, new Comparator() {
+
+ @Override
+ public int compare(ModeledConnection a, ModeledConnection b) {
+
+ return getActiveConnections(a).size()
+ - getActiveConnections(b).size();
+
+ }
+
+ });
+
+ // Track whether acquire fails due to user-specific limits
+ boolean userSpecificFailure = true;
+
+ // Return the first unreserved connection
+ for (ModeledConnection connection : sortedConnections) {
+
+ // Attempt to aquire connection according to per-user limits
+ Seat seat = new Seat(username, connection.getIdentifier());
+ if (tryAdd(activeSeats, seat,
+ connectionDefaultMaxConnectionsPerUser)) {
+
+ // Attempt to aquire connection according to overall limits
+ if (tryAdd(activeConnections, connection.getIdentifier(),
+ connectionDefaultMaxConnections))
+ return connection;
+
+ // Acquire failed - retry with next connection
+ activeSeats.remove(seat);
+
+ // Failure to acquire is not user-specific
+ userSpecificFailure = false;
+
+ }
+
+ }
+
+ // Too many connections by this user
+ if (userSpecificFailure)
+ throw new GuacamoleClientTooManyException("Cannot connect. Connection group already in use by this user.");
+
+ // Too many connections, but not necessarily due purely to this user
+ else
+ throw new GuacamoleResourceConflictException("Cannot connect. This connection is in use.");
+
+ }
+
+ @Override
+ protected void release(AuthenticatedUser user, ModeledConnection connection) {
+ activeSeats.remove(new Seat(user.getUser().getIdentifier(), connection.getIdentifier()));
+ activeConnections.remove(connection.getIdentifier());
+ }
+
+ @Override
+ protected void acquire(AuthenticatedUser user,
+ ModeledConnectionGroup connectionGroup) throws GuacamoleException {
+
+ // Get username
+ String username = user.getUser().getIdentifier();
+
+ // Attempt to aquire connection group according to per-user limits
+ Seat seat = new Seat(username, connectionGroup.getIdentifier());
+ if (tryAdd(activeGroupSeats, seat,
+ connectionGroupDefaultMaxConnectionsPerUser)) {
+
+ // Attempt to aquire connection group according to overall limits
+ if (tryAdd(activeGroups, connectionGroup.getIdentifier(),
+ connectionGroupDefaultMaxConnections))
+ return;
+
+ // Acquire failed
+ activeGroupSeats.remove(seat);
+
+ // Failure to acquire is not user-specific
+ throw new GuacamoleResourceConflictException("Cannot connect. This connection group is in use.");
+
+ }
+
+ // Already in use by this user
+ throw new GuacamoleClientTooManyException("Cannot connect. Connection group already in use by this user.");
+
+ }
+
+ @Override
+ protected void release(AuthenticatedUser user,
+ ModeledConnectionGroup connectionGroup) {
+ activeGroupSeats.remove(new Seat(user.getUser().getIdentifier(), connectionGroup.getIdentifier()));
+ activeGroups.remove(connectionGroup.getIdentifier());
+ }
+
+}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/MultiseatGuacamoleTunnelService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/MultiseatGuacamoleTunnelService.java
deleted file mode 100644
index 412e2f32e..000000000
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/MultiseatGuacamoleTunnelService.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2015 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.auth.jdbc.tunnel;
-
-import com.google.inject.Singleton;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-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.connectiongroup.ModeledConnectionGroup;
-
-
-/**
- * GuacamoleTunnelService implementation which restricts concurrency only on a
- * per-user basis. Each connection or group may be used concurrently any number
- * of times, but each concurrent use must be associated with a different user.
- *
- * @author Michael Jumper
- */
-@Singleton
-public class MultiseatGuacamoleTunnelService
- extends AbstractGuacamoleTunnelService {
-
- /**
- * The set of all active user/connection pairs.
- */
- private final Set activeSeats =
- Collections.newSetFromMap(new ConcurrentHashMap());
-
- /**
- * The set of all active user/connection group pairs.
- */
- private final Set activeGroupSeats =
- Collections.newSetFromMap(new ConcurrentHashMap());
-
- @Override
- protected ModeledConnection acquire(AuthenticatedUser user,
- List connections) throws GuacamoleException {
-
- String username = user.getUser().getIdentifier();
-
- // Sort connections in ascending order of usage
- ModeledConnection[] sortedConnections = connections.toArray(new ModeledConnection[connections.size()]);
- Arrays.sort(sortedConnections, new Comparator() {
-
- @Override
- public int compare(ModeledConnection a, ModeledConnection b) {
-
- return getActiveConnections(a).size()
- - getActiveConnections(b).size();
-
- }
-
- });
-
- // Return the first unreserved connection
- for (ModeledConnection connection : sortedConnections) {
- if (activeSeats.add(new Seat(username, connection.getIdentifier())))
- return connection;
- }
-
- // Already in use
- throw new GuacamoleResourceConflictException("Cannot connect. This connection is in use.");
-
- }
-
- @Override
- protected void release(AuthenticatedUser user, ModeledConnection connection) {
- activeSeats.remove(new Seat(user.getUser().getIdentifier(), connection.getIdentifier()));
- }
-
- @Override
- protected void acquire(AuthenticatedUser user,
- ModeledConnectionGroup connectionGroup) throws GuacamoleException {
-
- // Do not allow duplicate use of connection groups
- Seat seat = new Seat(user.getUser().getIdentifier(), connectionGroup.getIdentifier());
- if (!activeGroupSeats.add(seat))
- throw new GuacamoleClientTooManyException("Cannot connect. Connection group already in use by this user.");
-
- }
-
- @Override
- protected void release(AuthenticatedUser user,
- ModeledConnectionGroup connectionGroup) {
- activeGroupSeats.remove(new Seat(user.getUser().getIdentifier(), connectionGroup.getIdentifier()));
- }
-
-}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/SingleSeatGuacamoleTunnelService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/SingleSeatGuacamoleTunnelService.java
deleted file mode 100644
index 9fce08e5f..000000000
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/SingleSeatGuacamoleTunnelService.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2015 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.auth.jdbc.tunnel;
-
-import com.google.inject.Singleton;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-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.connectiongroup.ModeledConnectionGroup;
-
-
-/**
- * GuacamoleTunnelService implementation which allows exactly one use
- * of any connection at any time. Concurrent usage of connections is not
- * allowed, and concurrent usage of connection groups is allowed only between
- * different users.
- *
- * @author Michael Jumper
- */
-@Singleton
-public class SingleSeatGuacamoleTunnelService
- extends AbstractGuacamoleTunnelService {
-
- /**
- * The set of all active connection identifiers.
- */
- private final Set activeConnections =
- Collections.newSetFromMap(new ConcurrentHashMap());
-
- /**
- * The set of all active user/connection group pairs.
- */
- private final Set activeGroupSeats =
- Collections.newSetFromMap(new ConcurrentHashMap());
-
- @Override
- protected ModeledConnection acquire(AuthenticatedUser user,
- List connections) throws GuacamoleException {
-
- // Return the first unused connection
- for (ModeledConnection connection : connections) {
- if (activeConnections.add(connection.getIdentifier()))
- return connection;
- }
-
- // Already in use
- throw new GuacamoleResourceConflictException("Cannot connect. This connection is in use.");
-
- }
-
- @Override
- protected void release(AuthenticatedUser user, ModeledConnection connection) {
- activeConnections.remove(connection.getIdentifier());
- }
-
- @Override
- protected void acquire(AuthenticatedUser user,
- ModeledConnectionGroup connectionGroup) throws GuacamoleException {
-
- // Do not allow duplicate use of connection groups
- Seat seat = new Seat(user.getUser().getIdentifier(), connectionGroup.getIdentifier());
- if (!activeGroupSeats.add(seat))
- throw new GuacamoleClientTooManyException("Cannot connect. Connection group already in use by this user.");
-
- }
-
- @Override
- protected void release(AuthenticatedUser user,
- ModeledConnectionGroup connectionGroup) {
- activeGroupSeats.remove(new Seat(user.getUser().getIdentifier(), connectionGroup.getIdentifier()));
- }
-
-}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/UnrestrictedGuacamoleTunnelService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/UnrestrictedGuacamoleTunnelService.java
deleted file mode 100644
index 5aad81481..000000000
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/UnrestrictedGuacamoleTunnelService.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2015 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.auth.jdbc.tunnel;
-
-import com.google.inject.Singleton;
-import java.util.List;
-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.auth.jdbc.connectiongroup.ModeledConnectionGroup;
-
-
-/**
- * GuacamoleTunnelService implementation which imposes no restrictions
- * whatsoever on the number of concurrent or duplicate connections.
- *
- * @author Michael Jumper
- */
-@Singleton
-public class UnrestrictedGuacamoleTunnelService
- extends AbstractGuacamoleTunnelService {
-
- @Override
- protected ModeledConnection acquire(AuthenticatedUser user,
- List connections) throws GuacamoleException {
-
- ModeledConnection chosen = null;
- int lowestUsage = 0;
-
- // Find connection with lowest usage
- for (ModeledConnection connection : connections) {
-
- int usage = getActiveConnections(connection).size();
- if (chosen == null || usage < lowestUsage) {
- chosen = connection;
- lowestUsage = usage;
- }
-
- }
-
- return chosen;
-
- }
-
- @Override
- protected void release(AuthenticatedUser user, ModeledConnection connection) {
- // Do nothing
- }
-
- @Override
- protected void acquire(AuthenticatedUser user,
- ModeledConnectionGroup connectionGroup) throws GuacamoleException {
- // Do nothing
- }
-
- @Override
- protected void release(AuthenticatedUser user,
- ModeledConnectionGroup connectionGroup) {
- // Do nothing
- }
-
-}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java
index 617061bc2..544365a95 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java
@@ -30,10 +30,7 @@ import org.glyptodon.guacamole.net.auth.Credentials;
import org.glyptodon.guacamole.net.auth.UserContext;
import org.glyptodon.guacamole.auth.jdbc.JDBCAuthenticationProviderModule;
import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService;
-import org.glyptodon.guacamole.auth.jdbc.tunnel.MultiseatGuacamoleTunnelService;
-import org.glyptodon.guacamole.auth.jdbc.tunnel.BalancedGuacamoleTunnelService;
-import org.glyptodon.guacamole.auth.jdbc.tunnel.SingleSeatGuacamoleTunnelService;
-import org.glyptodon.guacamole.auth.jdbc.tunnel.UnrestrictedGuacamoleTunnelService;
+import org.glyptodon.guacamole.auth.jdbc.tunnel.ConfigurableGuacamoleTunnelService;
import org.glyptodon.guacamole.auth.jdbc.user.UserContextService;
import org.glyptodon.guacamole.environment.Environment;
import org.glyptodon.guacamole.environment.LocalEnvironment;
@@ -54,52 +51,62 @@ public class MySQLAuthenticationProvider implements AuthenticationProvider {
private final Injector injector;
/**
- * Returns the appropriate socket service class given the Guacamole
- * environment. The class is chosen based on configuration options that
- * dictate concurrent usage policy.
+ * Returns the appropriate tunnel service given the Guacamole environment.
+ * The service is configured based on configuration options that dictate
+ * the default concurrent usage policy.
*
* @param environment
* The environment of the Guacamole server.
*
* @return
- * The socket service class that matches the concurrent usage policy
- * options set in the Guacamole environment.
+ * A tunnel service implementation configured according to the
+ * concurrent usage policy options set in the Guacamole environment.
*
* @throws GuacamoleException
* If an error occurs while reading the configuration options.
*/
- private Class extends GuacamoleTunnelService>
- getSocketServiceClass(Environment environment)
+ private GuacamoleTunnelService getTunnelService(Environment environment)
throws GuacamoleException {
- // Read concurrency-related properties
+ // Tunnel service default configuration
+ int connectionDefaultMaxConnections;
+ int connectionDefaultMaxConnectionsPerUser;
+ int connectionGroupDefaultMaxConnections;
+ int connectionGroupDefaultMaxConnectionsPerUser;
+
+ // Read legacy concurrency-related properties
boolean disallowSimultaneous = environment.getProperty(MySQLGuacamoleProperties.MYSQL_DISALLOW_SIMULTANEOUS_CONNECTIONS, false);
boolean disallowDuplicate = environment.getProperty(MySQLGuacamoleProperties.MYSQL_DISALLOW_DUPLICATE_CONNECTIONS, true);
- if (disallowSimultaneous) {
+ // Legacy properties to not affect max connections per group
+ connectionGroupDefaultMaxConnections = 0;
- // Connections may not be used concurrently
- if (disallowDuplicate)
- return SingleSeatGuacamoleTunnelService.class;
-
- // Connections are reserved for a single user when in use
- else
- return BalancedGuacamoleTunnelService.class;
+ // Legacy "simultaneous" property dictates only the maximum number of
+ // connections per connection
+ if (disallowSimultaneous)
+ connectionDefaultMaxConnections = 1;
+ else
+ connectionDefaultMaxConnections = 0;
+ // Legacy "duplicate" property dictates whether connections and groups
+ // may be used concurrently only by different users
+ if (disallowDuplicate) {
+ connectionDefaultMaxConnectionsPerUser = 1;
+ connectionGroupDefaultMaxConnectionsPerUser = 1;
}
-
else {
-
- // Connections may be used concurrently, but only once per user
- if (disallowDuplicate)
- return MultiseatGuacamoleTunnelService.class;
-
- // Connection use is not restricted
- else
- return UnrestrictedGuacamoleTunnelService.class;
-
+ connectionDefaultMaxConnectionsPerUser = 0;
+ connectionGroupDefaultMaxConnectionsPerUser = 0;
}
-
+
+ // Return service configured for specified default limits
+ return new ConfigurableGuacamoleTunnelService(
+ connectionDefaultMaxConnections,
+ connectionDefaultMaxConnectionsPerUser,
+ connectionGroupDefaultMaxConnections,
+ connectionGroupDefaultMaxConnectionsPerUser
+ );
+
}
/**
@@ -123,7 +130,7 @@ public class MySQLAuthenticationProvider implements AuthenticationProvider {
new MySQLAuthenticationProviderModule(environment),
// Configure JDBC authentication core
- new JDBCAuthenticationProviderModule(environment, getSocketServiceClass(environment))
+ new JDBCAuthenticationProviderModule(environment, getTunnelService(environment))
);
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLAuthenticationProvider.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLAuthenticationProvider.java
index 1ee8afe2f..5a5d775f4 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLAuthenticationProvider.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLAuthenticationProvider.java
@@ -29,11 +29,8 @@ import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
import org.glyptodon.guacamole.net.auth.Credentials;
import org.glyptodon.guacamole.net.auth.UserContext;
import org.glyptodon.guacamole.auth.jdbc.JDBCAuthenticationProviderModule;
-import org.glyptodon.guacamole.auth.jdbc.tunnel.BalancedGuacamoleTunnelService;
+import org.glyptodon.guacamole.auth.jdbc.tunnel.ConfigurableGuacamoleTunnelService;
import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService;
-import org.glyptodon.guacamole.auth.jdbc.tunnel.MultiseatGuacamoleTunnelService;
-import org.glyptodon.guacamole.auth.jdbc.tunnel.SingleSeatGuacamoleTunnelService;
-import org.glyptodon.guacamole.auth.jdbc.tunnel.UnrestrictedGuacamoleTunnelService;
import org.glyptodon.guacamole.auth.jdbc.user.UserContextService;
import org.glyptodon.guacamole.environment.Environment;
import org.glyptodon.guacamole.environment.LocalEnvironment;
@@ -54,52 +51,62 @@ public class PostgreSQLAuthenticationProvider implements AuthenticationProvider
private final Injector injector;
/**
- * Returns the appropriate socket service class given the Guacamole
- * environment. The class is chosen based on configuration options that
- * dictate concurrent usage policy.
+ * Returns the appropriate tunnel service given the Guacamole environment.
+ * The service is configured based on configuration options that dictate
+ * the default concurrent usage policy.
*
* @param environment
* The environment of the Guacamole server.
*
* @return
- * The socket service class that matches the concurrent usage policy
- * options set in the Guacamole environment.
+ * A tunnel service implementation configured according to the
+ * concurrent usage policy options set in the Guacamole environment.
*
* @throws GuacamoleException
* If an error occurs while reading the configuration options.
*/
- private Class extends GuacamoleTunnelService>
- getSocketServiceClass(Environment environment)
+ private GuacamoleTunnelService getTunnelService(Environment environment)
throws GuacamoleException {
- // Read concurrency-related properties
+ // Tunnel service default configuration
+ int connectionDefaultMaxConnections;
+ int connectionDefaultMaxConnectionsPerUser;
+ int connectionGroupDefaultMaxConnections;
+ int connectionGroupDefaultMaxConnectionsPerUser;
+
+ // Read legacy concurrency-related properties
boolean disallowSimultaneous = environment.getProperty(PostgreSQLGuacamoleProperties.POSTGRESQL_DISALLOW_SIMULTANEOUS_CONNECTIONS, false);
boolean disallowDuplicate = environment.getProperty(PostgreSQLGuacamoleProperties.POSTGRESQL_DISALLOW_DUPLICATE_CONNECTIONS, true);
- if (disallowSimultaneous) {
+ // Legacy properties to not affect max connections per group
+ connectionGroupDefaultMaxConnections = 0;
- // Connections may not be used concurrently
- if (disallowDuplicate)
- return SingleSeatGuacamoleTunnelService.class;
-
- // Connections are reserved for a single user when in use
- else
- return BalancedGuacamoleTunnelService.class;
+ // Legacy "simultaneous" property dictates only the maximum number of
+ // connections per connection
+ if (disallowSimultaneous)
+ connectionDefaultMaxConnections = 1;
+ else
+ connectionDefaultMaxConnections = 0;
+ // Legacy "duplicate" property dictates whether connections and groups
+ // may be used concurrently only by different users
+ if (disallowDuplicate) {
+ connectionDefaultMaxConnectionsPerUser = 1;
+ connectionGroupDefaultMaxConnectionsPerUser = 1;
}
-
else {
-
- // Connections may be used concurrently, but only once per user
- if (disallowDuplicate)
- return MultiseatGuacamoleTunnelService.class;
-
- // Connection use is not restricted
- else
- return UnrestrictedGuacamoleTunnelService.class;
-
+ connectionDefaultMaxConnectionsPerUser = 0;
+ connectionGroupDefaultMaxConnectionsPerUser = 0;
}
-
+
+ // Return service configured for specified default limits
+ return new ConfigurableGuacamoleTunnelService(
+ connectionDefaultMaxConnections,
+ connectionDefaultMaxConnectionsPerUser,
+ connectionGroupDefaultMaxConnections,
+ connectionGroupDefaultMaxConnectionsPerUser
+ );
+
}
/**
@@ -123,7 +130,7 @@ public class PostgreSQLAuthenticationProvider implements AuthenticationProvider
new PostgreSQLAuthenticationProviderModule(environment),
// Configure JDBC authentication core
- new JDBCAuthenticationProviderModule(environment, getSocketServiceClass(environment))
+ new JDBCAuthenticationProviderModule(environment, getTunnelService(environment))
);