package cz.cuni.mff.d3s.been.manager;
import static cz.cuni.mff.d3s.been.core.task.TaskState.WAITING;
import java.util.Collection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.hazelcast.config.MapConfig;
import com.hazelcast.core.EntryEvent;
import com.hazelcast.core.EntryListener;
import com.hazelcast.core.IMap;
import com.hazelcast.query.SqlPredicate;
import cz.cuni.mff.d3s.been.cluster.ServiceException;
import cz.cuni.mff.d3s.been.cluster.context.ClusterContext;
import cz.cuni.mff.d3s.been.core.task.TaskEntries;
import cz.cuni.mff.d3s.been.core.task.TaskEntry;
import cz.cuni.mff.d3s.been.core.task.TaskState;
import cz.cuni.mff.d3s.been.manager.msg.Messages;
import cz.cuni.mff.d3s.been.manager.msg.TaskMessage;
import cz.cuni.mff.d3s.been.mq.IMessageSender;
import cz.cuni.mff.d3s.been.mq.MessagingException;
/**
* Listens for local key events of the Task Map.
*
*
* @author Martin Sixta
*/
final class LocalTaskListener extends TaskManagerService implements EntryListener<String, TaskEntry> {
/** logging */
private static final Logger log = LoggerFactory.getLogger(LocalTaskListener.class);
/** Format of "tasks waiting for a task to finish" query */
private static final String WAITING_TASKS_FMT = "taskContextId = '%s' AND taskDependency = '%s'";
/** task map */
private IMap<String, TaskEntry> taskMap;
/** Connection to the cluster */
private ClusterContext clusterCtx;
/** sender of "in-task manager" messages */
private IMessageSender<TaskMessage> sender;
/**
* Creates LocalTaskListener
*
* @param clusterCtx
* connection to the cluster.
*/
public LocalTaskListener(ClusterContext clusterCtx) {
this.clusterCtx = clusterCtx;
taskMap = clusterCtx.getTasks().getTasksMap();
MapConfig cfg = clusterCtx.getTasks().getTasksMapConfig();
if (cfg == null) {
throw new RuntimeException("BEEN_MAP_TASKS! does not have a config!");
}
if (cfg.isCacheValue()) {
throw new RuntimeException("Cache value == true for BEEN_MAP_TASKS!");
}
}
@Override
public void start() throws ServiceException {
sender = createSender();
taskMap.addLocalEntryListener(this);
}
@Override
public void stop() {
taskMap.removeEntryListener(this);
sender.close();
}
@Override
public synchronized void entryAdded(EntryEvent<String, TaskEntry> event) {
log.debug("TaskEntry {} added", event.getKey());
TaskEntry entry = event.getValue();
if (entry.isSetTaskDependency()) {
String dep = entry.getTaskDependency();
TaskEntries.setState(entry, WAITING, "Waiting for task %s to finish", dep);
clusterCtx.getTasks().putTask(entry);
return;
}
try {
TaskMessage msg = Messages.createNewTaskMessage(entry);
sender.send(msg);
} catch (MessagingException e) {
String msg = String.format("Cannot send message to '%s'", sender.getConnection());
log.error(msg, e);
}
}
@Override
public synchronized void entryRemoved(EntryEvent<String, TaskEntry> event) {
log.debug("TaskEntry {} removed ", event.getKey());
}
@Override
public synchronized void entryUpdated(EntryEvent<String, TaskEntry> event) {
log.debug("TaskEntry {} updated", event.getKey());
TaskEntry entry = event.getValue();
TaskState state = entry.getState();
// skip waiting tasks
if (state == TaskState.WAITING) {
try {
TaskMessage msg = Messages.createCheckSchedulabilityMessage(entry);
sender.send(msg);
} catch (MessagingException e) {
String msg = String.format("Cannot send message to '%s'", sender.getConnection());
log.error(msg, e);
}
return;
}
// schedule dependent tasks if any
if (state == TaskState.FINISHED || state == TaskState.ABORTED) {
scheduleWaitingTasks(entry);
}
try {
TaskMessage msg = Messages.createTaskChangedMessage(entry);
sender.send(msg);
} catch (MessagingException e) {
String msg = String.format("Cannot send message to '%s'", sender.getConnection());
log.error(msg, e);
}
}
@Override
public synchronized void entryEvicted(EntryEvent<String, TaskEntry> event) {
log.debug("TaskEntry {} evicted", event.getKey());
}
/**
*
* Schedules tasks dependent on the finished task
*
* @param entry
* the task that has finished
*/
private void scheduleWaitingTasks(TaskEntry entry) {
String query = String.format(WAITING_TASKS_FMT, entry.getTaskContextId(), entry.getId());
SqlPredicate predicate = new SqlPredicate(query);
try {
Collection<TaskEntry> entries = taskMap.values(predicate);
for (TaskEntry e : entries) {
sender.send(Messages.createScheduleTaskMessage(e));
}
} catch (Exception e) {
log.error("Cannot schedule a waiting task", e);
}
}
}