package com.onionnetworks.util;
import java.util.*;
import java.lang.reflect.Method;
/**
* @author Justin Chapweske
*/
public class ReflectiveEventDispatch implements Runnable {
public static final int DEFAULT_WARNING_TIME = 10;
private final Thread thread;
private HashMap methodCache = new HashMap();
private HashMap listeners = new HashMap();
private LinkedList eventQueue = new LinkedList();
private ExceptionHandler handler;
public ReflectiveEventDispatch() {
thread = new Thread(this,"Reflective Dispatch#" + hashCode());
thread.setDaemon(true);
thread.start();
}
public void setPriority(int priority) {
thread.setPriority(priority);
}
public void setExceptionHandler(ExceptionHandler h) {
handler = h;
}
public synchronized void addListener(Object source, EventListener el,
String methodName) {
this.addListener(source,el,new String[]{methodName});
}
public synchronized void addListener(Object source, EventListener el,
String[] methodNames) {
HashMap hm = (HashMap) listeners.get(source);
if (hm == null) {
hm = new HashMap();
listeners.put(source, hm);
}
for (int i=0;i<methodNames.length;i++) {
HashSet set = (HashSet) hm.get(methodNames[i]);
if (set == null) {
set = new HashSet();
hm.put(methodNames[i],set);
}
set.add(el);
}
}
public synchronized void removeListener(Object source, EventListener el,
String methodName) {
this.removeListener(source,el,new String[]{methodName});
}
public synchronized void removeListener(Object source, EventListener el,
String[] methodNames) {
HashMap hm = (HashMap) listeners.get(source);
if (hm == null) {
throw new IllegalArgumentException("Listener not registered.");
}
for (int i=0;i<methodNames.length;i++) {
HashSet set = (HashSet) hm.get(methodNames[i]);
if (set == null || !set.contains(el)) {
throw new IllegalArgumentException("Listener not registered.");
}
set.remove(el);
}
}
public synchronized void fire(EventObject ev, String methodName) {
eventQueue.add(new Tuple(ev, methodName));
this.notifyAll();
}
public synchronized void close() {
// Place this on the queue to signify that we are done.
eventQueue.add(this);
this.notifyAll();
}
public void run() {
boolean done = false;
while (!done) {
EventObject ev = null;
String methodName = null;
HashSet set = null;
synchronized (this) {
if (eventQueue.isEmpty()) {
try {
this.wait();
} catch (InterruptedException e) {
done = true;
}
continue;
}
Object obj = eventQueue.removeFirst();
if (obj == this) {
// If this is on the queue, it is time to close.
done = true;
continue;
}
Tuple t = (Tuple) obj;
ev = (EventObject) t.getLeft();
methodName = (String) t.getRight();
HashMap hm = (HashMap) listeners.get(ev.getSource());
if (hm == null) {
continue;
}
set = (HashSet) hm.get(methodName);
if (set == null) {
continue;
}
// Make a copy incase its modified while we're doing the shit.
set = (HashSet) set.clone();
}
for (Iterator it=set.iterator();it.hasNext();) {
EventListener el = (EventListener) it.next();
// Get the method and invoke it, passing the event.
//long t1 = System.currentTimeMillis();
try {
final Class elc = el.getClass();
final Class evc = ev.getClass();
// Cache the method because getPublicMethod is very
// expensive to invoke.
Tuple cacheKey = new Tuple(elc,new Tuple(methodName,evc));
Method m = (Method) methodCache.get(cacheKey);
if (m == null) {
final Class ca[] = new Class[] { evc };
// This version of getMethod supports subclasses as
// parameter types.
m = Util.getPublicMethod(elc,methodName,ca);
methodCache.put(cacheKey,m);
}
final Object oa[] = new Object[] { ev };
m.invoke(el,oa);
} catch (Throwable t) {
if (handler != null) {
handler.handleException(new ExceptionEvent(this,t));
} else {
t.printStackTrace();
}
}
//long t2 = System.currentTimeMillis()-t1;
//if (t2 > DEFAULT_WARNING_TIME) {
// System.out.println(el+"."+methodName+"("+ev+") took"+
// " too long: "+t2+" millis");
//}
}
}
}
}