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