package org.batfish.coordinator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.batfish.common.BatfishLogger;
import org.batfish.common.BfConsts;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONObject;
public class PoolMgr {
final class WorkerStatusRefreshTask implements Runnable {
@Override
public void run() {
Main.getPoolMgr().refreshWorkerStatus();
}
}
private BatfishLogger _logger;
// the key should be of the form <ip or hostname>:<port>
private HashMap<String, WorkerStatus> workerPool;
public PoolMgr(BatfishLogger logger) {
_logger = logger;
workerPool = new HashMap<>();
}
public synchronized void addToPool(final String worker) {
// start out as unknown and trigger refresh in the background
workerPool.put(worker, new WorkerStatus(WorkerStatus.StatusCode.UNKNOWN));
Thread thread = new Thread() {
@Override
public void run() {
refreshWorkerStatus(worker);
}
};
thread.start();
}
public synchronized void deleteFromPool(String worker) {
if (workerPool.containsKey(worker)) {
workerPool.remove(worker);
}
}
private synchronized List<String> getAllWorkers() {
List<String> workers = new LinkedList<>();
for (String worker : workerPool.keySet()) {
workers.add(worker);
}
return workers;
}
public synchronized HashMap<String, String> getPoolStatus() {
HashMap<String, String> copy = new HashMap<>();
for (Entry<String, WorkerStatus> entry : workerPool.entrySet()) {
copy.put(entry.getKey(), entry.getValue().toString());
}
return copy;
}
public synchronized String getWorkerForAssignment() {
for (Entry<String, WorkerStatus> workerEntry : workerPool.entrySet()) {
if (workerEntry.getValue()
.getStatus() == WorkerStatus.StatusCode.IDLE) {
updateWorkerStatus(workerEntry.getKey(),
WorkerStatus.StatusCode.TRYINGTOASSIGN);
return workerEntry.getKey();
}
}
return null;
}
public WorkerStatus getWorkerStatus(String worker) {
if (workerPool.containsKey(worker)) {
return workerPool.get(worker);
}
else {
return null;
}
}
public void markAssignmentResult(String worker,
boolean assignmentSuccessful) {
updateWorkerStatus(worker, assignmentSuccessful
? WorkerStatus.StatusCode.BUSY : WorkerStatus.StatusCode.IDLE);
}
public void refreshWorkerStatus() {
// _logger.info("PM:RefreshWorkerStatus: entered\n");
List<String> workers = getAllWorkers();
for (String worker : workers) {
refreshWorkerStatus(worker);
}
}
public void refreshWorkerStatus(String worker) {
// _logger.debug("PM:RefreshWorkerStatus: refreshing status of " + worker
// +"\n");
try {
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target(String.format("http://%s%s/%s",
worker, BfConsts.SVC_BASE_RSC, BfConsts.SVC_GET_STATUS_RSC));
Invocation.Builder invocationBuilder = webTarget
.request(MediaType.APPLICATION_JSON);
Response response = invocationBuilder.get();
// _logger.debug(webTarget.getUri());
if (response.getStatus() != Response.Status.OK.getStatusCode()) {
_logger.error("PM:RefreshWorkerStatus: Got non-OK response "
+ response.getStatus() + "\n");
}
else {
String sobj = response.readEntity(String.class);
JSONArray array = new JSONArray(sobj);
// _logger.info(String.format("response: %s [%s] [%s]\n",
// array.toString(), array.get(0), array.get(1)));
if (!array.get(0).equals(BfConsts.SVC_SUCCESS_KEY)) {
_logger.error(
String.format("got error while refreshing status: %s %s\n",
array.get(0), array.get(1)));
updateWorkerStatus(worker, WorkerStatus.StatusCode.UNKNOWN);
return;
}
JSONObject jObj = new JSONObject(array.get(1).toString());
if (!jObj.has("idle")) {
_logger.error(
String.format("did not see idle key in json response\n"));
updateWorkerStatus(worker, WorkerStatus.StatusCode.UNKNOWN);
return;
}
boolean status = jObj.getBoolean("idle");
// update the status, except leave the ones with TRYINGTOASSIGN
// alone
if (getWorkerStatus(worker)
.getStatus() != WorkerStatus.StatusCode.TRYINGTOASSIGN) {
updateWorkerStatus(worker, status ? WorkerStatus.StatusCode.IDLE
: WorkerStatus.StatusCode.BUSY);
}
}
}
catch (ProcessingException e) {
_logger.error(String.format("unable to connect to %s: %s\n", worker,
e.getMessage()));
updateWorkerStatus(worker, WorkerStatus.StatusCode.UNREACHABLE);
}
catch (Exception e) {
String stackTrace = ExceptionUtils.getFullStackTrace(e);
_logger.error(String.format("exception: %s\n", stackTrace));
updateWorkerStatus(worker, WorkerStatus.StatusCode.UNKNOWN);
}
}
public void startPoolManager() {
Runnable workerStatusRefreshTask = new WorkerStatusRefreshTask();
Executors.newScheduledThreadPool(1).scheduleWithFixedDelay(
workerStatusRefreshTask, 0,
Main.getSettings().getPeriodWorkerStatusRefreshMs(),
TimeUnit.MILLISECONDS);
}
private synchronized void updateWorkerStatus(String worker,
WorkerStatus.StatusCode statusCode) {
if (workerPool.containsKey(worker)) {
workerPool.get(worker).UpdateStatus(statusCode);
}
}
}