Skip to content

Instantly share code, notes, and snippets.

@brianm
Created March 11, 2010 16:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brianm/329309 to your computer and use it in GitHub Desktop.
Save brianm/329309 to your computer and use it in GitHub Desktop.
package org.skife.sand;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Comparator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import static java.lang.String.format;
public class SandBox
{
private final SortedMap<Integer, Handler> handlers = new TreeMap<Integer, Handler>(new Comparator<Integer>() {
public int compare(Integer first, Integer second)
{
return first.compareTo(second) * -1;
}
});
private final Semaphore flag = new Semaphore(0);
private final int highestPriority;
public SandBox(Object handler)
{
int hp = Integer.MIN_VALUE;
final Method[] methods = handler.getClass().getDeclaredMethods();
for (Method method : methods) {
if (Modifier.isPublic(method.getModifiers()) && method.isAnnotationPresent(Priority.class)) {
int priority = method.getAnnotation(Priority.class).value();
if (priority > hp) {
hp = priority;
}
Class[] param_types = method.getParameterTypes();
Handler h = new Handler(handler, method, param_types);
if (handlers.containsKey(priority)) {
throw new IllegalArgumentException(format("multiple reactor methods have priority %d", priority));
}
handlers.put(priority, h);
}
}
highestPriority = hp;
}
public synchronized void provide(Object value)
{
final Class type = value.getClass();
for (Map.Entry<Integer, Handler> entry : handlers.entrySet()) {
entry.getValue().provide(type, value);
if (entry.getKey() == highestPriority && entry.getValue().isSatisfied()) {
flag.release();
return; // we satisfied highest priority, short circuit
}
}
}
public boolean react(long number, TimeUnit unit) throws InterruptedException, InvocationTargetException, IllegalAccessException
{
if (flag.tryAcquire(number, unit)) {
// flag will only be avail *if* highest priority handler is triggered
for (Handler handler : handlers.values()) {
if (handler.isSatisfied()) {
handler.handle();
return true; // satisfied highest priority so short circuit and return
}
}
}
for (Handler handler : handlers.values()) {
if (handler.isSatisfied()) {
handler.handle();
return true;
}
}
return false;
}
private class Handler
{
private final Object[] values;
private final Object target;
private final Method method;
private final Class[] types;
public Handler(Object target, Method method, Class[] paramTypes)
{
this.target = target;
this.method = method;
types = paramTypes;
values = new Object[types.length];
}
public void provide(Class type, Object value)
{
for (int i = 0; i < types.length; i++) {
if (types[i].isAssignableFrom(type)) {
values[i] = value;
return;
}
}
}
public boolean isSatisfied()
{
for (Object value : values) {
if (value == null) {
return false;
}
}
return true;
}
public void handle() throws InvocationTargetException, IllegalAccessException
{
method.invoke(target, values);
}
}
}
package org.skife.sand;
import junit.framework.TestCase;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class TestSandBox extends TestCase
{
public void testFigureOutBasics() throws Exception
{
final AtomicInteger flag = new AtomicInteger(0);
final SandBox box = new SandBox(new Object()
{
@Priority(3)
public void best(Dog dog, Cat cat)
{
flag.set(1);
}
@Priority(2)
public void okay(Dog dog)
{
flag.set(2);
}
@Priority(1)
public void blech(Cat cat)
{
flag.set(3);
}
@Priority(0)
public void fallback()
{
flag.set(4);
}
});
final CountDownLatch latch = new CountDownLatch(1);
new Thread(new Runnable()
{
public void run()
{
box.provide(new Dog());
box.provide(new Cat());
latch.countDown();
}
}).start();
latch.await();
assertTrue(box.react(100, TimeUnit.MILLISECONDS));
assertEquals(1, flag.get());
}
public void testFigureOutBasics2() throws Exception
{
final AtomicInteger flag = new AtomicInteger(0);
final SandBox box = new SandBox(new Object()
{
@Priority(3)
public void best(Dog dog, Cat cat)
{
flag.set(1);
}
@Priority(2)
public void okay(Dog dog)
{
flag.set(2);
}
@Priority(1)
public void blech(Cat cat)
{
flag.set(3);
}
@Priority(0)
public void fallback()
{
flag.set(4);
}
});
final CountDownLatch latch = new CountDownLatch(1);
new Thread(new Runnable()
{
public void run()
{
box.provide(new Dog());
latch.countDown();
}
}).start();
latch.await();
assertTrue(box.react(100, TimeUnit.MILLISECONDS));
assertEquals(2, flag.get());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment