mirror of
				https://github.com/gyurix1968/guacamole-client.git
				synced 2025-10-31 00:53:21 +00:00 
			
		
		
		
	GUAC-1101: Implement reserved concurrency policy.
This commit is contained in:
		| @@ -24,9 +24,12 @@ package org.glyptodon.guacamole.auth.jdbc.socket; | ||||
|  | ||||
| import com.google.inject.Singleton; | ||||
| import java.util.List; | ||||
| import java.util.concurrent.ConcurrentHashMap; | ||||
| import java.util.concurrent.ConcurrentMap; | ||||
| 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; | ||||
|  | ||||
|  | ||||
| /** | ||||
| @@ -43,17 +46,138 @@ import org.glyptodon.guacamole.GuacamoleException; | ||||
| public class ReservedGuacamoleSocketService | ||||
|     extends AbstractGuacamoleSocketService { | ||||
|  | ||||
|     /** | ||||
|      * An arbitrary number of reservations associated with a specific user. | ||||
|      * Initially, each Reservation instance represents exactly one reservation, | ||||
|      * but future calls to acquire() may increase this value. Once the | ||||
|      * reservation count is reduced to zero by calls to release(), a | ||||
|      * Reservation instance is empty and cannot be reused. It must be discarded | ||||
|      * and replaced with a fresh Reservation. | ||||
|      *  | ||||
|      * This is necessary as each Reservation will be stored within a Map, and | ||||
|      * the effect of acquire() must be deterministic. If Reservations could be | ||||
|      * reused, the internal count could potentially increase after being | ||||
|      * removed from the map, resulting in a successful acquire() that really | ||||
|      * should have failed. | ||||
|      */ | ||||
|     private static class Reservation { | ||||
|  | ||||
|         /** | ||||
|          * The username of the user associated with this reservation. | ||||
|          */ | ||||
|         private final String username; | ||||
|  | ||||
|         /** | ||||
|          * The number of reservations effectively present under the associated | ||||
|          * username. | ||||
|          */ | ||||
|         private int count = 1; | ||||
|  | ||||
|         /** | ||||
|          * Creates a new reservation which tracks the overall number of | ||||
|          * reservations for a given user. | ||||
|          * @param username  | ||||
|          */ | ||||
|         public Reservation(String username) { | ||||
|             this.username = username; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Attempts to acquire a new reservation under the given username. If | ||||
|          * this reservation is for a different user, or the reservation has | ||||
|          * expired, this will fail. | ||||
|          * | ||||
|          * @param username | ||||
|          *     The username of the user to acquire the reservation for. | ||||
|          * | ||||
|          * @return | ||||
|          *     true if the reservation was successful, false otherwise. | ||||
|          */ | ||||
|         public boolean acquire(String username) { | ||||
|  | ||||
|             // Acquire always fails if for the wrong user | ||||
|             if (!this.username.equals(username)) | ||||
|                 return false; | ||||
|  | ||||
|             // Determine success/failure based on count | ||||
|             synchronized (this) { | ||||
|  | ||||
|                 // If already expired, no further reservations are allowed | ||||
|                 if (count == 0) | ||||
|                     return false; | ||||
|  | ||||
|                 // Otherwise, add another reservation, report success | ||||
|                 count++; | ||||
|                 return true; | ||||
|                  | ||||
|             } | ||||
|              | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Releases a previous reservation. The result of calling this function | ||||
|          * without a previous matching call to acquire is undefined. | ||||
|          * | ||||
|          * @return | ||||
|          *     true if the last reservation has been released and this | ||||
|          *     reservation is now empty, false otherwise. | ||||
|          */ | ||||
|         public boolean release() { | ||||
|             synchronized (this) { | ||||
|  | ||||
|                 // Reduce reservation count | ||||
|                 count--; | ||||
|  | ||||
|                 // Empty if no reservations remain | ||||
|                 return count == 0; | ||||
|                  | ||||
|             } | ||||
|         } | ||||
|          | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Map of connection identifier to associated reservations. | ||||
|      */ | ||||
|     private final ConcurrentMap<String, Reservation> reservations = | ||||
|             new ConcurrentHashMap<String, Reservation>(); | ||||
|      | ||||
|     @Override | ||||
|     protected ModeledConnection acquire(AuthenticatedUser user, | ||||
|             List<ModeledConnection> connections) throws GuacamoleException { | ||||
|         // STUB | ||||
|         throw new UnsupportedOperationException("STUB"); | ||||
|  | ||||
|         String username = user.getUser().getIdentifier(); | ||||
|          | ||||
|         // Return the first successfully-reserved connection | ||||
|         for (ModeledConnection connection : connections) { | ||||
|  | ||||
|             String identifier = connection.getIdentifier(); | ||||
|  | ||||
|             // Attempt to reserve connection, return if successful | ||||
|             Reservation reservation = reservations.putIfAbsent(identifier, new Reservation(username)); | ||||
|             if (reservation == null || reservation.acquire(username)) | ||||
|                 return connection; | ||||
|  | ||||
|         } | ||||
|  | ||||
|         // Already in use | ||||
|         throw new GuacamoleResourceConflictException("Cannot connect. This connection is in use."); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void release(AuthenticatedUser user, ModeledConnection connection) { | ||||
|         // STUB | ||||
|         throw new UnsupportedOperationException("STUB"); | ||||
|  | ||||
|         String identifier = connection.getIdentifier(); | ||||
|          | ||||
|         // Retrieve active reservation (which must exist) | ||||
|         Reservation reservation = reservations.get(identifier); | ||||
|         assert(reservation != null); | ||||
|  | ||||
|         // Release reservation, remove from map if empty | ||||
|         if (reservation.release()) | ||||
|             reservations.remove(identifier); | ||||
|          | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user