Skip to content

Instantly share code, notes, and snippets.

@reikje
Created December 23, 2011 09:32
Show Gist options
  • Select an option

  • Save reikje/1513718 to your computer and use it in GitHub Desktop.

Select an option

Save reikje/1513718 to your computer and use it in GitHub Desktop.
Using reference maps for caches and listeners
package javasplitter;
public interface Auditable {
void process();
void validate();
}
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);
}
}
}
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);
}
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()));
}
}
}
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));
}
}
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
}
}
}
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 &lt; 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 &lt; iterationCount; i++) {
action.run();
}
}
}
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");
}
}
}
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);
}
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