package com.neverwinterdp.scribengin.dataflow;
import java.util.ArrayList;
import java.util.List;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import com.neverwinterdp.registry.BatchOperations;
import com.neverwinterdp.registry.DataMapperCallback;
import com.neverwinterdp.registry.Node;
import com.neverwinterdp.registry.NodeCreateMode;
import com.neverwinterdp.registry.Registry;
import com.neverwinterdp.registry.RegistryException;
import com.neverwinterdp.registry.Transaction;
import com.neverwinterdp.registry.lock.Lock;
import com.neverwinterdp.registry.queue.DistributedQueue;
import com.neverwinterdp.scribengin.dataflow.DataflowTaskDescriptor.Status;
import com.neverwinterdp.scribengin.dataflow.worker.DataflowTaskExecutorDescriptor;
import com.neverwinterdp.util.JSONSerializer;
import com.neverwinterdp.vm.VMDescriptor;
//TODO: Nizar
@Singleton
public class DataflowRegistry {
final static public String TASKS_PATH = "tasks";
final static public String TASKS_DESCRIPTORS_PATH = TASKS_PATH + "/descriptors";
final static public String TASKS_AVAILABLE_PATH = TASKS_PATH + "/executors/available";
final static public String TASKS_ASSIGNED_PATH = TASKS_PATH + "/executors/assigned" ;
final static public String TASKS_FINISHED_PATH = TASKS_PATH + "/executors/finished";
final static public String TASKS_LOCK_PATH = TASKS_PATH + "/executors/locks";
final static public String MASTER_PATH = "master";
final static public String MASTER_LEADER_PATH = MASTER_PATH + "/leader";
final static public String WORKERS_PATH = "workers";
final static public DataMapperCallback<DataflowTaskDescriptor> TASK_DESCRIPTOR_DATA_MAPPER = new DataMapperCallback<DataflowTaskDescriptor>() {
@Override
public DataflowTaskDescriptor map(String path, byte[] data, Class<DataflowTaskDescriptor> type) {
DataflowTaskDescriptor descriptor = JSONSerializer.INSTANCE.fromBytes(data, type);
descriptor.setStoredPath(path);
return descriptor;
}
};
@Inject @Named("dataflow.registry.path")
private String dataflowPath;
@Inject
private Registry registry;
private Node status;
private Node tasksDescriptors;
//private Node tasksAvailable;
private DistributedQueue tasksAvailableQueue;
private Node tasksAssigned;
private Node tasksFinished;
private Node tasksLock;
private Node workers;
public DataflowRegistry() { }
public DataflowRegistry(Registry registry, String dataflowPath) throws Exception {
this.registry = registry;
this.dataflowPath = dataflowPath;
onInit();
}
public String getDataflowPath() { return this.dataflowPath ; }
public String getTasksFinishedPath() { return tasksFinished.getPath() ;}
public String getTasksAssignedPath() { return this.tasksAssigned.getPath(); }
public Registry getRegistry() { return this.registry ; }
public DataflowDescriptor getDataflowDescriptor() throws RegistryException {
return registry.getDataAs(dataflowPath, DataflowDescriptor.class);
}
@Inject
public void onInit() throws Exception {
status = registry.createIfNotExist(dataflowPath + "/status");
tasksDescriptors = registry.createIfNotExist(dataflowPath + "/" + TASKS_DESCRIPTORS_PATH);
tasksAvailableQueue = new DistributedQueue(registry, dataflowPath + "/" + TASKS_AVAILABLE_PATH) ;
tasksAssigned = registry.createIfNotExist(dataflowPath + "/" + TASKS_ASSIGNED_PATH);
tasksFinished = registry.createIfNotExist(dataflowPath + "/" + TASKS_FINISHED_PATH);
tasksLock = registry.createIfNotExist(dataflowPath + "/" + TASKS_LOCK_PATH);
registry.createIfNotExist(dataflowPath + "/" + MASTER_LEADER_PATH);
workers = registry.createIfNotExist(dataflowPath + "/" + WORKERS_PATH);
}
public void addAvailableTask(DataflowTaskDescriptor taskDescriptor) throws RegistryException {
Node taskNode = tasksDescriptors.createChild("task-", NodeCreateMode.PERSISTENT_SEQUENTIAL);
taskDescriptor.setStoredPath(taskNode.getPath());
taskNode.setData(taskDescriptor);
DataflowTaskReport report = new DataflowTaskReport();
report.setStartTime(System.currentTimeMillis());
taskNode.createChild("report", report, NodeCreateMode.PERSISTENT);
String nodeName = taskNode.getName();
tasksAvailableQueue.offer(nodeName.getBytes());
}
public void addWorker(VMDescriptor vmDescriptor) throws RegistryException {
workers.createChildRef(vmDescriptor.getId(), vmDescriptor.getStoredPath(), NodeCreateMode.PERSISTENT);
}
public void setStatus(DataflowLifecycleStatus event) throws RegistryException {
status.setData(event);
}
public DataflowTaskDescriptor assignDataflowTask(final VMDescriptor vmDescriptor) throws RegistryException {
Lock lock = tasksLock.getLock("write") ;
BatchOperations<DataflowTaskDescriptor> getAssignedtaskOp = new BatchOperations<DataflowTaskDescriptor>() {
@Override
public DataflowTaskDescriptor execute(Registry registry) throws RegistryException {
byte[] data = tasksAvailableQueue.poll();
if(data == null) return null;
String taskName = new String(data);
Node childNode = tasksDescriptors.getChild(taskName);
Transaction transaction = registry.getTransaction();
transaction.createChild(tasksAssigned, taskName, NodeCreateMode.PERSISTENT);
transaction.createDescendant(tasksAssigned, taskName + "/heartbeat", vmDescriptor, NodeCreateMode.EPHEMERAL);
transaction.commit();
DataflowTaskDescriptor descriptor = childNode.getDataAs(DataflowTaskDescriptor.class, TASK_DESCRIPTOR_DATA_MAPPER);
return descriptor;
}
};
return lock.execute(getAssignedtaskOp, 5, 1000);
}
public void createTaskExecutor(VMDescriptor vmDescriptor, DataflowTaskExecutorDescriptor descriptor) throws RegistryException {
Node worker = workers.getChild(vmDescriptor.getId()) ;
Node executors = worker.createDescendantIfNotExists("executors");
executors.createChild(descriptor.getId(), descriptor, NodeCreateMode.PERSISTENT);
}
public void updateTaskExecutor(VMDescriptor vmDescriptor, DataflowTaskExecutorDescriptor descriptor) throws RegistryException {
Node worker = workers.getChild(vmDescriptor.getId()) ;
Node executor = worker.getDescendant("executors/" + descriptor.getId()) ;
executor.setData(descriptor);
}
public void dataflowTaskSuspend(final DataflowTaskDescriptor descriptor) throws RegistryException {
Lock lock = tasksLock.getLock("write") ;
BatchOperations<Boolean> suspendtOp = new BatchOperations<Boolean>() {
@Override
public Boolean execute(Registry registry) throws RegistryException {
//TODO: use the transaction
descriptor.setStatus(Status.SUSPENDED);
dataflowTaskUpdate(descriptor);
Node descriptorNode = registry.get(descriptor.getStoredPath()) ;
String name = descriptorNode.getName();
tasksAvailableQueue.offer(name.getBytes());
//tasksAssigned.getChild(name).rdelete();
Transaction transaction = registry.getTransaction();
transaction.rdelete(tasksAssigned.getPath() + "/" + name);
transaction.commit();
return true;
}
};
lock.execute(suspendtOp, 5, 1000);
}
public void dataflowTaskFinish(final DataflowTaskDescriptor descriptor) throws RegistryException {
Lock lock = tasksLock.getLock("write") ;
BatchOperations<Boolean> commitOp = new BatchOperations<Boolean>() {
@Override
public Boolean execute(Registry registry) throws RegistryException {
Node descriptorNode = registry.get(descriptor.getStoredPath()) ;
String name = descriptorNode.getName();
descriptor.setStatus(Status.TERMINATED);
//TODO Transaction: convert to use the transaction
Transaction transaction = registry.getTransaction();
//update the task descriptor
transaction.setData(descriptor.getStoredPath(), descriptor);
transaction.createChild(tasksFinished, name, NodeCreateMode.PERSISTENT);
transaction.rdelete(tasksAssigned.getPath() + "/" + name);
//tasksFinished.createChild(transaction, name, NodeCreateMode.PERSISTENT);
//tasksAssigned.getChild(name).rdelete(transaction);
transaction.commit();
return true;
}
};
lock.execute(commitOp, 5, 1000);
}
public void dataflowTaskUpdate(DataflowTaskDescriptor descriptor) throws RegistryException {
registry.setData(descriptor.getStoredPath(), descriptor);
}
public void dataflowTaskReport(DataflowTaskDescriptor descriptor, DataflowTaskReport report) throws RegistryException {
Node reportNode = registry.get(descriptor.getStoredPath() + "/report");
reportNode.setData(report);
}
public void create(DataflowTaskDescriptor descriptor, DataflowTaskReport report) throws RegistryException {
Node taskNode = registry.get(descriptor.getStoredPath());
taskNode.createChild("report", report, NodeCreateMode.PERSISTENT);
}
public List<DataflowTaskDescriptor> getTaskDescriptors() throws RegistryException {
return tasksDescriptors.getChildrenAs(DataflowTaskDescriptor.class, TASK_DESCRIPTOR_DATA_MAPPER);
}
public DataflowTaskDescriptor getTaskDescriptor(String taskName) throws RegistryException {
return tasksDescriptors.getChild(taskName).getDataAs(DataflowTaskDescriptor.class, TASK_DESCRIPTOR_DATA_MAPPER);
}
public DataflowTaskReport getTaskReport(DataflowTaskDescriptor descriptor) throws RegistryException {
return registry.getDataAs(descriptor.getStoredPath() + "/report", DataflowTaskReport.class) ;
}
public List<DataflowTaskReport> getTaskReports(List<DataflowTaskDescriptor> descriptors) throws RegistryException {
List<String> reportPaths = new ArrayList<String>();
for(int i = 0; i < descriptors.size(); i++) {
reportPaths.add(descriptors.get(i).getStoredPath() + "/report") ;
}
return registry.getDataAs(reportPaths, DataflowTaskReport.class) ;
}
public VMDescriptor getDataflowMaster() throws RegistryException {
String leaderPath = dataflowPath + "/" + MASTER_LEADER_PATH;
Node node = registry.getRef(leaderPath);
return node.getDataAs(VMDescriptor.class);
}
public List<VMDescriptor> getDataflowMasters() throws RegistryException {
return registry.getRefChildrenAs(dataflowPath + "/" + MASTER_PATH, VMDescriptor.class);
}
public List<VMDescriptor> getWorkers() throws RegistryException {
return registry.getRefChildrenAs(dataflowPath + "/" + WORKERS_PATH, VMDescriptor.class);
}
public List<String> getWorkerNames() throws RegistryException {
return workers.getChildren();
}
public List<DataflowTaskExecutorDescriptor> getExecutors(String worker) throws RegistryException {
Node executors = workers.getDescendant(worker + "/executors") ;
return executors.getChildrenAs(DataflowTaskExecutorDescriptor.class);
}
}