/* * Copyright (c) 2015-2016, Christoph Engelbert (aka noctarius) and * contributors. All rights reserved. * * Licensed 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 com.noctarius.tengi.spi.pooling.impl; import com.noctarius.tengi.spi.pooling.ObjectHandler; import com.noctarius.tengi.spi.pooling.ObjectPool; import com.noctarius.tengi.spi.pooling.PooledObject; import org.junit.Test; import java.lang.reflect.Field; import java.util.Collections; import java.util.ConcurrentModificationException; import java.util.IdentityHashMap; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReferenceArray; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; public class NonBlockingObjectPoolTestCase { @Test public void test_acquire_null_activator_handler_action() throws Exception { ObjectPool<Value> pool = ObjectPool.create(new ObjectHandler<Value>() { @Override public Value create() { return new Value(); } @Override public void activateObject(Value object) { object.value = "test"; } }, 10); PooledObject<Value> valuePooledObject = pool.acquire(); assertNotNull(valuePooledObject); assertNotNull(valuePooledObject.getObject()); assertEquals("test", valuePooledObject.getObject().value); } @Test public void test_acquire_null_activator_handler_no_action() throws Exception { ObjectPool<Value> pool = ObjectPool.create(Value::new, 10); PooledObject<Value> valuePooledObject = pool.acquire(); assertNotNull(valuePooledObject); assertNotNull(valuePooledObject.getObject()); assertNull(valuePooledObject.getObject().value); } @Test public void test_acquire_non_null_activator_handler_action() throws Exception { ObjectPool<Value> pool = ObjectPool.create(new ObjectHandler<Value>() { @Override public Value create() { return new Value(); } @Override public void activateObject(Value object) { object.value = "test"; } }, 10); PooledObject<Value> valuePooledObject = pool.acquire((v) -> v.value = "test2"); assertNotNull(valuePooledObject); assertNotNull(valuePooledObject.getObject()); assertEquals("test", valuePooledObject.getObject().value); } @Test public void test_acquire_non_null_activator_handler_no_action() throws Exception { ObjectPool<Value> pool = ObjectPool.create(Value::new, 10); PooledObject<Value> valuePooledObject = pool.acquire((v) -> v.value = "test"); assertNotNull(valuePooledObject); assertNotNull(valuePooledObject.getObject()); assertEquals("test", valuePooledObject.getObject().value); } @Test public void test_release_null_handler_passivator_action() throws Exception { final Value value = new Value(); value.value = "test"; ObjectPool<Value> pool = ObjectPool.create(new ObjectHandler<Value>() { @Override public Value create() { return value; } @Override public void passivateObject(Value object) { object.value = null; } }, 10); PooledObject<Value> valuePooledObject = pool.acquire(); assertNotNull(valuePooledObject); assertNotNull(valuePooledObject.getObject()); assertEquals("test", valuePooledObject.getObject().value); pool.release(valuePooledObject); assertNull(value.value); } @Test public void test_release_null_passivator_handler_no_action() throws Exception { final Value value = new Value(); value.value = "test"; ObjectPool<Value> pool = ObjectPool.create(() -> value, 10); PooledObject<Value> valuePooledObject = pool.acquire(); assertNotNull(valuePooledObject); assertNotNull(valuePooledObject.getObject()); assertEquals("test", valuePooledObject.getObject().value); pool.release(valuePooledObject); assertEquals("test", value.value); } @Test public void test_release_non_null_passivator_handler_action() throws Exception { final Value value = new Value(); value.value = "test"; ObjectPool<Value> pool = ObjectPool.create(new ObjectHandler<Value>() { @Override public Value create() { return value; } @Override public void passivateObject(Value object) { object.value = null; } }, 10); PooledObject<Value> valuePooledObject = pool.acquire(); assertNotNull(valuePooledObject); assertNotNull(valuePooledObject.getObject()); assertEquals("test", valuePooledObject.getObject().value); pool.release(valuePooledObject, (v) -> v.value = "test2"); assertNull(value.value); } @Test public void test_release_non_null_passivator_handler_no_action() throws Exception { final Value value = new Value(); value.value = "test"; ObjectPool<Value> pool = ObjectPool.create(() -> value, 10); PooledObject<Value> valuePooledObject = pool.acquire(); assertNotNull(valuePooledObject); assertNotNull(valuePooledObject.getObject()); assertEquals("test", valuePooledObject.getObject().value); pool.release(valuePooledObject, (v) -> v.value = "test2"); assertEquals("test2", value.value); } @Test public void test_concurrent_acquire() throws Throwable { int concurrencyLevel = 100; ObjectPool<Value> pool = ObjectPool.create(Value::new, concurrencyLevel); AtomicReferenceArray<Object> results = new AtomicReferenceArray<>(concurrencyLevel); Semaphore start = new Semaphore(concurrencyLevel); CountDownLatch end = new CountDownLatch(concurrencyLevel); start.acquire(concurrencyLevel); for (int i = 0; i < concurrencyLevel; i++) { int index = i; new Thread(() -> { try { start.acquire(); PooledObject<Value> valuePooledObject = pool.acquire(); assertTrue(!valuePooledObject.toString().contains("INTERMEDIATE")); results.set(index, valuePooledObject.getObject()); } catch (Throwable e) { results.set(index, e); } finally { end.countDown(); } }).start(); } start.release(concurrencyLevel); end.await(20, TimeUnit.SECONDS); Set<Value> values = Collections.newSetFromMap(new IdentityHashMap<>()); for (int i = 0; i < concurrencyLevel; i++) { Object result = results.get(i); if (result instanceof Throwable) { throw (Throwable) result; } values.add((Value) result); } assertEquals(concurrencyLevel, values.size()); } @Test public void test_acquire_validator_valid() throws Exception { ObjectPool<Value> pool = ObjectPool.create(Value::new, (v) -> true, 2); PooledObject<Value> obj1 = pool.acquire(); assertTrue(!obj1.toString().contains("INTERMEDIATE")); PooledObject<Value> obj2 = pool.acquire(); assertTrue(!obj2.toString().contains("INTERMEDIATE")); pool.release(obj1); PooledObject<Value> obj3 = pool.acquire(); assertTrue(!obj3.toString().contains("INTERMEDIATE")); assertSame(obj1, obj3); } @Test public void test_acquire_validator_invalid() throws Exception { ObjectPool<Value> pool = ObjectPool.create(Value::new, (v) -> false, 2); PooledObject<Value> obj1 = pool.acquire(); assertTrue(!obj1.toString().contains("INTERMEDIATE")); PooledObject<Value> obj2 = pool.acquire(); assertTrue(!obj2.toString().contains("INTERMEDIATE")); pool.release(obj1); PooledObject<Value> obj3 = pool.acquire(); assertTrue(!obj3.toString().contains("INTERMEDIATE")); assertNotSame(obj1, obj3); } @Test public void test_concurrent_release() throws Throwable { int concurrencyLevel = 100; ObjectPool<Value> pool = ObjectPool.create(Value::new, concurrencyLevel); PooledObject<Value>[] results = new PooledObject[concurrencyLevel]; AtomicReferenceArray<Throwable> exceptions = new AtomicReferenceArray<>(concurrencyLevel); Semaphore start = new Semaphore(concurrencyLevel); CountDownLatch end = new CountDownLatch(concurrencyLevel); start.acquire(concurrencyLevel); for (int i = 0; i < concurrencyLevel; i++) { int index = i; PooledObject<Value> valuePooledObject = pool.acquire(); assertTrue(!valuePooledObject.toString().contains("INTERMEDIATE")); results[i] = valuePooledObject; new Thread(() -> { try { start.acquire(); pool.release(valuePooledObject); } catch (Throwable e) { exceptions.set(index, e); } finally { end.countDown(); } }).start(); } start.release(concurrencyLevel); end.await(20, TimeUnit.SECONDS); for (int i = 0; i < concurrencyLevel; i++) { Throwable throwable = exceptions.get(i); if (throwable != null) { throw throwable; } assertTrue(results[i].toString().contains("FREE")); } } @Test public void test_acquire_but_full_intermediate_entry() throws Exception { ObjectPool<Value> pool = ObjectPool.create(Value::new, 2); PooledObject<Value> obj1 = pool.acquire(); assertTrue(!obj1.toString().contains("INTERMEDIATE")); PooledObject<Value> obj2 = pool.acquire(); assertTrue(!obj2.toString().contains("INTERMEDIATE")); PooledObject<Value> obj3 = pool.acquire(); assertNotNull(obj3); assertTrue(obj3.toString().contains("INTERMEDIATE")); } @Test public void test_release_intermediate_entry() throws Exception { ObjectPool<Value> pool = ObjectPool.create(Value::new, 2); PooledObject<Value> obj1 = pool.acquire(); assertTrue(!obj1.toString().contains("INTERMEDIATE")); PooledObject<Value> obj2 = pool.acquire(); assertTrue(!obj2.toString().contains("INTERMEDIATE")); PooledObject<Value> obj3 = pool.acquire(); assertNotNull(obj3); assertTrue(obj3.toString().contains("INTERMEDIATE")); pool.release(obj3); PooledObject<Value> obj4 = pool.acquire(); assertNotNull(obj4); assertTrue(obj4.toString().contains("INTERMEDIATE")); assertNotSame(obj3, obj4); } @Test(expected = IllegalStateException.class) public void test_close() throws Exception { ObjectPool<Value> pool = ObjectPool.create(Value::new, 2); PooledObject<Value> obj = pool.acquire(); assertNotNull(obj); pool.release(obj); pool.close(); pool.acquire(); } @Test(expected = IllegalArgumentException.class) public void test_release_null_object() throws Exception { ObjectPool<Value> pool = ObjectPool.create(Value::new, 2); pool.release(null); } @Test(expected = IllegalArgumentException.class) public void test_release_wrong_typed_object() throws Exception { ObjectPool<Value> pool = ObjectPool.create(Value::new, 2); pool.release(new WrongPooledObject<>()); } @Test(expected = ConcurrentModificationException.class) public void test_release_already_free_object() throws Exception { ObjectPool<Value> pool = ObjectPool.create(Value::new, 2); PooledObject<Value> pooledObject = pool.acquire(); Field field = NonBlockingObjectPool.Entry.class.getDeclaredField("state"); field.setAccessible(true); field.set(pooledObject, 0); pool.release(pooledObject); } private static class Value { private String value; } private static class WrongPooledObject<T> implements PooledObject<T> { @Override public T getObject() { return null; } } }