package com.zillabyte.motherbrain.coordination.mock;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.log4j.Logger;
import org.javatuples.Pair;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.zillabyte.motherbrain.coordination.AskHandler;
import com.zillabyte.motherbrain.coordination.AskWrapper;
import com.zillabyte.motherbrain.coordination.CoordinationException;
import com.zillabyte.motherbrain.coordination.CoordinationService;
import com.zillabyte.motherbrain.coordination.MessageHandler;
import com.zillabyte.motherbrain.coordination.Watcher;
import com.zillabyte.motherbrain.utils.Utils;
public class MockStateService implements CoordinationService, AskWrapper.AskableService {
/**
*
*/
private static final long serialVersionUID = -862735335179265034L;
private static Logger _log = Logger.getLogger(MockStateService.class);
public Map<String, byte[]> _state = Maps.newConcurrentMap(); // public for test inspection
public Map<String, Lock> _locks = Maps.newConcurrentMap();
public List<Pair<String, MessageHandler>> _messageWatchers = Lists.newArrayList();
public Integer _defer = 0;
private AskWrapper _askWrapper = new AskWrapper(this);
public class MessageWatcher implements Watcher {
private Pair<String, MessageHandler> _p;
public MessageWatcher(Pair<String, MessageHandler> p) {
_p = p;
}
@Override
public void unsubscribe() {
synchronized(_messageWatchers) {
_messageWatchers.remove(_p);
}
}
}
public MockStateService() {
}
public MockStateService(Map<String, Object> config) {
this();
synchronized (_state) {
for(Entry<String, Object> e : config.entrySet()) {
_state.put(e.getKey(), Utils.serialize(e.getValue()));
}
}
}
@SuppressWarnings("unchecked")
@Override
public <T> T getState(String key, T defaultValue) {
// _log.debug("get state: " + key);
if (_state.containsKey(key)) {
return (T) Utils.deserialize( _state.get(key) );
}
return defaultValue;
}
@Override
public <T> void setState(final String key, final T val) {
//_log.debug("set state: " + key + " " + val);
synchronized(_state) {
_state.put(key, Utils.serialize(val));
}
}
@Override
public void initialize() {
}
@Override
public void shutdown() {
// clear();
}
@Override
public void removeStateWithPrefix(String pattern) {
synchronized(_state) {
for(Entry<String, byte[]> e : new HashSet<Entry<String, byte[]>>(_state.entrySet())) {
if (Utils.matchGlob(e.getKey(), pattern)) {
//_log.debug("removing state : " + e.getKey());
_state.remove(e.getKey());
}
}
}
}
@Override
public boolean hasState(String key) {
return _state.containsKey(key);
}
public List<Pair<String, Object>> queryStates(String query) {
List<Pair<String, Object>> list = Lists.newArrayList();
synchronized (_state) {
for(Entry<String, byte[]> e : new HashSet<Entry<String, byte[]>>(_state.entrySet())) {
if (Utils.matchGlob(e.getKey(), query)) {
list.add(new Pair<String, Object>(e.getKey(), Utils.deserialize(e.getValue())));
}
}
}
return list;
}
@Override
public com.zillabyte.motherbrain.coordination.Lock lock(final String name, long timeout, long duration) {
synchronized(_locks) {
if (_locks.containsKey(name) == false) {
// _log.info("lock doesn't exist for "+name+" yet. putting in hash.");
_locks.put(name, new ReentrantLock());
}
}
// _log.info("trying to lock: " + name);
_locks.get(name).lock();
// _log.info("lock success: " + name);
return(new com.zillabyte.motherbrain.coordination.Lock() {
@Override
public void release() throws CoordinationException {
Lock l = _locks.get(name);
if (l == null) throw new NullPointerException("tried to unlock non existant lock: " + name);
// _log.info("unlock: " + name);
try {
l.unlock();
} catch (IllegalMonitorStateException e) {
_log.warn("illegal unlock: " + e);
}
// _log.info("lock released: " + name);
}
});
}
@Override
public void clear() {
synchronized(_state) {
_state.clear();
}
synchronized(_messageWatchers) {
_messageWatchers.clear();
}
synchronized(_locks) {
_locks.clear();
}
}
@Override
public void sendMessage(final String stream, final Object message) {
maybeDefer(new Runnable() {
@Override
public void run() {
for(Pair<String, MessageHandler> p : globSelect(stream, _messageWatchers)) {
// _log.info("sending message: " + stream + " obj: " + message);
try {
handleNewMessage(stream, message, p.getValue1());
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
@Override
public Watcher watchForMessage(ExecutorService exec, String stream, MessageHandler messageHandler) {
synchronized(_messageWatchers) {
Pair<String, MessageHandler> p = new Pair<String, MessageHandler>(stream, messageHandler);
_messageWatchers.add(p);
return new MessageWatcher(p);
}
}
/***
* Use to (optionally) defer all callbacks
* @param fn
*/
private void maybeDefer(final Runnable fn) {
Utils.schedule(_defer, fn);
}
/***
* Used to select patterns based on globs
* @param key
* @param list
* @return
*/
private <T> List<Pair<String, T>> globSelect(String key, List<Pair<String, T>> list) {
List<Pair<String, T>> retList = Lists.newArrayList();
synchronized(list) {
for(Pair<String, T> p : list) {
if (Utils.matchGlob(key, p.getValue0())) {
retList.add(p);
}
}
}
return retList;
}
public int getNumberOfMessageListeners(String stream) {
return globSelect(stream, _messageWatchers).size();
}
@Override
public void sendTransactionalMessage(ExecutorService exec, String channel, Object message, long timeout) throws CoordinationException, TimeoutException {
_askWrapper.sendTransactionalMessage(exec, channel, message, timeout);
}
@Override
public Watcher watchForAsk(ExecutorService exec, String channel, AskHandler askHandler) throws CoordinationException {
return _askWrapper.watchForAsk(exec, channel, askHandler);
}
@Override
public Object ask(ExecutorService exec, String channel, Object message, long timeout) throws CoordinationException, TimeoutException {
return _askWrapper.ask(exec, channel, message, timeout);
}
@Override
public void handleNewMessage(String key, Object message, MessageHandler handler) throws CoordinationException {
_askWrapper.handleNewMessage(key, message, handler);
}
}