package cz.cuni.mff.d3s.been.cluster.action;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.hazelcast.core.EntryEvent;
import com.hazelcast.core.EntryListener;
import com.hazelcast.core.IMap;
import cz.cuni.mff.d3s.been.cluster.context.ClusterContext;
import cz.cuni.mff.d3s.been.socketworks.twoway.Replies;
import cz.cuni.mff.d3s.been.socketworks.twoway.Reply;
import cz.cuni.mff.d3s.been.task.checkpoints.CheckpointRequest;
/**
* An {@link Action} that handles a request for waiting until a value is
* assigned to a key in the checkpoint map.
*
* @author Martin Sixta
*/
final class MapWaitAction implements Action {
/** slf4j logger */
private static final Logger log = LoggerFactory.getLogger(MapWaitAction.class);
/** the request to handle */
private final CheckpointRequest request;
/** BEEN cluster instance */
private final ClusterContext ctx;
/** a blocking queue that will be used for the waiting operation */
BlockingQueue<String> queue = new LinkedBlockingQueue<>();
/**
* Default constructor, creates the action with the specified request and
* cluster context.
*
* @param request
* the request to handle
* @param ctx
* the cluster context
*/
public MapWaitAction(CheckpointRequest request, ClusterContext ctx) {
this.request = request;
this.ctx = ctx;
}
/**
* A helper class which implements a listener for a specified Hazelcast map
* entry and adds it into the blocking queue when the event occurs.
*/
class MapWaiter implements EntryListener<String, String> {
@Override
public void entryAdded(EntryEvent<String, String> event) {
queue.add(event.getValue());
}
@Override
public void entryRemoved(EntryEvent<String, String> event) {}
@Override
public void entryUpdated(EntryEvent<String, String> event) {
queue.add(event.getValue());
}
@Override
public void entryEvicted(EntryEvent<String, String> event) {}
}
@Override
public Reply handle() {
String map = Actions.checkpointMapNameForRequest(request);
String key = request.getSelector();
//if (!ctx.containsInstance(Instance.InstanceType.MAP, map)) {
// return Replies.createErrorReply("No such map %s", map);
//}
Reply reply = null;
final MapWaiter waiter = new MapWaiter();
IMap<String, String> iMap = ctx.getMap(map);
iMap.addEntryListener(waiter, key, true);
String value = iMap.get(key);
boolean timeout = false;
if (value == null) {
try {
if (request.getTimeout() <= 0) {
value = queue.take();
} else {
value = queue.poll(request.getTimeout(), TimeUnit.MILLISECONDS);
if (value == null) {
timeout = true;
}
}
} catch (InterruptedException e) {
log.warn("Poll interrupted", e);
}
}
if (value == null) {
if (timeout) {
reply = Replies.createErrorReply("TIMEOUT");
} else {
reply = Replies.createErrorReply("Unknown error");
}
} else {
reply = Replies.createOkReply(value);
}
iMap.removeEntryListener(waiter);
queue.clear();
return reply;
}
}