package com.dgrid.transport;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.dgrid.errors.TransportException;
import com.dgrid.gen.Host;
import com.dgrid.gen.InvalidApiKey;
import com.dgrid.gen.InvalidHost;
import com.dgrid.gen.InvalidJobId;
import com.dgrid.gen.InvalidJobletId;
import com.dgrid.gen.JOB_STATUS;
import com.dgrid.gen.Job;
import com.dgrid.gen.Joblet;
import com.dgrid.gen.JobletResult;
import com.dgrid.gen.NoHostAvailable;
import com.dgrid.gen.NoWorkAvailable;
import com.dgrid.helpers.MemcachedHelper;
import com.dgrid.service.DGridTransport;
public class DGridMemcachedTransport implements DGridTransport {
private static final String HOSTS = "hosts";
private static final String HOST_REFERENCES = "hosts.references";
private static final String SETTINGS = "settings";
private static final String HOST_SETTINGS = "settings.hosts";
private static final String JOBS = "jobs";
private static final String JOBLETS = "joblets";
private static final String SAVED_JOBLETS = "joblets.saved";
private static final String JOBLET_REFERENCES = "joblets.references";
private static final String SAVED_JOBLET_REFERENCES = "joblets.saved.references";
private static final String JOBLET_RESULTS = "joblets.results";
private static final String JOBLET_QUEUE = "joblets.queue";
private static final String SAVED_JOBLET_QUEUE = "joblets.saved.queue";
private Log log = LogFactory.getLog(getClass());
private MemcachedHelper mcc;
public void setMemcachedHelper(MemcachedHelper mcc) {
this.mcc = mcc;
}
public void setApiKey(String apiKey) {
log.trace("setApiKey()");
}
public void setEndpoint(String endpoint) {
log.trace("setEndpoint()");
}
public void setPort(int port) {
log.trace("setPort()");
}
public void completeJoblet(int jobletId, JobletResult result,
String logMessage) throws TransportException, InvalidApiKey,
InvalidJobletId {
log.trace("completeJoblet()");
Joblet joblet = (Joblet) mcc.get(getKey(JOBLETS, jobletId));
if (joblet == null)
throw new InvalidJobletId();
// save the result
result.setId(jobletId);
result.setTimeCreated(System.currentTimeMillis());
result.setJoblet(null);
mcc.set(getKey(JOBLET_RESULTS, result.getId()), result);
// update joblet status
joblet.setStatus(result.getStatus());
mcc.set(getKey(JOBLETS, jobletId), joblet);
// get the job
Job job = (Job) mcc.get(getKey(JOBS, joblet.getJobId()));
if (job == null)
throw new InvalidJobletId();
// TODO:
// check for job completion
boolean isCompleted = true;
boolean success = false;
int[] jobletIds = (int[]) mcc.get(getKey(JOBLET_REFERENCES, joblet
.getJobId()));
List<Integer> savedJobs = new ArrayList<Integer>(0);
for (int i = 0; i < jobletIds.length; ++i) {
Joblet j = (Joblet) mcc.get(getKey(JOBLETS, jobletIds[i]));
int status = j.getStatus();
// if status is received, queued or processing, we're still chugging
// along
if ((status >= JOB_STATUS.RECEIVED)
&& (status <= JOB_STATUS.PROCESSING)) {
isCompleted = false;
break;
} else if (status == JOB_STATUS.SAVED) {
savedJobs.add(new Integer(j.getId()));
} else if (status == JOB_STATUS.COMPLETED) {
success = true;
}
}
if (isCompleted && (savedJobs.isEmpty())) {
// completed, no saved jobs to execute
// set job status and perform any callbacks
int status = (success) ? JOB_STATUS.COMPLETED : JOB_STATUS.FAILED;
job.setStatus(status);
mcc.set(getKey(JOBS, job.getId()), job);
} else if (isCompleted) {
// completed but have saved jobs to exec
for (Integer i : savedJobs) {
Joblet j = (Joblet) mcc
.get(getKey(SAVED_JOBLETS, i.intValue()));
int oldJobletId = j.getId();
j.setStatus(JOB_STATUS.RECEIVED);
j.setId(createId(JOBLETS));
// post to joblets
mcc.set(getKey(JOBLETS, j.getId()), j);
// delete from saved joblets
mcc.delete(getKey(SAVED_JOBLETS, oldJobletId));
}
}
// complete the job
}
public Host getHost(int id) throws TransportException, InvalidApiKey,
InvalidHost {
log.trace("getHost()");
Host host = (Host) mcc.get(getKey(HOSTS, id));
if (host == null)
throw new InvalidHost();
else
return host;
}
public Host getHostByName(String hostname) throws TransportException,
InvalidApiKey, InvalidHost {
log.trace("getHostByName()");
String hostid = (String) mcc.get(getKey(HOST_REFERENCES, hostname));
if (hostid == null) {
throw new InvalidHost();
} else {
Host host = (Host) mcc.get(getKey(HOSTS, Integer.parseInt(hostid)));
if (host == null) {
throw new InvalidHost();
} else {
return host;
}
}
}
public String getHostSetting(int hostid, String name, String defaultValue)
throws TransportException, InvalidApiKey, InvalidHost {
log.trace("getHostSetting()");
String key = getKey(HOST_SETTINGS, String.format("%1$d.%2$s", hostid,
name));
String value = (String) mcc.get(key);
if (value == null) {
mcc.set(key, defaultValue);
value = defaultValue;
}
return value;
}
public Job getJob(int jobId) throws TransportException, InvalidApiKey,
InvalidJobId {
log.trace("getJob()");
Job job = (Job) mcc.get(getKey(JOBS, jobId));
if (job == null)
throw new InvalidJobId();
else {
// need to fill in joblets
int[] jobletIds = (int[]) mcc.get(getKey(JOBLET_REFERENCES, jobId));
if (jobletIds == null) {
job.setJoblets(Collections.EMPTY_LIST);
} else {
List<Joblet> joblets = new ArrayList<Joblet>(jobletIds.length);
for (int i = 0; i < jobletIds.length; ++i) {
Joblet joblet = (Joblet) mcc.get(getKey(JOBLETS,
jobletIds[i]));
joblets.add(joblet);
}
job.setJoblets(joblets);
}
}
return job;
}
public int getJobletQueueSize() throws TransportException, InvalidApiKey {
log.trace("getJobletQueueSize()");
throw new NoSuchMethodError();
}
public List<Joblet> listActiveJoblets(String submitter, int offset,
int limit) throws TransportException, InvalidApiKey {
log.trace("getJobletQueueSize()");
throw new NoSuchMethodError();
}
public JobletResult getJobletResult(int jobletId)
throws TransportException, InvalidApiKey, InvalidJobletId {
log.trace("getJobletResult()");
JobletResult result = (JobletResult) mcc.get(getKey(JOBLET_RESULTS,
jobletId));
if (result == null)
throw new InvalidJobletId();
else {
// set joblet reference
Joblet joblet = (Joblet) mcc.get(getKey(JOBLETS, jobletId));
result.setJoblet(joblet);
}
return result;
}
public List<JobletResult> getResults(int jobId) throws TransportException,
InvalidApiKey, InvalidJobId {
log.trace("getResults()");
int[] jobletIds = (int[]) mcc.get(getKey(JOBLET_REFERENCES, jobId));
if (jobletIds == null)
throw new InvalidJobId();
else {
List<JobletResult> results = new ArrayList<JobletResult>(
jobletIds.length);
for (int i = 0; i < jobletIds.length; ++i) {
JobletResult jr = (JobletResult) mcc.get(getKey(JOBLET_RESULTS,
jobletIds[i]));
if (jr != null) {
// set joblet reference
Joblet joblet = (Joblet) mcc.get(getKey(JOBLETS,
jobletIds[i]));
jr.setJoblet(joblet);
results.add(jr);
}
}
return results;
}
}
public String getSetting(String name, String defaultValue)
throws TransportException, InvalidApiKey {
log.trace("getSetting()");
String key = getKey(SETTINGS, name);
String value = (String) mcc.get(key);
if (value == null) {
mcc.set(key, defaultValue);
value = defaultValue;
}
return value;
}
public Joblet getWork() throws TransportException, InvalidApiKey,
InvalidHost, NoWorkAvailable {
log.trace("getWork()");
int counter = getId(JOBLET_QUEUE);
if (counter < 0) {
throw new NoWorkAvailable();
} else {
// get the joblet at named counter
Joblet joblet = (Joblet) mcc.get(getKey(JOBLETS, counter));
if (joblet != null) {
// update joblet status
joblet.setStatus(JOB_STATUS.QUEUED);
mcc.set(getKey(JOBLETS, joblet.getId()), joblet);
// increment counter
int next = createId(JOBLET_QUEUE);
return joblet;
} else {
throw new NoWorkAvailable();
}
}
}
public JobletResult gridExecute(Joblet joblet, int retries)
throws InvalidApiKey, TransportException, NoHostAvailable {
log.trace("gridExecute()");
// TODO Auto-generated method stub
return null;
}
public void log(int jobletId, int jobletStatus, String message)
throws TransportException, InvalidApiKey, InvalidJobletId {
log.trace("log()");
// TODO Auto-generated method stub
}
public Host registerHost(String hostname) throws TransportException,
InvalidApiKey, InvalidHost {
log.trace("registerHost()");
Host host = null;
String hostid = (String) mcc.get(getKey(HOST_REFERENCES, hostname));
if (hostid == null) {
int id = createId(HOSTS);
host = new Host(id, hostname, new HashMap<String, String>());
mcc.set(getKey(HOSTS, id), host);
// register alias from hostname -> id
mcc.set(getKey(HOST_REFERENCES, hostname), id);
} else {
int id = Integer.parseInt(hostid);
host = (Host) mcc.get(getKey(HOSTS, id));
if (host == null) {
// strange - have an id reference but no matching host
host = new Host(id, hostname, new HashMap<String, String>());
mcc.set(getKey(HOSTS, id), host);
}
}
return host;
}
public void releaseJoblet(int jobletId) throws InvalidApiKey,
TransportException, InvalidJobletId {
log.trace("releaseJoblet()");
throw new NoSuchMethodError();
}
public void setHostFacts(int hostid, Map<String, String> facts)
throws TransportException, InvalidApiKey, InvalidHost {
log.trace("setHostFacts()");
Host host = (Host) mcc.get(getKey(HOSTS, hostid));
if (host == null) {
throw new InvalidHost();
} else {
host.getFacts().putAll(facts);
mcc.set(getKey(HOSTS, hostid), host);
}
}
public Job submitJob(Job job) throws TransportException, InvalidApiKey {
log.trace("submitJob()");
int jobId = createId(JOBS);
job.setId(jobId);
job.setTimeCreated(System.currentTimeMillis());
mcc.set(getKey(JOBS, jobId), job);
int[] jobletReferences = new int[job.getJoblets().size()];
for (int i = 0; i < jobletReferences.length; ++i) {
Joblet joblet = job.getJoblets().get(i);
int jobletId = createId(JOBLETS);
joblet.setId(jobletId);
joblet.setJobId(jobId);
joblet.setTimeCreated(job.getTimeCreated());
mcc.set(getKey(JOBLETS, jobletId), joblet);
jobletReferences[i] = jobletId;
if (jobletId == 1) {
// set the work queue counter to at least 1
createId(JOBLET_QUEUE);
}
}
// populate job -> joblets reference
mcc.set(getKey(JOBLET_REFERENCES, jobId), jobletReferences);
return job;
}
public Joblet submitJoblet(Joblet joblet, int jobId, int callbackType,
String callbackAddress, String callbackContent)
throws TransportException, InvalidApiKey, InvalidJobId {
log.trace("submitJoblet()");
String queueKey = (joblet.getStatus() == JOB_STATUS.SAVED) ? SAVED_JOBLETS
: JOBLETS;
String referenceKey = (joblet.getStatus() == JOB_STATUS.SAVED) ? SAVED_JOBLET_REFERENCES
: JOBLET_REFERENCES;
String queueCounterKey = (joblet.getStatus() == JOB_STATUS.SAVED) ? SAVED_JOBLET_QUEUE
: JOBLET_QUEUE;
int jobletId = createId(queueKey);
joblet.setId(jobletId);
joblet.setTimeCreated(System.currentTimeMillis());
if (jobId == 0) {
// create the job
jobId = createId(JOBS);
Job job = new Job(jobId, joblet.getTimeCreated(), null, null, null,
callbackType, callbackAddress, callbackContent,
JOB_STATUS.RECEIVED);
mcc.set(getKey(JOBS, jobId), job);
// populate job -> joblets reference
mcc.set(getKey(referenceKey, jobId), new int[] { jobletId });
} else {
int[] jobletIds = (int[]) mcc.get(getKey(referenceKey, jobId));
int[] newJobletIds = new int[jobletIds.length];
for (int i = 0; i < jobletIds.length; ++i) {
newJobletIds[i] = jobletIds[i];
}
newJobletIds[newJobletIds.length - 1] = jobletId;
}
joblet.setJobId(jobId);
mcc.set(getKey(queueKey, jobletId), joblet);
if (jobletId == 1) {
// set the work queue counter to at least 1
createId(queueCounterKey);
}
return joblet;
}
private List<Joblet> getJoblets(int jobId, String referenceKey)
throws InvalidJobId {
log.trace("getJoblets()");
int[] jobletIds = (int[]) mcc.get(getKey(referenceKey, jobId));
if (jobletIds == null)
throw new InvalidJobId();
else {
List<Joblet> joblets = new ArrayList<Joblet>(jobletIds.length);
for (int i = 0; i < jobletIds.length; ++i) {
Joblet joblet = (Joblet) mcc.get(getKey(JOBLETS, jobletIds[i]));
joblets.add(joblet);
}
return joblets;
}
}
private int getId(String classification) {
log.trace("getId()");
return (int) mcc.getCounter(getKey(classification, "counter"));
}
private int createId(String classification) {
log.trace("createId()");
String key = getKey(classification, "counter");
return (int) mcc.addOrIncr(key, 1l);
}
private String getKey(String classification, int id) {
return getKey(classification, Integer.toString(id));
}
private String getKey(String classification, String id) {
return String.format("dgrid.%1$s.%2$s", classification, id);
}
}