package org.atomnuke.sink.eps;
import java.util.LinkedList;
import java.util.List;
import org.atomnuke.atom.model.Entry;
import org.atomnuke.atom.model.Feed;
import org.atomnuke.sink.AtomSink;
import org.atomnuke.sink.AtomSinkException;
import org.atomnuke.sink.AtomSinkResult;
import org.atomnuke.sink.SinkResult;
import org.atomnuke.sink.eps.eventlet.AtomEventlet;
import org.atomnuke.sink.eps.selector.DefaultSelector;
import org.atomnuke.sink.eps.selector.EntrySelector;
import org.atomnuke.plugin.InstanceContext;
import org.atomnuke.plugin.context.InstanceContextImpl;
import org.atomnuke.plugin.env.NopInstanceEnvironment;
import org.atomnuke.service.ServiceUnavailableException;
import org.atomnuke.service.gc.ReclamationHandler;
import org.atomnuke.sink.SinkAction;
import org.atomnuke.sink.eps.selector.SelectorResult;
import org.atomnuke.task.context.AtomTaskContext;
import org.atomnuke.lifecycle.InitializationException;
import org.atomnuke.util.remote.CancellationRemote;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* EPS - The event processing system... thingy
*
* TODO: Review this model a little bit more :x
*
* "Oh, yes! For humans, touch can connect you to an object in a very personal
* way; make it seem more real."
*
* -Jean-Luc Picard
*
* @author zinic
*/
public class EventletChainSink implements AtomSink, AtomEventletHandler {
private static final Logger LOG = LoggerFactory.getLogger(EventletChainSink.class);
private final List<EventletConduit> epsConduits;
private ReclamationHandler reclamationHandler;
public EventletChainSink() {
epsConduits = new LinkedList<EventletConduit>();
}
private static void destroyConduit(EventletConduit conduit) {
try {
conduit.destroy();
} catch (Exception de) {
LOG.error("Exception caught while destroying AtomEventHandler. Reason: " + de.getMessage(), de);
}
}
private synchronized List<EventletConduit> copyConduits() {
return new LinkedList<EventletConduit>(epsConduits);
}
private synchronized boolean removeConduit(EventletConduit conduit) {
destroyConduit(conduit);
return epsConduits.remove(conduit);
}
@Override
public CancellationRemote enlistHandler(AtomEventlet handler) {
return enlistHandler(new InstanceContextImpl<AtomEventlet>(NopInstanceEnvironment.getInstance(), handler));
}
@Override
public CancellationRemote enlistHandler(AtomEventlet handler, EntrySelector selector) {
return enlistHandler(new InstanceContextImpl<AtomEventlet>(NopInstanceEnvironment.getInstance(), handler), selector);
}
@Override
public CancellationRemote enlistHandler(InstanceContext<? extends AtomEventlet> handler) {
return enlistHandler(handler, DefaultSelector.instance());
}
@Override
public final synchronized CancellationRemote enlistHandler(InstanceContext<? extends AtomEventlet> handler, EntrySelector selector) {
final CancellationRemote cancellationRemote = reclamationHandler.watch(handler);
final EventletConduit newConduit = new EventletConduit((InstanceContext<AtomEventlet>) handler, cancellationRemote, selector);
epsConduits.add(newConduit);
return newConduit.cancellationRemote();
}
@Override
public void init(AtomTaskContext tc) throws InitializationException {
try {
reclamationHandler = tc.services().firstAvailable(ReclamationHandler.class);
} catch (ServiceUnavailableException sue) {
throw new InitializationException(sue);
}
}
@Override
public void destroy() {
for (EventletConduit conduit : epsConduits) {
destroyConduit(conduit);
}
epsConduits.clear();
}
private void garbageCollect() {
for (EventletConduit conduit : copyConduits()) {
if (conduit.cancellationRemote().canceled()) {
removeConduit(conduit);
}
}
}
@Override
public final SinkResult entry(Entry entry) throws AtomSinkException {
garbageCollect();
for (EventletConduit conduit : copyConduits()) {
final SelectorResult result = conduit.select(entry);
boolean shouldStop = false;
switch (result) {
case PROCESS:
conduit.perform(entry);
break;
case HALT:
shouldStop = true;
break;
case PASS:
default:
}
if (shouldStop) {
break;
}
}
return AtomSinkResult.ok();
}
@Override
public final SinkResult feedPage(Feed page) throws AtomSinkException {
garbageCollect();
SinkResult resultReturn = AtomSinkResult.ok();
for (Entry entry : page.entries()) {
final SinkResult result = entry(entry);
if (entry(entry).action() == SinkAction.HALT) {
resultReturn = result;
break;
}
}
return resultReturn;
}
}