package com.linkedin.pinot.controller.api.restlet.resources; import com.alibaba.fastjson.JSONObject; import com.linkedin.pinot.common.Utils; import com.linkedin.pinot.common.utils.CommonConstants.Helix.TableType; import com.linkedin.pinot.common.utils.NetUtil; import com.linkedin.pinot.controller.ControllerConf; import com.linkedin.pinot.controller.helix.core.PinotHelixResourceManager; import java.util.Map; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executor; import org.apache.commons.httpclient.HttpConnectionManager; import org.apache.commons.lang3.exception.ExceptionUtils; import org.restlet.Response; import org.restlet.data.MediaType; import org.restlet.data.Status; import org.restlet.engine.header.Header; import org.restlet.engine.header.HeaderConstants; import org.restlet.representation.StringRepresentation; import org.restlet.resource.ServerResource; import org.restlet.util.Series; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Base class for Controller restlet resource apis. */ public class BasePinotControllerRestletResource extends ServerResource { private static final Logger LOGGER = LoggerFactory.getLogger(BasePinotControllerRestletResource.class); protected static final String TABLE_NAME = "tableName"; protected static final String TABLE_TYPE = "type"; protected static final String INSTANCE_NAME = "instanceName"; protected static final String SEGMENT_NAME = "segmentName"; protected static final String STATE = "state"; protected final static String INVALID_STATE_ERROR = "Error: Invalid state, must be one of {enable|disable|drop}'."; protected final static String INVALID_INSTANCE_URI_ERROR = "Incorrect URI: must be of form: /instances/{instanceName}[?{state}={enable|disable|drop}]"; protected final static String INVALID_TABLE_TYPE_ERROR = "Error: Invalid table type, must be one of {offline|realtime}'."; private final static String HDR_CONTROLLER_HOST = "Pinot-Controller-Host"; private static String controllerHostName = null; private final static String HDR_CONTROLLER_VERSION = "Pinot-Controller-Version"; private final static String CONTROLLER_COMPONENT = "pinot-controller"; private static String controllerVersion = null; static enum StateType { ENABLE, DISABLE, DROP } protected final ControllerConf _controllerConf; protected final PinotHelixResourceManager _pinotHelixResourceManager; protected final Executor executor; protected final HttpConnectionManager connectionManager; private static String getHostName() { if (controllerHostName != null) { return controllerHostName; } controllerHostName = NetUtil.getHostnameOrAddress(); if (controllerHostName == null) { return "Unknown"; } return controllerHostName; } private static String getControllerVersion() { if (controllerVersion != null) { return controllerVersion; } Map<String, String> versions = Utils.getComponentVersions(); controllerVersion = versions.get(CONTROLLER_COMPONENT); if (controllerVersion == null) { controllerVersion = "Unknown"; } return controllerVersion; } public BasePinotControllerRestletResource() { ConcurrentMap<String, Object> appAttributes = getApplication().getContext().getAttributes(); _controllerConf = (ControllerConf) appAttributes.get(ControllerConf.class.toString()); _pinotHelixResourceManager = (PinotHelixResourceManager) appAttributes.get(PinotHelixResourceManager.class.toString()); connectionManager = (HttpConnectionManager) appAttributes.get(HttpConnectionManager.class.toString()); executor = (Executor) appAttributes.get(Executor.class.toString()); } /** * Check if state is one of {enable|disable|drop} * @param state; State string to be checked * @return True if state is one of {enable|disable|drop}, false otherwise. */ protected static boolean isValidState(String state) { return (StateType.ENABLE.name().equalsIgnoreCase(state) || StateType.DISABLE.name().equalsIgnoreCase(state) || StateType.DROP.name().equalsIgnoreCase(state)); } /** * Check if the tableType is one of {offline|realtime} * @param tableType: Table type string to be checked. * @return True if tableType is one of {offilne|realtime}, false otherwise. */ protected static boolean isValidTableType(String tableType) { return (TableType.OFFLINE.name().equalsIgnoreCase(tableType) || TableType.REALTIME.name().equalsIgnoreCase( tableType)); } public static void addExtraHeaders(Response response) { Series<Header> responseHeaders = (Series<Header>)response.getAttributes().get(HeaderConstants.ATTRIBUTE_HEADERS); if (responseHeaders == null) { responseHeaders = new Series(Header.class); response.getAttributes().put(HeaderConstants.ATTRIBUTE_HEADERS, responseHeaders); } responseHeaders.add(new Header(HDR_CONTROLLER_HOST, getHostName())); responseHeaders.add(new Header(HDR_CONTROLLER_VERSION, getControllerVersion())); } public static StringRepresentation exceptionToStringRepresentation(Exception e) { String errorMsg = e.getMessage() + "\n" + ExceptionUtils.getStackTrace(e); JSONObject errorMsgInJson = getErrorMsgInJson(errorMsg); return new StringRepresentation(errorMsgInJson.toJSONString(), MediaType.APPLICATION_JSON); } protected static JSONObject getErrorMsgInJson(String errorMsg) { JSONObject errorMsgJson = new JSONObject(); errorMsgJson.put("ERROR", errorMsg); return errorMsgJson; } protected StringRepresentation responseRepresentation(Status status, String jsonMsg) { setStatus(status); StringRepresentation repr = new StringRepresentation(jsonMsg); repr.setMediaType(MediaType.APPLICATION_JSON); return repr; } protected StringRepresentation errorResponseRepresentation(Status status, String msg) { return responseRepresentation(status, "{\"error\" : \"" + msg + "\"}"); } }