package com.hubspot.baragon.watcher;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.ThreadFactory;
import com.google.common.base.Function;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.hubspot.baragon.models.BaragonServiceState;
import com.hubspot.ringleader.watcher.Event;
import com.hubspot.ringleader.watcher.EventListener;
import com.hubspot.ringleader.watcher.PersistentWatcher;
import org.apache.curator.framework.listen.ListenerContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class BaragonStateWatcher {
private static final Logger LOG = LoggerFactory.getLogger(BaragonStateWatcher.class);
private final BaragonStateFetcher stateFetcher;
private final ListenerContainer<BaragonStateListener> listenerContainer;
private final ExecutorService executor;
private final BlockingQueue<Integer> versionQueue;
@Inject
public BaragonStateWatcher(final BaragonStateFetcher stateFetcher,
Set<BaragonStateListener> listeners,
@Baragon PersistentWatcher watcher) {
this.stateFetcher = stateFetcher;
this.listenerContainer = new ListenerContainer<>();
this.executor = newExecutor();
this.versionQueue = new LinkedTransferQueue<>();
for (BaragonStateListener listener : listeners) {
listenerContainer.addListener(listener);
}
watcher.getEventListenable().addListener(new EventListener() {
@Override
public void newEvent(Event event) {
switch (event.getType()) {
case NODE_UPDATED:
int version = event.getStat().getVersion();
versionQueue.add(version);
executor.submit(new Runnable() {
@Override
public void run() {
updateToLatestVersion();
}
});
break;
case NODE_DELETED:
LOG.warn("Baragon state node was deleted");
break;
default:
LOG.warn("Unrecognized event type {}", event.getType());
break;
}
}
}, executor);
watcher.start();
}
private void updateToLatestVersion() {
Deque<Integer> versions = new ArrayDeque<>();
versionQueue.drainTo(versions);
if (!versions.isEmpty()) {
int latestVersion = versions.getLast();
final Collection<BaragonServiceState> newState = stateFetcher.fetchState(latestVersion);
listenerContainer.forEach(new Function<BaragonStateListener, Void>() {
@Override
public Void apply(BaragonStateListener listener) {
listener.stateChanged(newState);
return null;
}
});
}
}
private ExecutorService newExecutor() {
return Executors.newSingleThreadExecutor(new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = Executors.defaultThreadFactory().newThread(r);
thread.setName("BaragonStateWatcher");
thread.setDaemon(true);
return thread;
}
});
}
}