/* Copyright (c) 2013-2014 Boundless and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Distribution License v1.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/org/documents/edl-v10.html
 *
 * Contributors:
 * David Winslow (Boundless) - initial implementation
 */
package org.locationtech.geogig.storage;

import java.util.HashMap;
import java.util.Map;

/**
 * A connection manager for ensuring that connections are acquired or released
 * in a threadsafe way. The manager is parametric in A, the type of Address
 * specifying a connection, and C, the actual connection type.
 * 
 * The address type A should be suitable for use as a map key (that is, have
 * value-based equals() and hashCode() implementations which are consistent with
 * each other.)
 * 
 * Implementors should use the @Singleton scope with this class when configuring
 * Guice.
 */
public abstract class ConnectionManager<A, C> {
    protected abstract C connect(A address);

    protected abstract void disconnect(C connection);

    private static class PoolEntry<C> {
        public final C connection;
        public int clients;

        public PoolEntry(C connection) {
            this.connection = connection;
        }
    }

    private Map<A, PoolEntry<C>> pool = new HashMap<A, PoolEntry<C>>();

    private Map.Entry<A, PoolEntry<C>> lookupConnection(C connection) {
        for (Map.Entry<A, PoolEntry<C>> entry : pool.entrySet()) {
            if (entry.getValue().connection == connection) {
                return entry;
            }
        }
        throw new IllegalStateException(
                "Attempted to retrieve connection that is not managed by this manager");
    }

    public final synchronized C acquire(A address) {
        PoolEntry<C> entry = pool.get(address);
        if (entry == null) {
            C connection = connect(address);
            entry = new PoolEntry<C>(connection);
            pool.put(address, entry);
        }
        entry.clients += 1;
        return entry.connection;
    }

    public final synchronized void release(C connection) {
        Map.Entry<A, PoolEntry<C>> record = lookupConnection(connection);
        A address = record.getKey();
        PoolEntry<C> poolentry = record.getValue();
        poolentry.clients -= 1;
        if (poolentry.clients < 0)
            throw new IllegalStateException(
                    "Negative client count for connection pool entry!");
        if (poolentry.clients == 0) {
            try {
                disconnect(poolentry.connection);
            } finally {
                pool.remove(address);
            }
        }
    }
}

@@ -440,20 +440,16 @@
 va.util.
-Hash
 Map;%0Aimp
@@ -458,24 +458,49 @@
 t java.util.
+concurrent.ConcurrentHash
 Map;%0A%0A/**%0A *
@@ -575,19 +575,16 @@
 released
-%0A *
  in a th
@@ -596,16 +596,19 @@
 afe way.
+%0A *
  The man
@@ -651,19 +651,16 @@
  Address
-%0A *
  specify
@@ -694,16 +694,19 @@
 e actual
+%0A *
  connect
@@ -795,19 +795,16 @@
 is, have
-%0A *
  value-b
@@ -816,16 +816,19 @@
 equals()
+%0A *
  and has
@@ -876,19 +876,16 @@
 ent with
-%0A *
  each ot
@@ -974,19 +974,16 @@
 figuring
-%0A *
  Guice.%0A
@@ -1202,32 +1202,33 @@
 l C connection;%0A
+%0A
         public i
@@ -1388,31 +1388,26 @@
 new 
-HashMap%3CA, PoolEntry%3CC%3E
+ConcurrentHashMap%3C
 %3E();
@@ -2477,29 +2477,8 @@
 ion(
-%0A                    
 %22Neg

Move ConnectionManager to core