package com.liveramp.hank.ui;
import com.google.common.base.Throwables;
import com.liveramp.hank.coordinator.*;
import org.json.JSONException;
import org.json.JSONObject;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class HankApiServlet extends HttpServlet {
private static class InvalidParamsException extends Exception {
}
private static class Params {
public static final String DOMAIN = "domain";
public static final String DOMAIN_VERSION = "domain_version";
public static final String DOMAIN_GROUP = "domain_group";
public static final String RING_GROUP = "ring_group";
public static final String ALL_RING_GROUPS = "get_all_ring_groups";
public static final String DEPLOY_STATUS_FOR_DOMAIN = "deploy_status_for_domain";
public static final String DEPLOY_STATUS_FOR_DOMAIN_GROUP = "deploy_status_for_domain_group";
public static String[] getParamKeys() {
return new String[]{DOMAIN, DOMAIN_VERSION, DOMAIN_GROUP, RING_GROUP, ALL_RING_GROUPS, DEPLOY_STATUS_FOR_DOMAIN, DEPLOY_STATUS_FOR_DOMAIN_GROUP};
}
public static boolean paramsAreValid(Collection<String> params) {
return paramsMatch(params, DOMAIN) ||
paramsMatch(params, DOMAIN, DOMAIN_VERSION) ||
paramsMatch(params, DEPLOY_STATUS_FOR_DOMAIN) ||
paramsMatch(params, DOMAIN_GROUP) ||
paramsMatch(params, DEPLOY_STATUS_FOR_DOMAIN_GROUP) ||
paramsMatch(params, RING_GROUP) ||
paramsMatch(params, ALL_RING_GROUPS);
}
private static boolean paramsMatch(Collection<String> params, String... expected) {
if (params.size() == expected.length &&
params.containsAll(Arrays.asList(expected))) {
return true;
}
return false;
}
}
public static final String ERROR_INTERNAL_SERVER_ERROR = "Internal Server Error";
public static final String ERROR_INVALID_PARAMETERS = "The combination of parameters submitted is not valid.";
static final String JSON_FORMAT = "application/json;charset=utf-8";
private final Coordinator coordinator;
private final HankApiHelper apiHelper;
public HankApiServlet(Coordinator coordinator) {
this.coordinator = coordinator;
this.apiHelper = new HankApiHelper(coordinator);
}
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
doGet(request, response);
}
/**
* Respond to GET requests.
*/
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
String responseBody;
try {
// Parse request data
Map<String, Object> requestData = parseRequestParams(request, Params.getParamKeys());
Map<String, Object> responseData = getResponseData(requestData);
responseBody = getJsonResponseBody(responseData).toString();
} catch (InvalidParamsException ipe) {
sendResponseInvalidParams(response);
return;
} catch (Throwable t) {
sendResponseInternalServerError(response, t);
return;
}
response.setContentType(JSON_FORMAT);
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().print(responseBody);
}
private void sendResponseError(int errorCode, String errorMessage, HttpServletResponse response) {
response.reset();
response.setContentType("text/plain;charset=utf-8");
response.setStatus(errorCode);
try {
response.getWriter().print(errorMessage);
} catch (IOException e) {
}
}
protected void sendResponseInternalServerError(HttpServletResponse response, Throwable t) {
String error = "";
for (Throwable cause : Throwables.getCausalChain(t)) {
error += cause.getMessage();
error += "\n" + Throwables.getStackTraceAsString(cause);
}
sendResponseError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
ERROR_INTERNAL_SERVER_ERROR + ":\n" + error,
response);
}
protected void sendResponseInvalidParams(HttpServletResponse response) {
sendResponseError(HttpServletResponse.SC_BAD_REQUEST, ERROR_INVALID_PARAMETERS, response);
}
protected Map<String, Object> parseRequestParams(HttpServletRequest request, String[] paramKeys) {
Map<String, Object> params = new HashMap<String, Object>();
Map requestParamsMap = request.getParameterMap();
for (String key : paramKeys) {
if (requestParamsMap.containsKey(key)) {
params.put(key, request.getParameter(key));
}
}
return params;
}
public Map<String, Object> getResponseData(Map<String, Object> requestData) throws IOException, InvalidParamsException {
Map<String, Object> responseData = new HashMap<String, Object>();
if (!Params.paramsAreValid(requestData.keySet())) {
throw new InvalidParamsException();
}
if (requestData.containsKey(Params.DOMAIN)) {
if (requestData.containsKey(Params.DOMAIN_VERSION)) {
addDomainVersionDataToResponse(requestData, responseData);
} else {
addDomainDataToResponse(requestData, responseData);
}
} else if (requestData.containsKey(Params.DEPLOY_STATUS_FOR_DOMAIN)) {
addDomainDeployStatusToResponse(requestData, responseData);
} else if (requestData.containsKey(Params.ALL_RING_GROUPS)) {
addAllRingGroupsDataToResponse(requestData, responseData);
} else if (requestData.containsKey(Params.RING_GROUP)) {
addRingGroupDataToResponse(requestData, responseData);
} else if (requestData.containsKey(Params.DOMAIN_GROUP)) {
addDomainGroupDataToResponse(requestData, responseData);
} else if (requestData.containsKey(Params.DEPLOY_STATUS_FOR_DOMAIN_GROUP)) {
addDomainGroupDeployStatusToResponse(requestData, responseData);
}
return responseData;
}
private void addAllRingGroupsDataToResponse(Map<String, Object> requestData, Map<String, Object> responseData) throws IOException {
for (RingGroup ringGroup : coordinator.getRingGroups()) {
responseData.put(ringGroup.getName(), apiHelper.getRingGroupData(ringGroup).asMap());
}
}
private void addRingGroupDataToResponse(Map<String, Object> requestData, Map<String, Object> responseData) throws IOException {
RingGroup ringGroup = coordinator.getRingGroup((String) requestData.get(Params.RING_GROUP));
if (ringGroup != null) {
responseData.put(ringGroup.getName(), apiHelper.getRingGroupData(ringGroup).asMap());
}
}
private void addDomainDataToResponse(Map<String, Object> requestData, Map<String, Object> responseData) throws IOException {
HankApiHelper.DomainData data = apiHelper.getDomainData((String) requestData.get(Params.DOMAIN));
if (data != null) {
responseData.put(data.name, data.asMap());
}
}
private void addDomainDeployStatusToResponse(Map<String, Object> requestData, Map<String, Object> responseData) throws IOException {
Domain domain = coordinator.getDomain((String) requestData.get(Params.DEPLOY_STATUS_FOR_DOMAIN));
if (domain != null) {
responseData.put(domain.getName(), apiHelper.getDomainDeployStatus(domain).asMap());
}
}
private void addDomainGroupDeployStatusToResponse(Map<String, Object> requestData, Map<String, Object> responseData) throws IOException {
DomainGroup domainGroup = coordinator.getDomainGroup((String) requestData.get(Params.DEPLOY_STATUS_FOR_DOMAIN_GROUP));
if (domainGroup != null) {
responseData.put(domainGroup.getName(), apiHelper.getDomainGroupDeployStatus(domainGroup).asMap());
}
}
private void addDomainVersionDataToResponse(Map<String, Object> requestData, Map<String, Object> responseData) throws IOException {
Domain domain = coordinator.getDomain((String) requestData.get(Params.DOMAIN));
try {
DomainVersion version = domain.getVersion(Integer.valueOf((String) requestData.get(Params.DOMAIN_VERSION)));
responseData.put(String.valueOf(version.getVersionNumber()), apiHelper.getDomainVersionData(version).asMap());
} catch (Exception ignored) {
} // No data added, but no harm done
}
private void addDomainGroupDataToResponse(Map<String, Object> requestData, Map<String, Object> responseData) throws IOException {
DomainGroup domainGroup = coordinator.getDomainGroup((String) requestData.get(Params.DOMAIN_GROUP));
if (domainGroup != null) {
HankApiHelper.DomainGroupData data = apiHelper.getDomainGroupData(domainGroup);
responseData.put(domainGroup.getName(), data.asMap());
}
}
private JSONObject getJsonResponseBody(Map<String, Object> data) throws JSONException, IOException {
JSONObject json = new JSONObject();
for (Map.Entry<String, Object> e : data.entrySet()) {
if (e.getValue() instanceof Map) {
json.put(e.getKey(), getJsonResponseBody((Map<String, Object>) e.getValue()));
} else {
json.put(e.getKey(), e.getValue());
}
}
return json;
}
}