SimpleSmartObjectPool.java
/*
* Copyright (c) 2001, Zoltan Farkas All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.spf4j.pool.impl;
import com.google.common.collect.LinkedHashMultimap;
import org.spf4j.base.Throwables;
import org.spf4j.base.Pair;
import org.spf4j.pool.ObjectBorower;
import org.spf4j.pool.ObjectCreationException;
import org.spf4j.pool.ObjectDisposeException;
import org.spf4j.pool.ObjectPool;
import org.spf4j.pool.SmartObjectPool;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
*
* @author zoly
*/
public final class SimpleSmartObjectPool<T> implements SmartObjectPool<T> {
private int maxSize;
private final LinkedHashMultimap<ObjectBorower<T>, T> borrowedObjects = LinkedHashMultimap.create();
private final List<T> availableObjects = new ArrayList<T>();
private final ReentrantLock lock;
private final Condition available;
private final ObjectPool.Factory<T> factory;
private final long timeoutMillis;
public SimpleSmartObjectPool(final int initialSize, final int maxSize,
final ObjectPool.Factory<T> factory, final long timeoutMillis, final boolean fair)
throws ObjectCreationException {
this.maxSize = maxSize;
this.factory = factory;
this.timeoutMillis = timeoutMillis;
this.lock = new ReentrantLock(fair);
this.available = this.lock.newCondition();
for (int i = 0; i < initialSize; i++) {
availableObjects.add(factory.create());
}
}
@Override
public T borrowObject(final ObjectBorower borower) throws InterruptedException,
TimeoutException, ObjectCreationException {
lock.lock();
try {
if (availableObjects.size() > 0) {
Iterator<T> it = availableObjects.iterator();
T object = it.next();
it.remove();
borrowedObjects.put(borower, object);
return object;
} else if (borrowedObjects.size() < maxSize) {
T object = factory.create();
borrowedObjects.put(borower, object);
return object;
} else {
if (borrowedObjects.isEmpty()) {
throw new RuntimeException("Pool size is probably closing down or is missconfigured withe size 0");
}
for (ObjectBorower<T> b : borrowedObjects.keySet()) {
if (borower != b) {
T object = b.returnObjectIfNotInUse();
if (object != null) {
if (!borrowedObjects.remove(b, object)) {
throw new IllegalStateException("Returned Object hasn't been borrowed " + object);
}
borrowedObjects.put(borower, object);
return object;
}
}
}
Object object;
do {
Iterator<ObjectBorower<T>> itt = borrowedObjects.keySet().iterator();
ObjectBorower<T> b = itt.next();
while (b == borower && itt.hasNext()) {
b = itt.next();
}
if (b == borower) {
throw new IllegalStateException("Borrower " + b + " already has "
+ "max number of pool objects");
}
do {
object = b.requestReturnObject();
if (object != null && object != ObjectBorower.REQUEST_MADE) {
if (!borrowedObjects.remove(b, object)) {
throw new IllegalStateException("Returned Object hasn't been borrowed " + object);
}
borrowedObjects.put(borower, (T) object);
return (T) object;
}
//CHECKSTYLE:OFF -- inner assignement
} while (object != ObjectBorower.REQUEST_MADE && (itt.hasNext() && ((b = itt.next()) != null)));
//CHECKSTYLE:ON
} while (object != ObjectBorower.REQUEST_MADE);
while (availableObjects.isEmpty()) {
if (!available.await(timeoutMillis, TimeUnit.MILLISECONDS)) {
throw new TimeoutException("Object wait timeout expired " + timeoutMillis);
}
}
Iterator<T> it = availableObjects.iterator();
object = it.next();
it.remove();
borrowedObjects.put(borower, (T) object);
return (T) object;
}
} finally {
lock.unlock();
}
}
@Override
public void returnObject(final T object, final ObjectBorower borower) {
lock.lock();
try {
borrowedObjects.remove(borower, object);
availableObjects.add(object);
available.signalAll();
} finally {
lock.unlock();
}
}
@Override
public void dispose() throws ObjectDisposeException {
lock.lock();
try {
maxSize = 0;
List<Pair<ObjectBorower<T>, T>> returnedObjects = new ArrayList<Pair<ObjectBorower<T>, T>>();
for (ObjectBorower<T> b : borrowedObjects.keySet()) {
T object = (T) b.requestReturnObject();
if (object != null) {
returnedObjects.add(Pair.of(b, object));
}
}
for (Pair<ObjectBorower<T>, T> objectAndBorrower : returnedObjects) {
T object = objectAndBorrower.getSecond();
if (object != ObjectBorower.REQUEST_MADE) {
if (!borrowedObjects.remove(objectAndBorrower.getFirst(), object)) {
throw new IllegalStateException("Returned Object hasn't been borrowed " + object);
}
availableObjects.add(object);
}
}
ObjectDisposeException exception = disposeReturnedObjects(null);
while (!borrowedObjects.isEmpty()) {
if (!available.await(timeoutMillis, TimeUnit.MILLISECONDS)) {
throw new TimeoutException("Object wait timeout expired " + timeoutMillis);
}
disposeReturnedObjects(exception);
}
if (exception != null) {
throw exception;
}
} catch (Exception e) {
throw new ObjectDisposeException(e);
} finally {
lock.unlock();
}
}
private ObjectDisposeException disposeReturnedObjects(final ObjectDisposeException exception) {
ObjectDisposeException result = exception;
for (T obj : availableObjects) {
try {
factory.dispose(obj);
} catch (ObjectDisposeException ex) {
result = Throwables.suppress(ex, result);
} catch (Exception ex) {
result = Throwables.suppress(new ObjectDisposeException(ex), result);
}
}
availableObjects.clear();
return result;
}
@Override
public boolean scan(final ScanHandler<T> handler) throws Exception {
lock.lock();
try {
for (ObjectBorower<T> objectBorower : borrowedObjects.keySet()) {
try {
if (!objectBorower.scan(handler)) {
return false;
}
} finally {
Collection<T> returned = objectBorower.returnObjectsIfNotNeeded();
if (returned != null) {
for (T ro : returned) {
if (!borrowedObjects.remove(objectBorower, ro)) {
throw new IllegalStateException("Object returned hasn't been borrowed" + ro);
}
availableObjects.add(ro);
}
}
}
}
for (T object : availableObjects) {
if (!handler.handle(object)) {
return false;
}
}
return true;
} finally {
lock.unlock();
}
}
public void requestReturnFromBorrowersIfNotInUse() {
lock.lock();
try {
List<Pair<ObjectBorower<T>, T>> returnedObjects = new ArrayList<Pair<ObjectBorower<T>, T>>();
for (ObjectBorower<T> b : borrowedObjects.keySet()) {
Collection<T> objects = b.returnObjectsIfNotInUse();
if (objects != null) {
for (T object : objects) {
returnedObjects.add(Pair.of(b, object));
}
}
}
for (Pair<ObjectBorower<T>, T> ro : returnedObjects) {
T object = ro.getSecond();
borrowedObjects.remove(ro.getFirst(), object);
availableObjects.add(object);
}
} finally {
lock.unlock();
}
}
@Override
public String toString() {
lock.lock();
try {
return "SimpleSmartObjectPool{" + "maxSize=" + maxSize + ", borrowedObjects="
+ borrowedObjects.values() + ", returnedObjects=" + availableObjects
+ ", factory=" + factory + ", timeoutMillis=" + timeoutMillis + '}';
} finally {
lock.unlock();
}
}
}