Created
March 11, 2010 16:32
-
-
Save brianm/329309 to your computer and use it in GitHub Desktop.
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 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); | |
} | |
} | |
} |
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 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