@Override
    @Transactional
    public GuacamoleTunnel getGuacamoleTunnel(ModeledAuthenticatedUser user,
            ModeledConnectionGroup connectionGroup,
            GuacamoleClientInformation info, Map<String, String> tokens)
            throws GuacamoleException {

        // Track failures in upstream (remote desktop) connections
        boolean upstreamHasFailed = false;

        // If group has no associated balanced connections, cannot connect
        List<ModeledConnection> connections = getBalancedConnections(user, connectionGroup);
        if (connections.isEmpty())
            throw new GuacamoleSecurityException("Permission denied.");

        do {

            // Acquire group
            acquire(user, connectionGroup);

            // Attempt to acquire to any child, including failover-only
            // connections only if at least one upstream failure has occurred
            ModeledConnection connection;
            try {
                connection = acquire(user, connections, upstreamHasFailed);
            }

            // Ensure connection group is always released if child acquire fails
            catch (GuacamoleException e) {
                release(user, connectionGroup);
                throw e;
            }

            try {

                // Connect to acquired child
                ActiveConnectionRecord connectionRecord = new ActiveConnectionRecord(connectionMap, user, connectionGroup, connection);
                GuacamoleTunnel tunnel = assignGuacamoleTunnel(connectionRecord,
                        info, tokens, connections.size() > 1);

                // If session affinity is enabled, prefer this connection going forward
                if (connectionGroup.isSessionAffinityEnabled())
                    user.preferConnection(connection.getIdentifier());

                // Warn if we are connecting to a failover-only connection
                if (connection.isFailoverOnly())
                    logger.warn("One or more normal connections within "
                            + "group \"{}\" have failed. Some connection "
                            + "attempts are being routed to designated "
                            + "failover-only connections.",
                            connectionGroup.getIdentifier());

                return tunnel;

            }

            // If connection failed due to an upstream error, retry other
            // connections
            catch (GuacamoleUpstreamException e) {
                logger.info("Upstream error intercepted for connection \"{}\". Failing over to next connection in group...", connection.getIdentifier());
                logger.debug("Upstream remote desktop reported an error during connection.", e);
                connections.remove(connection);
                upstreamHasFailed = true;
            }

        } while (!connections.isEmpty());

        // All connection possibilities have been exhausted
        throw new GuacamoleResourceConflictException("Cannot connect. All upstream connections are unavailable.");

    }
Returns a list of all balanced connections within a given connection
group. If the connection group is not balancing, or it contains no
connections, an empty list is returned.

@param user
    The user on whose behalf the balanced connections within the given
    connection group are being retrieved.

@param connectionGroup
    The connection group to retrieve the balanced connections of.

@return
    A list containing all balanced connections within the given group,
    or an empty list if there are no such connections.
extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java