package org.jboss.fresh.events.impl;
import org.jboss.fresh.events.EventCentral;
import org.jboss.fresh.events.EventListener;
import org.jboss.fresh.events.EventFilter;
import org.jboss.fresh.events.Event;
import org.jboss.fresh.events.EventBroadcaster;
import org.jboss.fresh.events.RegistrationException;
import org.jboss.fresh.events.InvalidFormatException;
import org.jboss.fresh.events.net.EventNetRouter;
import java.util.Map;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;
import org.jboss.fresh.persist.RandomKeyPKGenerator;
public class EventCentralImpl implements EventCentral {
private static org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(EventCentralImpl.class);
private static org.apache.log4j.Logger fwlog = org.apache.log4j.Logger.getLogger(EventCentralImpl.ForwardListener.class);
private HashMap routers = new HashMap();
private LinkedHashMap allListenersByID = new LinkedHashMap();
private LinkedHashMap listenersByID = new LinkedHashMap();
private LinkedHashMap allBcsByID = new LinkedHashMap();
private LinkedHashMap bcsByID = new LinkedHashMap();
private LinkedHashMap centrals = new LinkedHashMap();
private EventListener forwardLis = new ForwardListener();
private EventBroadcaster eb;
private RandomKeyPKGenerator pkgen;
private String host;
private String app;
private LinkedHashMap seenEvents = new LinkedHashMap() {
protected boolean removeEldestEntry(Map.Entry ent) {
return size() > 10000;
}
};
private boolean silentlyDropSeen = false;
public EventCentralImpl(String hostLabel, String subdomain) {
host = hostLabel;
app = subdomain;
// Uses
pkgen = new RandomKeyPKGenerator("11,31", null);
pkgen.reseed(hostLabel + subdomain + System.currentTimeMillis());
eb = new EventBroadcaster(this, "EventCentral");
}
public void registerEventListener(String name, EventListener lis) {
try {
registerEventListener(name, lis, (List) null, 0);
} catch(InvalidFormatException ex) {
log.error("Internal application error - illegal state: ", ex);
}
}
public void registerEventListener(String name, EventListener lis, int flags) {
try {
registerEventListener(name, lis, (List) null, flags);
} catch(InvalidFormatException ex) {
log.error("Internal application error - illegal state: ", ex);
}
}
public void registerEventListener(String name, EventListener lis, String [] filters) throws InvalidFormatException {
registerEventListener(name, lis, filters == null ? (List) null : java.util.Arrays.asList(filters), 0);
}
public void registerEventListener(String name, EventListener lis, String [] filters, int flags) throws InvalidFormatException {
registerEventListener(name, lis, filters == null ? (List) null : java.util.Arrays.asList(filters), flags);
}
public void registerEventListener(String id, EventListener lis, List filters) throws InvalidFormatException {
registerEventListener(id, lis, new EventFilter(filters), 0);
}
public void registerEventListener(String id, EventListener lis, List filters, int flags) throws InvalidFormatException {
registerEventListener(id, lis, new EventFilter(filters), flags);
}
public void registerEventListener(String id, EventListener lis, EventFilter filter) throws InvalidFormatException {
registerEventListener(id, lis, filter, 0);
}
/**
* id - lis - filters
* When event is dispatched it is passed through all EventFilters
* The list it passes through could be minimized if EventFilters were categorized
* in hierarchical manner:
*
* /
* <any>
* hostLabel1
* hostLabel2
* hostLabel3
* |
* <any>
* elan
* lds
* |
* <any>
* component
*
* What if name contains a pattern? This would complicate the code. For now we keep it simple
*/
public void registerEventListener(String id, EventListener lis, EventFilter filter, int flags) throws InvalidFormatException {
log.info("[" + host + "/" + app + "] registerEventListener() : " + id + ", " + filter + " :: " + lis); // , new Exception("REG TRACE")
filter = filter==null ? new EventFilter((List)null) : new EventFilter(filter, this);
if((flags & SILENT) == 0) {
Object [] dat = new Object[2];
dat[0] = id;
dat[1] = new ListenerData(lis, filter);
Event ev = new Event("ECentral", "RegisterListener", dat);
try {
dispatch(eb.produceEvent(ev));
} catch(Exception ex) {
log.warn("Dispatching of RegisterListener event produced exception: ", ex);
}
}
// we dispatch registerListener event first to avoid sending it to just registered listener
synchronized (this) {
//ListenerData l = (ListenerData) listenersByID.get(id);
//if (l != null) {
// listenersByID.remove(id);
//throw new RegistrationException("EventListener already registered for id: " + id + " (unregister it first)");
//}
ListenerData dat = new ListenerData(lis, filter);
if((flags & HIDDEN) == 0)
listenersByID.put(id, dat);
allListenersByID.put(id, dat);
}
}
// remove specified event listener from all listen targets
/*
public synchronized void unregisterEventListener(EventListener lis) {
LinkedList l = (LinkedList) lmap.get(lis);
if (l == null) return;
Iterator it = l.iterator();
while (it.hasNext()) {
Object key = it.next();
LinkedList l2 = (LinkedList) lismap.get(key);
if (l2 == null) continue;
Iterator it2 = l2.iterator();
while (it2.hasNext()) {
if (it2.next().equals(lis)) {
it2.remove();
break;
}
}
if (l2.size() == 0) lismap.remove(key);
}
lmap.remove(lis);
}
*/
public synchronized EventListener unregisterEventListener(String id) {
ListenerData l = (ListenerData) allListenersByID.remove(id);
listenersByID.remove(id);
// if (l != null) {
// listenersByID.remove(id);
//throw new RegistrationException("EventListener already registered for id: " + id + " (unregister it first)");
// }
return l.getListener();
}
public synchronized void unregisterEventListener(EventListener lis) {
// iterate over everything find all occurencies of lis and unregister them all
Iterator it = new HashSet(allListenersByID.entrySet()).iterator();
while(it.hasNext()) {
Map.Entry ent = (Map.Entry) it.next();
EventListener el = ((ListenerData)ent.getValue()).getListener();
if(el.equals(lis)) {
listenersByID.remove(ent.getKey());
allListenersByID.remove(ent.getKey());
}
}
}
// This method is part of dead-end idea how ECs will be grouped together
// we'll remove it
public synchronized void registerEventCentral(EventCentral ec, String lisid, EventListener lis) throws InvalidFormatException {
// get app part
int start = lisid.indexOf("/");
if(start==-1) throw new InvalidFormatException(lisid);
String host = lisid.substring(0, start);
int end = lisid.indexOf("/", start+1);
if(end==-1) throw new InvalidFormatException(lisid);
String app = lisid.substring(start+1, end);
centrals.put(app, lis);
// app EC registers when it's comming up. It has no broadcasters or listeners registered at this point
// now we go over our listeners - see which are interested in anything to do with host/app
// we register listeners only for those interested
Iterator it = listenersByID.entrySet().iterator();
while(it.hasNext()) {
Map.Entry ent = (Map.Entry) it.next();
EventFilter filter = ((ListenerData) ent.getValue()).getFilter();
ec.registerEventListener((String) ent.getKey(), forwardLis, filter);
}
// we register all broadcasters
it = bcsByID.entrySet().iterator();
while(it.hasNext()) {
Map.Entry ent = (Map.Entry) it.next();
ec.registerEventBroadcaster((String) ent.getKey(), (EventBroadcaster) ent.getValue());
}
// register the other EC's listener
registerEventListener(lisid, lis, new String[] {"//ECentral//"});
}
public void registerEventBroadcaster(String id, EventBroadcaster bc) {
registerEventBroadcaster(id, bc, 0);
}
public synchronized void registerEventBroadcaster(String id, EventBroadcaster bc, int flags) {
if((flags & HIDDEN) == 0)
bcsByID.put(id, bc);
allBcsByID.put(id, bc);
}
public synchronized EventBroadcaster unregisterEventBroadcaster(String id) {
//log.info("Unregistering for id: "+id);
EventBroadcaster eb = (EventBroadcaster) allBcsByID.remove(id);
bcsByID.remove(id);
//log.info("Remainging: "+bcsByID);
return eb;
}
public Map getBroadcasters() {
return getBroadcasters(false);
}
public synchronized Map getBroadcasters(boolean hidden) {
if(hidden) return new LinkedHashMap(allBcsByID);
return new LinkedHashMap(bcsByID);
}
public Iterator broadcastersIterator() {
return broadcastersIterator(false);
}
public synchronized Iterator broadcastersIterator(boolean hidden) {
if(hidden) return new ArrayList(allBcsByID.entrySet()).iterator();
return new ArrayList(bcsByID.entrySet()).iterator();
}
public void dispatchEvent(Event ev) throws Exception {
ev.addTrace("EventCentralImpl:" + host + "/" + app);
if(!checkEvent(ev)) return;
if("ECentral".equals(ev.getEventClass())) {
if("RegisterListener".equals(ev.getEventName())) {
Object [] dat = (Object []) ev.getValueObject();
String id = (String) dat[0];
EventCentral.ListenerData ld = (EventCentral.ListenerData) dat[1];
//log.info(" EVENT:RegisterListener : " + id + " :: " + ld.getListener() + " :: " + ld.getFilter());
// this event listener must be hidden - it's part of infrastructure and should only be used locally
// WTF?
// �e ni hidden potem broadcasta, da je bil registriran in to se potem na taisti remote client
// ponese, ki je spro�il event. Seveda pa no�emo da je dejansko hidden - samo da ne broadcasta
// registration eventa. Ampak to je pa spet lame. Mi lahko registration event broadcastamo, ampak ne sme iti
// na remote EC, kjer je originalno nastal. Ne. Ker To bo samo �e en RegisterListener ustrelilo in se botsta
// zdaj propagirala 2 saj vidi� v nadaljevanju da originalni event dispatchamo listenerjem. Mi samo hkrati
// potihem �e registriramo listenerja pri sebi - da je viden zainteresiranim. Zato je prav, da je
// NO_BROADCAST oz. 'hidden' . Tukaj tudi vidimo potrebo po distinkciji med hidden in silent.
// To tukaj je silent and not hidden
registerEventListener(id, ld.getListener(), ld.getFilter(), SILENT);
}
}
dispatch(ev, false);
}
protected synchronized boolean checkEvent(Event ev) {
String evid = ev.getOrigin() + "/" + ev.getID();
//log.debug("eventid: " + evid);
if(seenEvents.get(evid) != null) {
// we silently drop events that already passed through
if(!silentlyDropSeen)
log.warn("Event has already passed through: " + ev + " trace: " + ev.getTrace());
return false;
}
seenEvents.put(evid, evid); // we don't really need the event
return true;
}
public void dispatch(Event ev) throws Exception {
dispatch(ev, true);
}
protected void dispatch(Event ev, boolean checkEvent) throws Exception {
//log.debug(" dispatch() :: " + ev);
if(log.isDebugEnabled()) log.debug(" dispatch() " + this + " :: " + ev);
// make a copy of listeners then iterate over them
List ls = null;
synchronized(this) {
if(log.isDebugEnabled()) log.debug(" checking event ...");
if(checkEvent && !checkEvent(ev)) return;
ls = new ArrayList(allListenersByID.entrySet());
}
/*
Iterator it = ls.iterator();
while(it.hasNext()) {
Map.Entry ent = (Map.Entry) it.next();
ListenerData data = (ListenerData)ent.getValue();
if(data.getFilter().match(ev)) {
data.getListener().event(ev);
}
}
*/
//log.info("All listeners: " + ls);
for(int i=0; i<6 ; i++) {
ls = matchField(ls, i, ev);
}
if(log.isDebugEnabled()) log.debug("Matching listeners: " + ls);
HashSet dispatched = new HashSet();
Iterator it = ls.iterator();
while(it.hasNext()) {
Map.Entry ent = (Map.Entry) it.next();
ListenerData data = (ListenerData)ent.getValue();
EventListener lis = data.getListener();
if(!dispatched.contains(lis)) {
ev.addTrace(ent.getKey() + ":" + data.getListener());
if(log.isDebugEnabled()) log.debug("dispatching event to: " + ent.getKey() + ":" + data.getListener());
lis.event(ev);
dispatched.add(lis);
}
}
}
private List matchField(List ls, int field, Event ev) {
//log.info("matchField() " + field + " on " + ev + " for " + ls);
Iterator it = ls.iterator();
while(it.hasNext()) {
Map.Entry ent = (Map.Entry) it.next();
ListenerData data = (ListenerData)ent.getValue();
EventFilter filt = data.getFilter();
//log.info("Matching event " + ev + " against filter: " + filt + " (" + data.getListener() + ") for field: " + field);
boolean bMatch = false;
switch(field) {
case 0:
bMatch = filt.matchHost(ev);
break;
case 1:
bMatch = filt.matchApplication(ev);
break;
case 2:
bMatch = filt.matchComponent(ev);
break;
case 3:
bMatch = filt.matchComponentID(ev);
break;
case 4:
bMatch = filt.matchEventClass(ev);
break;
case 5:
bMatch = filt.matchEventName(ev);
break;
default:
throw new RuntimeException("Field out of range: " + field);
}
if(!bMatch) {
log.info(" <no match>");
it.remove();
}
}
return ls;
}
public Map getListeners() {
return getListeners(false);
}
public synchronized Map getListeners(boolean hidden) {
if(hidden) return new LinkedHashMap(allListenersByID);
return new LinkedHashMap(listenersByID);
}
public Iterator listenersIterator() {
return listenersIterator(false);
}
public synchronized Iterator listenersIterator(boolean hidden) {
if(hidden) return new ArrayList(allListenersByID.entrySet()).iterator();
return new ArrayList(listenersByID.entrySet()).iterator();
}
public Iterator listenersIteratorInverse() {
return null;
}
public synchronized void registerEventNetRouter(String agentid, EventNetRouter router) throws RegistrationException {
EventNetRouter rt = (EventNetRouter) routers.get(agentid);
if(rt != null) throw new RegistrationException("Router is already registered under the specified agentid: " + agentid + " (Either you should reuse existing one or there are more agents with same agentid - which should never be - which means wrong configuration.)");
routers.put(agentid, router);
}
public synchronized EventNetRouter getEventNetRouter(String agentid) {
return (EventNetRouter) routers.get(agentid);
}
public List getEventListenerFilters(String id) {
ListenerData data = (ListenerData) listenersByID.get(id);
return data.getFilter().getFilters();
}
public String generateID() {
try {
return (String) pkgen.newKey();
} catch(RuntimeException ex) {
throw ex;
} catch(Exception ex) {
throw new RuntimeException("Exception happened while generating id: ", ex);
}
}
public String produceOrigin(String componentName) {
return getHostLabel() + "/" + getApplication() + "/" + componentName + "(" + generateID() + ")";
}
public String produceOrigin(String componentName, String id) {
return getHostLabel() + "/" + getApplication() + "/" + componentName + "(" + id + ")";
}
public String getHostLabel() {
return host;
}
public String getApplication() {
return app;
}
public boolean isOriginOf(String origin) {
//log.info("\n\r\n\r>>>>>>>>>>>>>>>>>>>>>>>>>>> checking " + origin + " vs. " + host + "/" + "\n\r\n\r");
return origin.startsWith(host+"/");
}
class ForwardListener implements EventListener {
public void event(Event ev) {
// if ECentral RegisterListener
// register the damn listener
// broadcast it to all listeners
try {
//log.info("##### " + this + ": event() " + ev);
dispatchEvent(ev);
} catch(Exception ex) {
fwlog.error("failed to dispatch event: " + ev, ex);
}
}
}
public String toString() {
return super.toString() + " " + host + "/" + app;
}
}