package com.growcontrol.common.meta;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import com.growcontrol.common.meta.exceptions.InvalidMetaValueException;
import com.growcontrol.common.meta.exceptions.UnknownAddressException;
import com.poixson.commonjava.Utils.Keeper;
import com.poixson.commonjava.Utils.utils;
import com.poixson.commonjava.Utils.exceptions.RequiredArgumentException;
import com.poixson.commonjava.xEvents.xEventData;
import com.poixson.commonjava.xEvents.xEventListener;
import com.poixson.commonjava.xEvents.xEventListener.ListenerPriority;
import com.poixson.commonjava.xEvents.xHandlerSimple;
import com.poixson.commonjava.xEvents.xListenerDAO;
import com.poixson.commonjava.xEvents.xRunnableEvent;
public class MetaRouter extends xHandlerSimple {
private static final String LISTENER_METHOD_NAME = "onMetaEvent";
private static volatile MetaRouter instance = null;
private static final Object instanceLock = new Object();
// listener addresses
private final Map<MetaAddress, xListenerDAO> dests =
new ConcurrentHashMap<MetaAddress, xListenerDAO>();
private final Set<MetaAddress> sources =
new CopyOnWriteArraySet<MetaAddress>();
public static MetaRouter get() {
if(instance == null) {
synchronized(instanceLock) {
if(instance == null) {
instance = new MetaRouter();
Keeper.add(instance);
}
}
}
return instance;
}
private MetaRouter() {
// super();
this.listeners = null;
}
// listener type
@Override
public Class<? extends xEventListener> getEventListenerType() {
return MetaListener.class;
}
// event type
@Override
public Class<? extends xEventData> getEventDataType() {
return MetaEvent.class;
}
// fixed method name
@Override
protected String getMethodName() {
return LISTENER_METHOD_NAME;
}
public String register(final String addrStr, final MetaListener listener) {
if(utils.isEmpty(addrStr)) throw new RequiredArgumentException("addrStr");
if(listener == null) throw new RequiredArgumentException("listener");
final MetaAddress address = MetaAddress.get(addrStr);
if(address == null) throw new UnknownAddressException(addrStr);
return this.register(
address,
listener
);
}
public String register(final MetaAddress address, final MetaListener listener) {
if(address == null) throw new RequiredArgumentException("address");
if(listener == null) throw new RequiredArgumentException("listener");
final MetaAddress addr =
address == null
? MetaAddress.getRandom()
: address;
final Method method;
try {
method = listener.getClass().getMethod(
this.getMethodName(),
MetaEvent.class
);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (SecurityException e) {
throw new RuntimeException(e);
}
if(method == null) throw new RuntimeException("Failed to find listener method: "+this.getMethodName());
if(this.dests.containsKey(addr))
log().fine("Replacing meta event listener: "+addr.hash);
else
log().finer("Registered meta address: "+addr.hash);
final xListenerDAO dao = new xListenerDAO(
listener,
method,
ListenerPriority.NORMAL,
false, // filter handled,
true // filter cancelled
);
this.dests.put(addr, dao);
return addr.hash;
}
@Override
public void register(final xEventListener listener) {
throw new UnsupportedOperationException();
}
//TODO: this is untested
@Override
public void unregister(final xEventListener listener) {
if(listener == null) throw new RequiredArgumentException("listener");
if(!(listener instanceof MetaListener))
throw new UnsupportedOperationException();
final MetaListener expected = (MetaListener) listener;
final Iterator<Entry<MetaAddress, xListenerDAO>> it =
this.dests.entrySet().iterator();
int count = 0;
while(it.hasNext()) {
final Entry<MetaAddress, xListenerDAO> entry = it.next();
final MetaAddress address = entry.getKey();
final xListenerDAO dao = entry.getValue();
if(expected.equals(dao.listener)) {
this.dests.remove(address);
this.log().finest("Removed listener: "+address.getKey()+
":"+dao.listener.getClass().getName());
// return;
}
}
if(count == 0) {
this.log().finest("Listener not found to remove");
} else {
this.log().finest("Removed [ "+Integer.toString(count)+
" ] listeners of type: "+listener.getClass().getName());
}
}
@Override
public void unregisterType(final Class<?> listenerClass) {
if(listenerClass == null) throw new RequiredArgumentException("listener class");
final Iterator<Entry<MetaAddress, xListenerDAO>> it =
this.dests.entrySet().iterator();
int count = 0;
while(it.hasNext()) {
final Entry<MetaAddress, xListenerDAO> entry = it.next();
final MetaAddress address = entry.getKey();
final xListenerDAO dao = entry.getValue();
if(listenerClass.equals(dao.listener.getClass())) {
this.dests.remove(address);
count++;
this.log().finest("Removed listener: "+dao.listener.getClass().getName());
}
}
if(count == 0) {
this.log().finest("Listener not found to remove");
} else {
this.log().finest("Removed [ "+Integer.toString(count)+
" ] listeners of type: "+listenerClass.getName());
}
}
@Override
public void unregisterAll() {
super.unregisterAll();
this.dests.clear();
this.sources.clear();
}
// route an event
public void route(final String destAddr, final String value) {
if(utils.isEmpty(destAddr)) throw new RequiredArgumentException("destAddr");
if(utils.isEmpty(value)) throw new RequiredArgumentException("value");
final MetaType metaValue = MetaType.get(value);
if(metaValue == null) throw new InvalidMetaValueException(value);
this.route(
destAddr,
metaValue
);
}
public void route(final String destAddr, final MetaType value) {
if(utils.isEmpty(destAddr)) throw new RequiredArgumentException("destAddr");
if(value == null) throw new RequiredArgumentException("value");
final MetaAddress addr = MetaAddress.get(destAddr);
if(addr == null) throw new UnknownAddressException(destAddr);
this.route(
addr,
value
);
}
public void route(final MetaAddress dest, final MetaType value) {
if(dest == null) throw new RequiredArgumentException("dest");
if(value == null) throw new RequiredArgumentException("value");
final MetaEvent event = new MetaEvent(dest, value);
this.trigger(event);
}
@Override
public void trigger(final xEventData event) {
// ensure main thread
//TODO: how did I do this before?
//TODO:
//this.log().warning("xHandler->trigger() function is unfinished!");
if(event == null) throw new RequiredArgumentException("event");
final MetaEvent metaEvent = (MetaEvent) event;
this.log().finest("Triggering meta event: "+event.toString());
// final Set<xRunnableEvent> waitFor = new HashSet<xRunnableEvent>();
final Iterator<Entry<MetaAddress, xListenerDAO>> it = this.dests.entrySet().iterator();
// LOOP_LISTENERS:
while(it.hasNext()) {
if(event.isCancelled())
break;
final Entry<MetaAddress, xListenerDAO> entry = it.next();
final MetaAddress address = entry.getKey();
final xListenerDAO dao = entry.getValue();
if(!metaEvent.destination.matches(address))
continue;
// run event
final xRunnableEvent run = new xRunnableEvent(
dao,
event,
ListenerPriority.NORMAL
);
//TODO:
// waitFor.add(run);
// xThreadPool.getMainPool()
// .runLater(run);
run.run();
} // listeners loop
//TODO:
// // wait for event tasks to complete
// for(final xRunnableEvent run : waitFor) {
// run.waitUntilRun();
// }
if(event.isCancelled())
this.log().fine("Event was cancelled: "+event.toString());
if(!event.isHandled())
this.log().fine("Event was not handled: "+event.toString());
}
// get known destination addresses
public MetaAddress[] getKnownDestinations() {
return this.dests.keySet()
.toArray(new MetaAddress[0]);
}
// get known source addresses
public MetaAddress[] getKnownSources() {
return this.sources
.toArray(new MetaAddress[0]);
}
// get listener by address
public MetaListener getListener(final String destAddr) {
final MetaAddress address = MetaAddress.get(destAddr);
if(address == null)
return null;
return getListener(address);
}
public MetaListener getListener(final MetaAddress destAddr) {
final xListenerDAO dao = this.dests.get(destAddr);
if(dao == null)
return null;
return (MetaListener) dao.listener;
}
}