package cz.cuni.mff.d3s.been.cluster.action; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; 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.core.task.TaskEntry; import cz.cuni.mff.d3s.been.socketworks.twoway.Replies; import cz.cuni.mff.d3s.been.socketworks.twoway.Reply; import cz.cuni.mff.d3s.been.socketworks.twoway.Request; import cz.cuni.mff.d3s.been.task.checkpoints.CheckpointRequest; /** * An {@link Action} that handles a request for waiting until a task has * finished. * * @author Martin Sixta */ final class TaskStatusWaitAction implements Action { /** the request to handle */ private final Request request; /** BEEN cluster instance */ private final ClusterContext ctx; /** a blocking queue that is used for waiting */ BlockingQueue<Reply> 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 TaskStatusWaitAction(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 TaskWaiter implements EntryListener<String, TaskEntry> { @Override public void entryAdded(EntryEvent<String, TaskEntry> event) { add(event.getValue()); } @Override public void entryUpdated(EntryEvent<String, TaskEntry> event) { add(event.getValue()); } @Override public void entryEvicted(EntryEvent<String, TaskEntry> event) { addFailure("EVICTED"); } @Override public void entryRemoved(EntryEvent<String, TaskEntry> event) { addFailure("REMOVED"); } /** * Creates a positive reply and add the task entry into the blocking queue. * * @param entry * the task entry */ private void add(TaskEntry entry) { Reply reply = createOkReply(entry); queue.add(reply); } /** * Creates a failure reply and add the task entry into the blocking queue. * * @param msg * the message of the error */ private void addFailure(String msg) { Reply reply = Replies.createErrorReply(msg); queue.add(reply); } } @Override public Reply handle() { String taskId = request.getSelector(); if (taskId == null || taskId.isEmpty()) { return Replies.createErrorReply("No such '%s' task id", taskId); } Reply reply = null; final TaskWaiter waiter = new TaskWaiter(); IMap<String, TaskEntry> map = ctx.getTasks().getTasksMap(); map.addEntryListener(waiter, taskId, true); TaskEntry value = map.get(taskId); if (value == null) { try { reply = queue.poll(request.getTimeout(), TimeUnit.MILLISECONDS); } catch (InterruptedException e) { // TODO error msg? e.printStackTrace(); } } if (reply == null) { reply = Replies.createErrorReply("TIMEOUT"); } map.removeEntryListener(waiter); queue.clear(); return reply; } /** * Creates a positive reply with the specified task entry. * * @param entry * the task entry * @return a new positive reply */ private Reply createOkReply(TaskEntry entry) { return Replies.createOkReply(entry.getState().toString()); } }