/* * Copyright (C) 2011-2014 Chris Vest (mr.chrisvest@gmail.com) * * 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 stormpot; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import static java.util.Collections.synchronizedList; public class AlloKit { public interface Action { GenericPoolable apply(Slot slot, GenericPoolable obj) throws Exception; } public interface CountingAllocator extends Allocator<GenericPoolable> { int countAllocations(); int countDeallocations(); List<GenericPoolable> getAllocations(); List<GenericPoolable> getDeallocations(); void clearLists(); } public interface CountingReallocator extends CountingAllocator, Reallocator<GenericPoolable> { int countReallocations(); List<GenericPoolable> getReallocations(); } static class ActionSeq { private final Action[] actions; private int counter; ActionSeq(Action... actions) { this.actions = actions; counter = 0; } GenericPoolable applyNext(Slot slot, GenericPoolable obj) throws Exception { Action action = actions[counter]; counter = Math.min(actions.length - 1, counter + 1); return action.apply(slot, obj); } } public static class OnAllocation extends ActionSeq { OnAllocation(Action... actions) { super(actions); } } public static class OnReallocation extends ActionSeq { OnReallocation(Action... actions) { super(actions); } } public static class OnDeallocation extends ActionSeq { OnDeallocation(Action... actions) { super(actions); } } private static class CountingAllocatorImpl implements CountingAllocator { private final OnAllocation onAllocation; private final OnDeallocation onDeallocation; private final List<GenericPoolable> allocations; private final List<GenericPoolable> deallocations; private CountingAllocatorImpl( OnAllocation onAllocation, OnDeallocation onDeallocation) { this.onAllocation = onAllocation; this.onDeallocation = onDeallocation; allocations = synchronizedList(new ArrayList<>()); deallocations = synchronizedList(new ArrayList<>()); } @Override public int countAllocations() { return allocations.size(); } @Override public int countDeallocations() { return deallocations.size(); } @Override public List<GenericPoolable> getAllocations() { return allocations; } @Override public List<GenericPoolable> getDeallocations() { return deallocations; } @Override public void clearLists() { allocations.clear(); deallocations.clear(); } @Override public GenericPoolable allocate(Slot slot) throws Exception { assert slot != null : "Slot cannot be null in allocate"; GenericPoolable obj = onAllocation.applyNext(slot, null); allocations.add(obj); return obj; } @Override public void deallocate(GenericPoolable poolable) throws Exception { // no not-null assertion because exceptions from deallocate are ignored deallocations.add(poolable); onDeallocation.applyNext(null, poolable); } } private static class CountingReallocatorImpl extends CountingAllocatorImpl implements CountingReallocator { private final OnReallocation onReallocation; private final List<GenericPoolable> reallocations; private CountingReallocatorImpl( OnAllocation onAllocation, OnDeallocation onDeallocation, OnReallocation onReallocation) { super(onAllocation, onDeallocation); this.onReallocation = onReallocation; reallocations = synchronizedList(new ArrayList<>()); } @Override public int countReallocations() { return reallocations.size(); } @Override public List<GenericPoolable> getReallocations() { return reallocations; } @Override public void clearLists() { super.clearLists(); reallocations.clear(); } @Override public GenericPoolable reallocate( Slot slot, GenericPoolable poolable) throws Exception { assert slot != null : "Slot cannot be null in reallocate"; assert poolable != null : "Cannot reallocate null Poolable for slot: " + slot; GenericPoolable obj = onReallocation.applyNext(slot, poolable); reallocations.add(obj); return obj; } } public static OnAllocation alloc(Action... actions) { return new OnAllocation(actions); } public static OnDeallocation dealloc(Action... actions) { return new OnDeallocation(actions); } public static OnReallocation realloc(Action... actions) { return new OnReallocation(actions); } public static CountingAllocator allocator( OnAllocation onAllocation, OnDeallocation onDeallocation) { return new CountingAllocatorImpl(onAllocation, onDeallocation); } public static CountingAllocator allocator(OnAllocation onAllocation) { return allocator(onAllocation, dealloc($null)); } public static CountingAllocator allocator(OnDeallocation onDeallocation) { return allocator(alloc($new), onDeallocation); } public static CountingAllocator allocator() { return allocator(alloc($new), dealloc($null)); } public static CountingReallocator reallocator( OnAllocation onAllocation, OnDeallocation onDeallocation, OnReallocation onReallocation) { return new CountingReallocatorImpl( onAllocation, onDeallocation, onReallocation); } public static CountingReallocator reallocator( OnAllocation onAllocation, OnDeallocation onDeallocation) { return reallocator(onAllocation, onDeallocation, realloc($new)); } public static CountingReallocator reallocator( OnAllocation onAllocation, OnReallocation onReallocation) { return reallocator(onAllocation, dealloc($null), onReallocation); } public static CountingReallocator reallocator( OnDeallocation onDeallocation, OnReallocation onReallocation) { return reallocator(alloc($new), onDeallocation, onReallocation); } public static CountingReallocator reallocator(OnAllocation onAllocation) { return reallocator(onAllocation, dealloc($null), realloc($new)); } public static CountingReallocator reallocator(OnDeallocation onDeallocation) { return reallocator(alloc($new), onDeallocation, realloc($new)); } public static CountingReallocator reallocator(OnReallocation onReallocation) { return reallocator(alloc($new), dealloc($null), onReallocation); } public static CountingReallocator reallocator() { return reallocator(alloc($new), dealloc($null), realloc($new)); } public static final Action $new = (slot, obj) -> new GenericPoolable(slot); public static final Action $null = (slot, obj) -> null; public static Action $throw(final Exception e) { return (slot, obj) -> { throw e; }; } public static Action $throw(final Error e) { return (slot, obj) -> { throw e; }; } public static Action $acquire(final Semaphore semaphore, final Action action) { return (slot, obj) -> { semaphore.acquire(); return action.apply(slot, obj); }; } public static Action $release(final Semaphore semaphore, final Action action) { return (slot, obj) -> { semaphore.release(); return action.apply(slot, obj); }; } public static Action $countDown( final CountDownLatch latch, final Action action) { return (slot, obj) -> { try { return action.apply(slot, obj); } finally { latch.countDown(); } }; } public static Action $sync(final Lock lock, final Action action) { return (slot, obj) -> { lock.lock(); try { return action.apply(slot, obj); } finally { lock.unlock(); } }; } public static Action $observeNull( final AtomicBoolean observedNull, final Action action) { return (slot, obj) -> { if (obj == null) { observedNull.set(true); } return action.apply(slot, obj); }; } public static Action $sleep(final long millis, final Action action) { return (slot, obj) -> { Thread.sleep(millis); return action.apply(slot, obj); }; } public static Action $if( final AtomicBoolean cond, final Action then, final Action alt) { return (slot, obj) -> { if (cond.get()) { return then.apply(slot, obj); } return alt.apply(slot, obj); }; } public static Action $incrementAnd( final AtomicLong counter, final Action action) { return new Action() { @Override public GenericPoolable apply(Slot slot, GenericPoolable obj) throws Exception { counter.getAndIncrement(); return action.apply(slot, obj); } }; } }