/** * AnalyzerBeans * Copyright (C) 2014 Neopost - Customer Information Management * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program 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 Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.eobjects.analyzer.util; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import junit.framework.TestCase; public class UsageAwareCloseableTest extends TestCase { private UsageAwareCloseable _closeable; private AtomicInteger _closedCounter; private AtomicInteger _createdCounter; private AtomicInteger _actionCounter; @Override protected void setUp() throws Exception { super.setUp(); _createdCounter = new AtomicInteger(0); _closedCounter = new AtomicInteger(0); _actionCounter = new AtomicInteger(0); _closeable = createCloseable(); } private UsageAwareCloseable createCloseable() { _createdCounter.incrementAndGet(); return new UsageAwareCloseable() { @Override protected void closeInternal() { _closedCounter.incrementAndGet(); } }; } public void testSimple() throws Exception { assertFalse(_closeable.isClosed()); assertEquals(0, _closedCounter.get()); _closeable.close(); assertEquals(1, _closedCounter.get()); assertTrue(_closeable.isClosed()); } public void testStackedUsage() throws Exception { assertFalse(_closeable.isClosed()); assertEquals(0, _closedCounter.get()); assertTrue(_closeable.requestUsage()); assertTrue(_closeable.requestUsage()); assertFalse(_closeable.isClosed()); assertEquals(0, _closedCounter.get()); _closeable.close(); _closeable.close(); assertFalse(_closeable.isClosed()); assertEquals(0, _closedCounter.get()); _closeable.close(); assertTrue(_closeable.isClosed()); assertEquals(1, _closedCounter.get()); } public void testCloseMultipleTimes() throws Exception { assertFalse(_closeable.isClosed()); assertEquals(0, _closedCounter.get()); _closeable.close(); assertEquals(1, _closedCounter.get()); assertTrue(_closeable.isClosed()); _closeable.close(); assertEquals(1, _closedCounter.get()); assertTrue(_closeable.isClosed()); } public void testMultiThreadedUse() throws Throwable { // start with a closed instance _closeable.close(); assertTrue(_closeable.isClosed()); assertEquals(1, _closedCounter.get()); _createdCounter.set(0); _closedCounter.set(0); final int numThreads = 3; final int numInternalUsages = 500; ExecutorService threadPool = Executors.newFixedThreadPool(numThreads); final Future<?>[] futures = new Future[numThreads]; for (int i = 0; i < numThreads; i++) { final int threadNumber = i + 1; Runnable runnable = new Runnable() { @Override public void run() { for (int j = 0; j < numInternalUsages; j++) { String action = "Thread" + threadNumber + "_" + numInternalUsages + "_" + _actionCounter.incrementAndGet(); System.out.println("Usage (B" + action + "): " + _closeable.getUsageCount() + ", Creates: " + _createdCounter.get() + ", Closes: " + _closedCounter.get()); try (UsageAwareCloseable closeable = giveMeUsage(action)) { // do something System.out.println("Usage (A" + action + "): " + _closeable.getUsageCount() + ", Creates: " + _createdCounter.get() + ", Closes: " + _closedCounter.get()); } } } }; futures[i] = threadPool.submit(runnable); } for (int i = 0; i < numThreads; i++) { try { futures[i].get(); } catch (ExecutionException e) { throw e.getCause(); } } threadPool.shutdown(); assertEquals(_closedCounter.get(), _createdCounter.get()); assertTrue("Closed below expected: " + _closedCounter, _closedCounter.get() > 3); } // will either return the existing or create a new closable for usage. private UsageAwareCloseable giveMeUsage(String action) { final UsageAwareCloseable closeable = _closeable; final boolean useExisting = closeable.requestUsage(); if (useExisting) { int closed = _closedCounter.get(); int created = _createdCounter.get(); assertEquals("Action: " + action + ", Closed: " + closed + ", Created: " + created, closed + 1, created); return closeable; } synchronized (this) { if (_closeable.isClosed()) { int closed = _closedCounter.get(); int created = _createdCounter.get(); assertEquals("Action: " + action + ", Closed: " + closed + ", Created: " + created, closed, created); _closeable = createCloseable(); return _closeable; } } // repeat (still unsynchronized) return giveMeUsage(action); } }