/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.brooklyn.util.pool;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable;
import org.apache.brooklyn.util.pool.BasicPool;
import org.apache.brooklyn.util.pool.Lease;
import org.apache.brooklyn.util.pool.Pool;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import org.testng.internal.annotations.Sets;
import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
public class BasicPoolTest {
private List<Integer> closedVals;
private Supplier<Integer> supplier;
Function<Integer,Void> closer;
private ListeningExecutorService executor;
@BeforeMethod(alwaysRun=true)
public void setUp() throws Exception {
// Note we use final theCounter variable, rather than just a field, to guarantee
// that each sequential test run won't be accessing the same field value if a test
// doesn't tear down and keeps executing in another thread for some reason.
final AtomicInteger theCounter = new AtomicInteger(0);
closedVals = new CopyOnWriteArrayList<Integer>();
supplier = new Supplier<Integer>() {
@Override public Integer get() {
return theCounter.getAndIncrement();
}
};
closer = new Function<Integer,Void>() {
@Override public Void apply(@Nullable Integer input) {
closedVals.add(input);
return null;
}
};
executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
}
@AfterMethod(alwaysRun=true)
public void tearDown() throws Exception {
if (executor != null) executor.shutdownNow();
}
@Test
public void testGeneratesNewValuesWhenRequired() throws Exception {
Pool<Integer> pool = BasicPool.<Integer>builder().supplier(supplier).build();
Lease<Integer> lease1 = pool.leaseObject();
assertEquals(lease1.leasedObject(), (Integer)0);
Lease<Integer> lease2 = pool.leaseObject();
assertEquals(lease2.leasedObject(), (Integer)1);
}
@Test
public void testReusesReturnedVals() throws Exception {
Pool<Integer> pool = BasicPool.<Integer>builder().supplier(supplier).build();
Lease<Integer> lease1 = pool.leaseObject();
assertEquals(lease1.leasedObject(), (Integer)0);
lease1.close();
Lease<Integer> lease2 = pool.leaseObject();
assertEquals(lease2.leasedObject(), (Integer)0);
}
@Test
public void testGeneratesNewIfOnlyUnviableValsInPool() throws Exception {
Pool<Integer> pool = BasicPool.<Integer>builder().supplier(supplier).viabilityChecker(Predicates.alwaysFalse()).closer(closer).build();
Lease<Integer> lease1 = pool.leaseObject();
assertEquals(lease1.leasedObject(), (Integer)0);
lease1.close();
Lease<Integer> lease2 = pool.leaseObject();
assertEquals(lease2.leasedObject(), (Integer)1);
// Expect the "unviable" resource to have been closed
assertEquals(closedVals, ImmutableList.of(0));
}
@Test
public void testReusesOnlyViableVals() throws Exception {
Pool<Integer> pool = BasicPool.<Integer>builder().supplier(supplier).viabilityChecker(Predicates.equalTo(1)).build();
Lease<Integer> lease1 = pool.leaseObject();
Lease<Integer> lease2 = pool.leaseObject();
Lease<Integer> lease3 = pool.leaseObject();
lease1.close();
lease2.close();
lease3.close();
Lease<Integer> lease4 = pool.leaseObject();
assertEquals(lease4.leasedObject(), (Integer)1);
}
@Test
public void testClosesResourcesInPool() throws Exception {
Pool<Integer> pool = BasicPool.<Integer>builder().supplier(supplier).closer(closer).build();
Lease<Integer> lease1 = pool.leaseObject();
Lease<Integer> lease2 = pool.leaseObject();
lease1.close();
lease2.close();
pool.close();
assertEquals(closedVals, ImmutableList.of(0, 1));
}
@Test
public void testClosesResourceReturnedAfterPoolIsClosed() throws Exception {
Pool<Integer> pool = BasicPool.<Integer>builder().supplier(supplier).closer(closer).build();
Lease<Integer> lease1 = pool.leaseObject();
pool.close();
assertEquals(closedVals, ImmutableList.of());
lease1.close();
assertEquals(closedVals, ImmutableList.of(lease1.leasedObject()));
}
@Test
public void testDoesNotReuseUnviableVals() throws Exception {
Pool<Integer> pool = BasicPool.<Integer>builder().supplier(supplier).viabilityChecker(Predicates.alwaysFalse()).build();
Lease<Integer> lease1 = pool.leaseObject();
assertEquals(lease1.leasedObject(), (Integer)0);
lease1.close();
Lease<Integer> lease2 = pool.leaseObject();
assertEquals(lease2.leasedObject(), (Integer)1);
}
@Test
public void testConcurrentCallsNeverHaveSameVal() throws Exception {
final Pool<Integer> pool = BasicPool.<Integer>builder().supplier(supplier).build();
final Set<Lease<Integer>> leases = Collections.newSetFromMap(new ConcurrentHashMap<Lease<Integer>, Boolean>());
List<ListenableFuture<?>> futures = Lists.newArrayList();
for (int i = 0; i < 1000; i++) {
futures.add(executor.submit(new Runnable() {
public void run() {
leases.add(pool.leaseObject());
}
}));
}
Futures.allAsList(futures).get();
Set<Integer> currentlyLeased = Sets.newLinkedHashSet();
for (Lease<Integer> lease : leases) {
boolean val = currentlyLeased.add(lease.leasedObject());
if (!val) fail("duplicate="+lease.leasedObject()+"; vals="+leases);
}
}
}