Created
December 23, 2011 09:32
-
-
Save reikje/1513718 to your computer and use it in GitHub Desktop.
Using reference maps for caches and listeners
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package javasplitter; | |
| public interface Auditable { | |
| void process(); | |
| void validate(); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package javasplitter; | |
| import java.util.Iterator; | |
| import java.util.NavigableSet; | |
| import java.util.TreeSet; | |
| public class AuditableInvoker { | |
| private final Auditable[] invokeables; | |
| private final NavigableSet<AuditableLifecycleListener> listeners; | |
| public AuditableInvoker(final Auditable... invokeables) { | |
| this.invokeables = invokeables; | |
| this.listeners = new TreeSet<AuditableLifecycleListener>(); | |
| } | |
| public void addListener(final AuditableLifecycleListener listener) { | |
| this.listeners.add(listener); | |
| } | |
| public void invoke() { | |
| for (Auditable invokeable : invokeables) { | |
| validate(invokeable); | |
| process(invokeable); | |
| } | |
| } | |
| private void validate(final Auditable auditable) { | |
| for (final Iterator<AuditableLifecycleListener> iterator = this.listeners.descendingIterator(); | |
| iterator.hasNext(); ) { | |
| final AuditableLifecycleListener listener = iterator.next(); | |
| listener.onValidationStart(auditable); | |
| } | |
| auditable.validate(); | |
| for (final AuditableLifecycleListener listener : this.listeners) { | |
| listener.onValidationFinish(auditable); | |
| } | |
| } | |
| private void process(final Auditable auditable) { | |
| for (final Iterator<AuditableLifecycleListener> iterator = this.listeners.descendingIterator(); | |
| iterator.hasNext(); ) { | |
| final AuditableLifecycleListener listener = iterator.next(); | |
| listener.onProcessStart(auditable); | |
| } | |
| auditable.process(); | |
| for (final AuditableLifecycleListener listener : this.listeners) { | |
| listener.onProcessFinish(auditable); | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package javasplitter; | |
| public interface AuditableLifecycleListener { | |
| void onValidationStart(final Auditable auditable); | |
| void onValidationFinish(final Auditable auditable); | |
| void onProcessStart(final Auditable auditable); | |
| void onProcessFinish(final Auditable auditable); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package javasplitter; | |
| import org.apache.commons.collections15.map.AbstractReferenceMap; | |
| import org.apache.commons.collections15.map.ReferenceIdentityMap; | |
| import java.util.Collections; | |
| import java.util.Map; | |
| public class ExecutionTimingAuditableLifecycleListener implements AuditableLifecycleListener { | |
| private final StatsCollector statsCollector; | |
| private final Map<Auditable, Long> timedExecutions = Collections.synchronizedMap( | |
| new ReferenceIdentityMap<Auditable, Long>( | |
| AbstractReferenceMap.WEAK, | |
| AbstractReferenceMap.HARD | |
| ) | |
| ); | |
| public ExecutionTimingAuditableLifecycleListener(final StatsCollector statsCollector) { | |
| this.statsCollector = statsCollector; | |
| } | |
| @Override | |
| public void onValidationStart(final Auditable auditable) { | |
| this.timedExecutions.put(auditable, System.currentTimeMillis()); | |
| } | |
| @Override | |
| public void onValidationFinish(final Auditable auditable) { | |
| final Long startTime = this.timedExecutions.get(auditable); | |
| if (startTime != null) { | |
| final long validationTime = System.currentTimeMillis() - startTime; | |
| this.statsCollector.collectValidationStats(auditable.getClass(), | |
| validationTime); | |
| } else { | |
| System.out.println( | |
| String.format( | |
| "Unable to find validation start time for %s", | |
| auditable.getClass().getSimpleName())); | |
| } | |
| } | |
| @Override | |
| public void onProcessStart(final Auditable auditable) { | |
| this.timedExecutions.put(auditable, System.currentTimeMillis()); | |
| } | |
| @Override | |
| public void onProcessFinish(final Auditable auditable) { | |
| final Long startTime = this.timedExecutions.get(auditable); | |
| if (startTime != null) { | |
| final long validationTime = System.currentTimeMillis() - startTime; | |
| this.statsCollector.collectProcessStats(auditable.getClass(), | |
| validationTime); | |
| } else { | |
| System.out.println( | |
| String.format( | |
| "Unable to find process start time for %s", | |
| auditable.getClass().getSimpleName())); | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package javasplitter; | |
| import org.junit.Test; | |
| import static org.junit.Assert.*; | |
| public class ExecutionTimingAuditableLifecycleListenerTest { | |
| @Test | |
| public void testTimingExecutions() throws InterruptedException { | |
| final int threads = 500; | |
| final int iterations = 5000; | |
| final int total = threads * iterations; | |
| final StatsCollector statsCollector = new UnboundedStatsCollector(); | |
| final AuditableLifecycleListener listener = | |
| new ExecutionTimingAuditableLifecycleListener(statsCollector); | |
| final MultithreadedStressTester stressTester = | |
| new MultithreadedStressTester(threads, iterations); | |
| stressTester.stress( | |
| new Runnable() { | |
| @Override | |
| public void run() { | |
| final Auditable sleeping = new SleepingAuditable(); | |
| final Auditable iterating = new IteratingAuditable(); | |
| final AuditableInvoker invoker = new AuditableInvoker(sleeping, iterating); | |
| invoker.addListener(listener); | |
| invoker.invoke(); | |
| } | |
| } | |
| ); | |
| assertEquals(total, statsCollector.timesValidated(SleepingAuditable.class)); | |
| assertEquals(total, statsCollector.timesValidated(IteratingAuditable.class)); | |
| assertEquals(total, statsCollector.timesProcessed(SleepingAuditable.class)); | |
| assertEquals(total, statsCollector.timesProcessed(IteratingAuditable.class)); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package javasplitter; | |
| public class IteratingAuditable implements Auditable { | |
| @Override | |
| public void process() { | |
| for (int i = 0; i < 5; i++) { | |
| // do nothing | |
| } | |
| } | |
| @Override | |
| public void validate() { | |
| for (int i = 0; i < 50; i++) { | |
| // do nothing | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public final class MultithreadedStressTester { | |
| /** | |
| * The default number of threads to run concurrently. | |
| */ | |
| public static final int DEFAULT_THREAD_COUNT = 2; | |
| private final ExecutorService executor; | |
| private final int threadCount; | |
| private final int iterationCount; | |
| public MultithreadedStressTester(int threadCount, int iterationCount) { | |
| this.threadCount = threadCount; | |
| this.iterationCount = iterationCount; | |
| this.executor = Executors.newCachedThreadPool(); | |
| } | |
| public void stress(final Runnable action) throws InterruptedException { | |
| spawnThreads(action).await(); | |
| } | |
| private CountDownLatch spawnThreads(final Runnable action) { | |
| final CountDownLatch finished = new CountDownLatch(threadCount); | |
| for (int i = 0; i < threadCount; i++) { | |
| executor.execute(new Runnable() { | |
| public void run() { | |
| try { | |
| repeat(action); | |
| } | |
| finally { | |
| finished.countDown(); | |
| } | |
| } | |
| }); | |
| } | |
| return finished; | |
| } | |
| private void repeat(Runnable action) { | |
| for (int i = 0; i < iterationCount; i++) { | |
| action.run(); | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package javasplitter; | |
| public class SleepingAuditable implements Auditable { | |
| @Override | |
| public void process() { | |
| try { | |
| Thread.sleep(20L); | |
| } catch (InterruptedException e) { | |
| System.out.println("I got interrupted"); | |
| } | |
| } | |
| @Override | |
| public void validate() { | |
| try { | |
| Thread.sleep(10L); | |
| } catch (InterruptedException e) { | |
| System.out.println("I got interrupted"); | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package javasplitter; | |
| public interface StatsCollector { | |
| void collectValidationStats(Class<? extends Auditable> clazz, long executionTime); | |
| void collectProcessStats(Class<? extends Auditable> clazz, long executionTime); | |
| long timesValidated(Class<? extends Auditable> clazz); | |
| long timesProcessed(Class<? extends Auditable> clazz); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package javasplitter; | |
| import java.util.Map; | |
| import java.util.concurrent.ConcurrentHashMap; | |
| import java.util.concurrent.atomic.AtomicInteger; | |
| public class UnboundedStatsCollector implements StatsCollector { | |
| private final Map<Class<? extends Auditable>, AtomicInteger> validationStats = | |
| new ConcurrentHashMap<Class<? extends Auditable>, AtomicInteger>(); | |
| private final Map<Class<? extends Auditable>, AtomicInteger> processStats = | |
| new ConcurrentHashMap<Class<? extends Auditable>, AtomicInteger>(); | |
| public UnboundedStatsCollector() { | |
| this.validationStats.put(SleepingAuditable.class, new AtomicInteger(0)); | |
| this.processStats.put(SleepingAuditable.class, new AtomicInteger(0)); | |
| this.validationStats.put(IteratingAuditable.class, new AtomicInteger(0)); | |
| this.processStats.put(IteratingAuditable.class, new AtomicInteger(0)); | |
| } | |
| @Override | |
| public void collectValidationStats(final Class<? extends Auditable> clazz, final long executionTime) { | |
| this.validationStats.get(clazz).incrementAndGet(); | |
| } | |
| @Override | |
| public void collectProcessStats(final Class<? extends Auditable> clazz, final long executionTime) { | |
| this.processStats.get(clazz).incrementAndGet(); | |
| } | |
| @Override | |
| public long timesValidated(final Class<? extends Auditable> clazz) { | |
| return this.validationStats.get(clazz).longValue(); | |
| } | |
| @Override | |
| public long timesProcessed(final Class<? extends Auditable> clazz) { | |
| return this.processStats.get(clazz).longValue(); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment