From edcb869c2009ba7e313d7389f42b8608132c9374 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 1 Mar 2015 13:59:34 -0800 Subject: [PATCH 1/4] GUAC-1101: Username parameter of selectOne() is "username", not "identifier". --- .../org/glyptodon/guacamole/auth/jdbc/user/UserMapper.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/user/UserMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/user/UserMapper.xml index fb7e6ff55..5e284658f 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/user/UserMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/user/UserMapper.xml @@ -110,7 +110,7 @@ password_salt FROM guacamole_user WHERE - username = #{identifier,jdbcType=VARCHAR} + username = #{username,jdbcType=VARCHAR} From 89194b432d4f0433e1bce92c2d04e73cc47124ab Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 1 Mar 2015 13:59:54 -0800 Subject: [PATCH 2/4] GUAC-1101: Implement balancing. --- .../AbstractGuacamoleSocketService.java | 109 +++++++++++++++--- .../UnrestrictedGuacamoleSocketService.java | 23 +++- 2 files changed, 115 insertions(+), 17 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java index 5f1b623e6..036dbda7c 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java @@ -23,6 +23,8 @@ package org.glyptodon.guacamole.auth.jdbc.socket; import com.google.inject.Inject; +import com.google.inject.Provider; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -40,6 +42,8 @@ import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionRecordModel; import org.glyptodon.guacamole.auth.jdbc.connection.ParameterModel; import org.glyptodon.guacamole.auth.jdbc.user.UserModel; import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleSecurityException; +import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionMapper; import org.glyptodon.guacamole.environment.Environment; import org.glyptodon.guacamole.net.GuacamoleSocket; import org.glyptodon.guacamole.net.InetGuacamoleSocket; @@ -51,6 +55,7 @@ import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; import org.glyptodon.guacamole.token.StandardTokens; import org.glyptodon.guacamole.token.TokenFilter; +import org.mybatis.guice.transactional.Transactional; /** @@ -68,6 +73,18 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS @Inject private Environment environment; + /** + * Mapper for accessing connections. + */ + @Inject + private ConnectionMapper connectionMapper; + + /** + * Provider for creating connections. + */ + @Inject + private Provider connectionProvider; + /** * Mapper for accessing connection parameters. */ @@ -140,21 +157,24 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS } /** - * Acquires possibly-exclusive access to the given connection on behalf of - * the given user. If access is denied for any reason, an exception is - * thrown. + * Acquires possibly-exclusive access to any one of the given connections + * on behalf of the given user. If access is denied for any reason, or if + * no connection is available, an exception is thrown. * * @param user * The user acquiring access. * - * @param connection - * The connection being accessed. + * @param connections + * The connections being accessed. + * + * @return + * The connection that has been acquired on behalf of the given user. * * @throws GuacamoleException * If access is denied to the given user for any reason. */ - protected abstract void acquire(AuthenticatedUser user, - ModeledConnection connection) throws GuacamoleException; + protected abstract ModeledConnection acquire(AuthenticatedUser user, + List connections) throws GuacamoleException; /** * Releases possibly-exclusive access to the given connection on behalf of @@ -170,8 +190,34 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS protected abstract void release(AuthenticatedUser user, ModeledConnection connection); - @Override - public GuacamoleSocket getGuacamoleSocket(final AuthenticatedUser user, + /** + * Creates a socket for the given user which connects to the given + * connection, which MUST already be acquired via acquire(). The given + * client information will be passed to guacd when the connection is + * established. + * + * The connection will be automatically released when it closes, or if it + * fails to establish entirely. + * + * @param user + * The user for whom the connection is being established. + * + * @param connection + * The connection the user is connecting to. + * + * @param info + * Information describing the Guacamole client connecting to the given + * connection. + * + * @return + * A new GuacamoleSocket which is configured and connected to the given + * connection. + * + * @throws GuacamoleException + * If an error occurs while the connection is being established, or + * while connection configuration information is being retrieved. + */ + private GuacamoleSocket connect(final AuthenticatedUser user, final ModeledConnection connection, GuacamoleClientInformation info) throws GuacamoleException { @@ -200,8 +246,7 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS // Return new socket try { - // Atomically gain access to connection - acquire(user, connection); + // Record new active connection addActiveConnection(connection, activeConnection); // Return newly-reserved connection @@ -255,6 +300,18 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS } + @Override + @Transactional + public GuacamoleSocket getGuacamoleSocket(final AuthenticatedUser user, + final ModeledConnection connection, GuacamoleClientInformation info) + throws GuacamoleException { + + // Acquire and connect to single connection + acquire(user, Collections.singletonList(connection)); + return connect(user, connection, info); + + } + @Override public List getActiveConnections(Connection connection) { synchronized (activeConnections) { @@ -272,11 +329,35 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS } @Override + @Transactional public GuacamoleSocket getGuacamoleSocket(AuthenticatedUser user, ModeledConnectionGroup connectionGroup, GuacamoleClientInformation info) throws GuacamoleException { - // STUB - throw new UnsupportedOperationException("STUB"); + + // If not a balancing group, cannot connect + if (connectionGroup.getType() != ConnectionGroup.Type.BALANCING) + throw new GuacamoleSecurityException("Permission denied."); + + // If group has no children, cannot connect + Collection identifiers = connectionMapper.selectIdentifiersWithin(connectionGroup.getIdentifier()); + if (identifiers.isEmpty()) + throw new GuacamoleSecurityException("Permission denied."); + + // Otherwise, retrieve all children + Collection models = connectionMapper.select(identifiers); + List connections = new ArrayList(models.size()); + + // Convert each retrieved model to a modeled connection + for (ConnectionModel model : models) { + ModeledConnection connection = connectionProvider.get(); + connection.init(user, model); + connections.add(connection); + } + + // Acquire and connect to any child + ModeledConnection connection = acquire(user, connections); + return connect(user, connection, info); + } @Override @@ -284,5 +365,5 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS // STUB return Collections.EMPTY_LIST; } - + } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/UnrestrictedGuacamoleSocketService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/UnrestrictedGuacamoleSocketService.java index a1d619378..c5d2a0503 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/UnrestrictedGuacamoleSocketService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/UnrestrictedGuacamoleSocketService.java @@ -23,6 +23,7 @@ package org.glyptodon.guacamole.auth.jdbc.socket; 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; @@ -39,9 +40,25 @@ public class UnrestrictedGuacamoleSocketService extends AbstractGuacamoleSocketService { @Override - protected void acquire(AuthenticatedUser user, ModeledConnection connection) - throws GuacamoleException { - // Do nothing + 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 From 207d5e433040674abe054f678822e33a20316e3c Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 1 Mar 2015 15:03:07 -0800 Subject: [PATCH 3/4] GUAC-1101: Track connection group usage. --- .../AbstractGuacamoleSocketService.java | 99 ++++---------- .../jdbc/socket/ActiveConnectionMultimap.java | 128 ++++++++++++++++++ 2 files changed, 152 insertions(+), 75 deletions(-) create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionMultimap.java diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java index 036dbda7c..78f4d61ca 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java @@ -28,10 +28,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; -import java.util.HashMap; -import java.util.LinkedList; import java.util.List; -import java.util.Map; import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; import org.glyptodon.guacamole.auth.jdbc.connection.ModeledConnection; import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup; @@ -98,63 +95,14 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS private ConnectionRecordMapper connectionRecordMapper; /** - * The current number of concurrent uses of the connection having a given - * identifier. + * All active connections to a connection having a given identifier. */ - private final Map> activeConnections = - new HashMap>(); + private final ActiveConnectionMultimap activeConnections = new ActiveConnectionMultimap(); /** - * Atomically increments the current usage count for the given connection. - * - * @param connection - * The connection which is being used. + * All active connections to a connection group having a given identifier. */ - private void addActiveConnection(Connection connection, ConnectionRecord record) { - synchronized (activeConnections) { - - String identifier = connection.getIdentifier(); - - // Get set of active connection records, creating if necessary - LinkedList connections = activeConnections.get(identifier); - if (connections == null) { - connections = new LinkedList(); - activeConnections.put(identifier, connections); - } - - // Add active connection - connections.addFirst(record); - - } - } - - /** - * Atomically decrements the current usage count for the given connection. - * If a combination of incrementUsage() and decrementUsage() calls result - * in the usage counter being reduced to zero, it is guaranteed that one - * of those decrementUsage() calls will remove the value from the map. - * - * @param connection - * The connection which is no longer being used. - */ - private void removeActiveConnection(Connection connection, ConnectionRecord record) { - synchronized (activeConnections) { - - String identifier = connection.getIdentifier(); - - // Get set of active connection records - LinkedList connections = activeConnections.get(identifier); - assert(connections != null); - - // Remove old record - connections.remove(record); - - // If now empty, clean the tracking entry - if (connections.isEmpty()) - activeConnections.remove(identifier); - - } - } + private final ActiveConnectionMultimap activeConnectionGroups = new ActiveConnectionMultimap(); /** * Acquires possibly-exclusive access to any one of the given connections @@ -223,6 +171,10 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS // Create record for active connection final ActiveConnectionRecord activeConnection = new ActiveConnectionRecord(user); + + // Get relevant identifiers + final String identifier = connection.getIdentifier(); + final String parentIdentifier = connection.getParentIdentifier(); // Generate configuration from available data GuacamoleConfiguration config = new GuacamoleConfiguration(); @@ -232,7 +184,7 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS config.setProtocol(model.getProtocol()); // Set parameters from associated data - Collection parameters = parameterMapper.select(connection.getIdentifier()); + Collection parameters = parameterMapper.select(identifier); for (ParameterModel parameter : parameters) config.setParameter(parameter.getName(), parameter.getValue()); @@ -247,7 +199,8 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS try { // Record new active connection - addActiveConnection(connection, activeConnection); + activeConnections.put(identifier, activeConnection); + activeConnectionGroups.put(parentIdentifier, activeConnection); // Return newly-reserved connection return new ConfiguredGuacamoleSocket( @@ -265,7 +218,8 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS super.close(); // Release connection upon close - removeActiveConnection(connection, activeConnection); + activeConnections.remove(identifier, activeConnection); + activeConnectionGroups.remove(parentIdentifier, activeConnection); release(user, connection); UserModel userModel = user.getUser().getModel(); @@ -274,7 +228,7 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS // Copy user information and timestamps into new record recordModel.setUserID(userModel.getObjectID()); recordModel.setUsername(userModel.getIdentifier()); - recordModel.setConnectionIdentifier(connection.getIdentifier()); + recordModel.setConnectionIdentifier(identifier); recordModel.setStartDate(activeConnection.getStartDate()); recordModel.setEndDate(new Date()); @@ -291,7 +245,8 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS catch (GuacamoleException e) { // Atomically release access to connection - removeActiveConnection(connection, activeConnection); + activeConnections.remove(identifier, activeConnection); + activeConnectionGroups.remove(parentIdentifier, activeConnection); release(user, connection); throw e; @@ -314,18 +269,7 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS @Override public List getActiveConnections(Connection connection) { - synchronized (activeConnections) { - - String identifier = connection.getIdentifier(); - - // Get set of active connection records - LinkedList connections = activeConnections.get(identifier); - if (connections != null) - return Collections.unmodifiableList(connections); - - return Collections.EMPTY_LIST; - - } + return activeConnections.get(connection.getIdentifier()); } @Override @@ -362,8 +306,13 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS @Override public List getActiveConnections(ConnectionGroup connectionGroup) { - // STUB - return Collections.EMPTY_LIST; + + // If not a balancing group, assume no connections + if (connectionGroup.getType() != ConnectionGroup.Type.BALANCING) + return Collections.EMPTY_LIST; + + return activeConnectionGroups.get(connectionGroup.getIdentifier()); + } } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionMultimap.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionMultimap.java new file mode 100644 index 000000000..5c6213965 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionMultimap.java @@ -0,0 +1,128 @@ +/* + * 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.socket; + +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import org.glyptodon.guacamole.net.auth.ConnectionRecord; + + +/** + * Mapping of object identifiers to lists of connection records. Records are + * added or removed individually, and the overall list of current records + * associated with a given object can be retrieved at any time. The public + * methods of this class are all threadsafe. + * + * @author Michael Jumper + */ +public class ActiveConnectionMultimap { + + /** + * All active connections to a connection having a given identifier. + */ + private final Map> records = + new HashMap>(); + + /** + * Stores the given connection record in the list of active connections + * associated with the object having the given identifier. + * + * @param identifier + * The identifier of the object being connected to. + * + * @param record + * The record associated with the active connection. + */ + public void put(String identifier, ConnectionRecord record) { + synchronized (records) { + + // Get set of active connection records, creating if necessary + LinkedList connections = records.get(identifier); + if (connections == null) { + connections = new LinkedList(); + records.put(identifier, connections); + } + + // Add active connection + connections.addFirst(record); + + } + } + + /** + * Removes the given connection record from the list of active connections + * associated with the object having the given identifier. + * + * @param identifier + * The identifier of the object being disconnected from. + * + * @param record + * The record associated with the active connection. + */ + public void remove(String identifier, ConnectionRecord record) { + synchronized (records) { + + // Get set of active connection records + LinkedList connections = records.get(identifier); + assert(connections != null); + + // Remove old record + connections.remove(record); + + // If now empty, clean the tracking entry + if (connections.isEmpty()) + records.remove(identifier); + + } + } + + /** + * Returns the current list of active connection records associated with + * the object having the given identifier. The list will be sorted in + * ascending order of connection age. If there are no such connections, an + * empty list is returned. + * + * @param identifier + * The identifier of the object to check. + * + * @return + * An immutable list of records associated with the object having the + * given identifier, or an empty list if there are no such records. + */ + public List get(String identifier) { + synchronized (records) { + + // Get set of active connection records + LinkedList connections = records.get(identifier); + if (connections != null) + return Collections.unmodifiableList(connections); + + return Collections.EMPTY_LIST; + + } + } + +} From b6c36a12e50e8117b59b36cc49704079696b623f Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 1 Mar 2015 18:05:35 -0800 Subject: [PATCH 4/4] GUAC-1101: Manage active connection records using sets rather than lists. --- .../jdbc/connection/ConnectionService.java | 2 ++ .../AbstractGuacamoleSocketService.java | 4 +-- .../jdbc/socket/ActiveConnectionMultimap.java | 36 ++++++++++--------- .../jdbc/socket/GuacamoleSocketService.java | 28 ++++++++------- 4 files changed, 38 insertions(+), 32 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java index f7d0b5ac5..9bf566770 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java @@ -26,6 +26,7 @@ import com.google.inject.Inject; import com.google.inject.Provider; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -367,6 +368,7 @@ public class ConnectionService extends DirectoryObjectService records = new ArrayList(socketService.getActiveConnections(connection)); + Collections.reverse(records); // Add past connections from model objects for (ConnectionRecordModel model : models) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java index 78f4d61ca..d684f29c9 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java @@ -268,7 +268,7 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS } @Override - public List getActiveConnections(Connection connection) { + public Collection getActiveConnections(Connection connection) { return activeConnections.get(connection.getIdentifier()); } @@ -305,7 +305,7 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS } @Override - public List getActiveConnections(ConnectionGroup connectionGroup) { + public Collection getActiveConnections(ConnectionGroup connectionGroup) { // If not a balancing group, assume no connections if (connectionGroup.getType() != ConnectionGroup.Type.BALANCING) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionMultimap.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionMultimap.java index 5c6213965..e20ed733f 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionMultimap.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionMultimap.java @@ -22,11 +22,12 @@ package org.glyptodon.guacamole.auth.jdbc.socket; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; +import java.util.LinkedHashMap; import java.util.Map; +import java.util.Set; import org.glyptodon.guacamole.net.auth.ConnectionRecord; @@ -43,8 +44,8 @@ public class ActiveConnectionMultimap { /** * All active connections to a connection having a given identifier. */ - private final Map> records = - new HashMap>(); + private final Map> records = + new HashMap>(); /** * Stores the given connection record in the list of active connections @@ -60,14 +61,14 @@ public class ActiveConnectionMultimap { synchronized (records) { // Get set of active connection records, creating if necessary - LinkedList connections = records.get(identifier); + Set connections = records.get(identifier); if (connections == null) { - connections = new LinkedList(); + connections = Collections.newSetFromMap(new LinkedHashMap()); records.put(identifier, connections); } // Add active connection - connections.addFirst(record); + connections.add(record); } } @@ -86,7 +87,7 @@ public class ActiveConnectionMultimap { synchronized (records) { // Get set of active connection records - LinkedList connections = records.get(identifier); + Set connections = records.get(identifier); assert(connections != null); // Remove old record @@ -100,25 +101,26 @@ public class ActiveConnectionMultimap { } /** - * Returns the current list of active connection records associated with - * the object having the given identifier. The list will be sorted in - * ascending order of connection age. If there are no such connections, an - * empty list is returned. + * Returns a collection of active connection records associated with the + * object having the given identifier. The collection will be sorted in + * insertion order. If there are no such connections, an empty collection is + * returned. * * @param identifier * The identifier of the object to check. * * @return - * An immutable list of records associated with the object having the - * given identifier, or an empty list if there are no such records. + * An immutable collection of records associated with the object having + * the given identifier, or an empty collection if there are no such + * records. */ - public List get(String identifier) { + public Collection get(String identifier) { synchronized (records) { // Get set of active connection records - LinkedList connections = records.get(identifier); + Collection connections = records.get(identifier); if (connections != null) - return Collections.unmodifiableList(connections); + return Collections.unmodifiableCollection(connections); return Collections.EMPTY_LIST; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/GuacamoleSocketService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/GuacamoleSocketService.java index 31e240ba2..e8daf4653 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/GuacamoleSocketService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/GuacamoleSocketService.java @@ -22,7 +22,7 @@ package org.glyptodon.guacamole.auth.jdbc.socket; -import java.util.List; +import java.util.Collection; import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; import org.glyptodon.guacamole.auth.jdbc.connection.ModeledConnection; import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup; @@ -72,18 +72,19 @@ public interface GuacamoleSocketService { throws GuacamoleException; /** - * Returns a list containing connection records representing all currently- - * active connections using the given connection. These records will have - * usernames and start dates, but no end date. + * Returns a connection containing connection records representing all + * currently-active connections using the given connection. These records + * will have usernames and start dates, but no end date, and will be + * sorted in ascending order by start date. * * @param connection * The connection to check. * * @return - * A list containing connection records representing all currently- - * active connections. + * A connection containing connection records representing all + * currently-active connections. */ - public List getActiveConnections(Connection connection); + public Collection getActiveConnections(Connection connection); /** * Creates a socket for the given user which connects to the given @@ -116,17 +117,18 @@ public interface GuacamoleSocketService { throws GuacamoleException; /** - * Returns a list containing connection records representing all currently- - * active connections using the given connection group. These records will - * have usernames and start dates, but no end date. + * Returns a collection containing connection records representing all + * currently-active connections using the given connection group. These + * records will have usernames and start dates, but no end date, and will + * be sorted in ascending order by start date. * * @param connectionGroup * The connection group to check. * * @return - * A list containing connection records representing all currently- - * active connections. + * A collection containing connection records representing all + * currently-active connections. */ - public List getActiveConnections(ConnectionGroup connectionGroup); + public Collection getActiveConnections(ConnectionGroup connectionGroup); }