/* 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);
            }
        }
    }
}

/* 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.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 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 ConcurrentHashMap<>();

    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);
            }
        }
    }
}

Move ConnectionManager to core