| package org.apache.helix.rest.server.resources; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import java.io.IOException; |
| import java.io.StringWriter; |
| import java.util.HashMap; |
| import java.util.Map; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.ws.rs.Consumes; |
| import javax.ws.rs.Produces; |
| import javax.ws.rs.core.Application; |
| import javax.ws.rs.core.Context; |
| import javax.ws.rs.core.MediaType; |
| import javax.ws.rs.core.Response; |
| |
| import com.fasterxml.jackson.databind.ObjectMapper; |
| import com.fasterxml.jackson.databind.ObjectReader; |
| import com.fasterxml.jackson.databind.SerializationFeature; |
| import org.apache.helix.HelixException; |
| import org.apache.helix.rest.common.ContextPropertyKeys; |
| import org.apache.helix.rest.common.HelixRestNamespace; |
| import org.apache.helix.rest.server.auditlog.AuditLog; |
| import org.apache.helix.zookeeper.datamodel.ZNRecord; |
| import org.apache.helix.zookeeper.introspect.CodehausJacksonIntrospector; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}) |
| @Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}) |
| public class AbstractResource { |
| private static Logger _logger = LoggerFactory.getLogger(AbstractResource.class.getName()); |
| |
| public enum Properties { |
| id, |
| disabled, |
| history, |
| count, |
| error |
| } |
| |
| public enum Command { |
| activate, |
| addInstanceTag, |
| expand, |
| enable, |
| disable, |
| enableMaintenanceMode, |
| disableMaintenanceMode, |
| enablePartitions, |
| disablePartitions, |
| update, |
| add, |
| delete, |
| stoppable, |
| rebalance, |
| reset, |
| resetPartitions, |
| removeInstanceTag, |
| addResource, |
| addWagedResource, |
| getResource, |
| validateWeight, |
| enableWagedRebalance, |
| enableWagedRebalanceForAllResources, |
| purgeOfflineParticipants, |
| getInstance, |
| getAllInstances |
| } |
| |
| @Context |
| protected Application _application; |
| |
| @Context |
| protected HttpServletRequest _servletRequest; |
| protected AuditLog.Builder _auditLogBuilder; |
| |
| protected void addExceptionToAuditLog(Exception ex) { |
| if (_auditLogBuilder == null) { |
| _auditLogBuilder = |
| (AuditLog.Builder) _servletRequest.getAttribute(AuditLog.ATTRIBUTE_NAME); |
| } |
| _auditLogBuilder.addException(ex); |
| } |
| |
| protected Response serverError() { |
| return Response.serverError().build(); |
| } |
| |
| protected Response serverError(String errorMsg) { |
| return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(errorMsgToJson(errorMsg)) |
| .build(); |
| } |
| |
| protected Response serverError(Exception ex) { |
| addExceptionToAuditLog(ex); |
| return Response.serverError().entity(errorMsgToJson(ex.getMessage())).build(); |
| } |
| |
| protected Response notFound() { |
| return Response.status(Response.Status.NOT_FOUND).build(); |
| } |
| |
| protected Response notFound(String errorMsg) { |
| return Response.status(Response.Status.NOT_FOUND).entity(errorMsgToJson(errorMsg)).build(); |
| } |
| |
| protected Response OK(Object entity) { |
| return Response.ok(entity, MediaType.APPLICATION_JSON_TYPE).build(); |
| } |
| |
| protected Response OK() { |
| return Response.ok().build(); |
| } |
| |
| protected Response OKWithHeader(Object entity, String headerName, Object headerValue) { |
| if (headerName == null || headerName.length() == 0) { |
| return OK(entity); |
| } else { |
| return Response.ok(entity).header(headerName, headerValue).build(); |
| } |
| } |
| |
| protected Response created() { |
| return Response.status(Response.Status.CREATED).build(); |
| } |
| |
| protected Response badRequest(String errorMsg) { |
| return Response.status(Response.Status.BAD_REQUEST).entity(errorMsgToJson(errorMsg)) |
| .type(MediaType.TEXT_PLAIN).build(); |
| } |
| |
| private String errorMsgToJson(String error) { |
| try { |
| Map<String, String> errorMap = new HashMap<>(); |
| errorMap.put(Properties.error.name(), error); |
| return toJson(errorMap); |
| } catch (IOException e) { |
| _logger.error("Failed to convert " + error + " to JSON string", e); |
| return error; |
| } |
| } |
| |
| protected Response JSONRepresentation(Object entity) { |
| return JSONRepresentation(entity, null, null); |
| } |
| |
| /** |
| * Any metadata about the response could be conveyed through the entity headers. |
| * More details can be found at 'REST-API-Design-Rulebook' -- Ch4 Metadata Design |
| */ |
| protected Response JSONRepresentation(Object entity, String headerName, Object headerValue) { |
| try { |
| String jsonStr = toJson(entity); |
| return OKWithHeader(jsonStr, headerName, headerValue); |
| } catch (IOException e) { |
| _logger.error("Failed to convert " + entity + " to JSON response", e); |
| return serverError(); |
| } |
| } |
| |
| protected static ObjectMapper OBJECT_MAPPER = new ObjectMapper(); |
| |
| // Needs a separate object reader for ZNRecord annotated with Jackson 1 |
| // TODO: remove AnnotationIntrospector config once ZNRecord upgrades Jackson |
| protected static ObjectReader ZNRECORD_READER = new ObjectMapper() |
| .setAnnotationIntrospector(new CodehausJacksonIntrospector()) |
| .readerFor(ZNRecord.class); |
| |
| protected static String toJson(Object object) |
| throws IOException { |
| OBJECT_MAPPER.enable(SerializationFeature.INDENT_OUTPUT); |
| |
| StringWriter sw = new StringWriter(); |
| OBJECT_MAPPER.writeValue(sw, object); |
| sw.append('\n'); |
| |
| return sw.toString(); |
| } |
| |
| protected Command getCommand(String commandStr) throws HelixException { |
| if (commandStr == null) { |
| throw new HelixException("Command string is null!"); |
| } |
| try { |
| return Command.valueOf(commandStr); |
| } catch (IllegalArgumentException ex) { |
| throw new HelixException("Unknown command: " + commandStr); |
| } |
| } |
| |
| protected String getNamespace() { |
| HelixRestNamespace namespace = |
| (HelixRestNamespace) _application.getProperties().get(ContextPropertyKeys.METADATA.name()); |
| return namespace.getName(); |
| } |
| } |