package com.rayo.client.io;
import java.util.Collection;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.rayo.client.filter.XmppObjectFilter;
import com.rayo.client.listener.StanzaListener;
import com.rayo.client.xmpp.stanza.AbstractXmppObject;
import com.rayo.client.xmpp.stanza.Error;
import com.rayo.client.xmpp.stanza.Error.Condition;
import com.rayo.client.xmpp.stanza.Error.Type;
import com.rayo.client.xmpp.stanza.IQ;
import com.rayo.client.xmpp.stanza.Message;
import com.rayo.client.xmpp.stanza.Presence;
import com.rayo.client.xmpp.stanza.XmppObject;
/**
* <p>Implements the {@link MessageDispatcher} interface providing an
* implementation based in an unbounded queue and a thread that reads from
* the queue and dispatches messages to the different listeners and filters.</p>
*
* @author martin
*
*/
public class UnboundedQueueMessageDispatcher implements MessageDispatcher {
private Logger log = LoggerFactory.getLogger(UnboundedQueueMessageDispatcher.class);
private Collection<StanzaListener> stanzaListeners = new ConcurrentLinkedQueue<StanzaListener>();
private Collection<XmppObjectFilter> filters = new ConcurrentLinkedQueue<XmppObjectFilter>();
private LinkedBlockingQueue<XmppObject> messagesQueue = new LinkedBlockingQueue<XmppObject>();
private LinkedBlockingQueue<XmppObject> filtersQueue = new LinkedBlockingQueue<XmppObject>();
/**
* Initiates the message dispatcher. When created, the instance will start a
* new thread that will be ready to process incoming messages.
*/
public UnboundedQueueMessageDispatcher() {
Runnable listenersTask = new Runnable() {
@Override
public void run() {
while(true) {
XmppObject object = null;
try {
object = messagesQueue.poll(1000, TimeUnit.SECONDS);
} catch (InterruptedException e) {}
if (object != null) {
process(object);
}
}
}
};
new Thread(listenersTask).start();
Runnable filterTask = new Runnable() {
@Override
public void run() {
while(true) {
XmppObject object = null;
try {
object = filtersQueue.poll(1000, TimeUnit.SECONDS);
} catch (InterruptedException e) {}
if (object != null) {
filter((AbstractXmppObject)object);
}
}
}
};
new Thread(filterTask).start();
}
@Override
public void addStanzaListener(StanzaListener listener) {
stanzaListeners.add(listener);
}
@Override
public void removeStanzaListener(StanzaListener listener) {
stanzaListeners.remove(listener);
}
@Override
public void addFilter(XmppObjectFilter filter) {
filters.add(filter);
}
@Override
public void removeFilter(XmppObjectFilter filter) {
filters.remove(filter);
}
@Override
public void dispatch(XmppObject object) {
log.trace(String.format("Dispatching XMPP Object with id [%s] to the dispatching queue", object.getId()));
messagesQueue.add(object);
filtersQueue.add(object);
}
private void filter(final AbstractXmppObject object) {
log.trace(String.format("Invoking filters on XMPP Object with id [%s]", object.getId()));
for (XmppObjectFilter filter: filters) {
try {
log.trace("Invoking filter " + filter);
filter.filter(object);
log.trace(String.format("Filter [%s] has finished its work", filter));
} catch (Exception e) {
e.printStackTrace();
dispatch(new Error(Condition.undefined_condition, Type.cancel, String.format("Error on client filter: %s - %s",e.getClass(),e.getMessage())));
}
}
log.trace(String.format("Done invoking filters", object.getId()));
}
@Override
public void reset() {
messagesQueue.clear();
filters.clear();
stanzaListeners.clear();
}
private void process(XmppObject object) {
log.trace(String.format("Fetched XMPP Object [%s] from the dispatching queue", object));
for(StanzaListener listener: stanzaListeners) {
if (object instanceof IQ) {
log.trace(String.format("Invoking listener [%s] onIQ method with IQ id [%s]", listener, object.getId()));
listener.onIQ((IQ)object);
} else if (object instanceof Presence) {
log.trace(String.format("Invoking listener [%s] onPresence method with presence id [%s]", listener, object.getId()));
listener.onPresence((Presence)object);
} else if (object instanceof Message) {
log.trace(String.format("Invoking listener [%s] onMessage method with message id [%s]", listener, object.getId()));
listener.onMessage((Message)object);
} else if (object instanceof Error) {
log.trace(String.format("Invoking listener [%s] onError method with error id [%s]", listener, object.getId()));
listener.onError((Error)object);
}
log.trace(String.format("Listener [%s] has finished its work", listener));
}
}
}