Java tutorial
/* * Copyright 2013 Rackspace * * Licensed 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. */ package com.rackspacecloud.blueflood.outputs.handlers; import com.codahale.metrics.Timer; import com.google.common.annotations.VisibleForTesting; import com.google.gson.*; import com.rackspacecloud.blueflood.concurrent.ThreadPoolBuilder; import com.rackspacecloud.blueflood.exceptions.InvalidRequestException; import com.rackspacecloud.blueflood.exceptions.SerializationException; import com.rackspacecloud.blueflood.http.DefaultHandler; import com.rackspacecloud.blueflood.http.HttpRequestHandler; import com.rackspacecloud.blueflood.http.HttpRequestWithDecodedQueryParams; import com.rackspacecloud.blueflood.http.HttpResponder; import com.rackspacecloud.blueflood.io.Constants; import com.rackspacecloud.blueflood.outputs.formats.MetricData; import com.rackspacecloud.blueflood.outputs.serializers.BatchedMetricsJSONOutputSerializer; import com.rackspacecloud.blueflood.outputs.serializers.BatchedMetricsOutputSerializer; import com.rackspacecloud.blueflood.outputs.utils.PlotRequestParser; import com.rackspacecloud.blueflood.outputs.utils.RollupsQueryParams; import com.rackspacecloud.blueflood.service.Configuration; import com.rackspacecloud.blueflood.service.HttpConfig; import com.rackspacecloud.blueflood.tracker.Tracker; import com.rackspacecloud.blueflood.types.Locator; import com.rackspacecloud.blueflood.utils.Metrics; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.*; import org.json.simple.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ThreadPoolExecutor; public class HttpMultiRollupsQueryHandler extends RollupHandler implements HttpRequestHandler { private static final Logger log = LoggerFactory.getLogger(HttpMultiRollupsQueryHandler.class); private final BatchedMetricsOutputSerializer<JSONObject> serializer; private final Gson gson; // thread-safe private final JsonParser parser; // thread-safe private final Timer httpBatchMetricsFetchTimer = Metrics.timer(HttpMultiRollupsQueryHandler.class, "Handle HTTP batch request for metrics"); private final ThreadPoolExecutor executor; private final int maxMetricsPerRequest; HttpMultiRollupsQueryHandler() { this(new BatchedMetricsJSONOutputSerializer()); } @VisibleForTesting public HttpMultiRollupsQueryHandler(BatchedMetricsOutputSerializer<JSONObject> serializer) { Configuration config = Configuration.getInstance(); int maxThreadsToUse = config.getIntegerProperty(HttpConfig.MAX_READ_WORKER_THREADS); int maxQueueSize = config.getIntegerProperty(HttpConfig.MAX_BATCH_READ_REQUESTS_TO_QUEUE); this.maxMetricsPerRequest = config.getIntegerProperty(HttpConfig.MAX_METRICS_PER_BATCH_QUERY); this.serializer = serializer; this.gson = new GsonBuilder().setPrettyPrinting().serializeNulls().create(); this.parser = new JsonParser(); this.executor = new ThreadPoolBuilder().withCorePoolSize(maxThreadsToUse).withMaxPoolSize(maxThreadsToUse) .withName("HTTP-BatchMetricsFetch").withBoundedQueue(maxQueueSize).build(); } @Override public void handle(ChannelHandlerContext ctx, FullHttpRequest request) { Tracker.getInstance().track(request); final String tenantId = request.headers().get("tenantId"); if (!(request instanceof HttpRequestWithDecodedQueryParams)) { DefaultHandler.sendErrorResponse(ctx, request, "Missing query params: from, to, points", HttpResponseStatus.BAD_REQUEST); return; } final String body = request.content().toString(Constants.DEFAULT_CHARSET); if (body == null || body.isEmpty()) { DefaultHandler.sendErrorResponse(ctx, request, "Invalid body. Expected JSON array of metrics.", HttpResponseStatus.BAD_REQUEST); return; } List<String> locators = new ArrayList<String>(); try { locators.addAll(getLocatorsFromJSONBody(tenantId, body)); } catch (Exception ex) { log.debug(ex.getMessage(), ex); sendResponse(ctx, request, ex.getMessage(), HttpResponseStatus.BAD_REQUEST); return; } if (locators.size() > maxMetricsPerRequest) { DefaultHandler.sendErrorResponse(ctx, request, "Too many metrics fetch in a single call. Max limit is " + maxMetricsPerRequest + ".", HttpResponseStatus.BAD_REQUEST); return; } HttpRequestWithDecodedQueryParams requestWithParams = (HttpRequestWithDecodedQueryParams) request; final Timer.Context httpBatchMetricsFetchTimerContext = httpBatchMetricsFetchTimer.time(); try { RollupsQueryParams params = PlotRequestParser.parseParams(requestWithParams.getQueryParams()); Map<Locator, MetricData> results = getRollupByGranularity(tenantId, locators, params.getRange().getStart(), params.getRange().getStop(), params.getGranularity(tenantId)); JSONObject metrics = serializer.transformRollupData(results, params.getStats()); final JsonElement element = parser.parse(metrics.toString()); final String jsonStringRep = gson.toJson(element); sendResponse(ctx, request, jsonStringRep, HttpResponseStatus.OK); } catch (InvalidRequestException e) { log.debug(e.getMessage()); DefaultHandler.sendErrorResponse(ctx, request, e.getMessage(), HttpResponseStatus.BAD_REQUEST); } catch (SerializationException e) { log.debug(e.getMessage(), e); DefaultHandler.sendErrorResponse(ctx, request, e.getMessage(), HttpResponseStatus.INTERNAL_SERVER_ERROR); } catch (Exception e) { log.error(e.getMessage(), e); DefaultHandler.sendErrorResponse(ctx, request, e.getMessage(), HttpResponseStatus.INTERNAL_SERVER_ERROR); } finally { httpBatchMetricsFetchTimerContext.stop(); } } private List<String> getLocatorsFromJSONBody(String tenantId, String body) { JsonElement element = gson.fromJson(body, JsonElement.class); JsonArray metrics = element.getAsJsonArray(); final List<String> locators = new ArrayList<String>(); Iterator<JsonElement> it = metrics.iterator(); while (it.hasNext()) { JsonElement metricElement = it.next(); locators.add(metricElement.getAsString()); } return locators; } private void sendResponse(ChannelHandlerContext channel, FullHttpRequest request, String messageBody, HttpResponseStatus status) { FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status); if (messageBody != null && !messageBody.isEmpty()) { response.content().writeBytes(Unpooled.copiedBuffer(messageBody, Constants.DEFAULT_CHARSET)); } HttpResponder.respond(channel, request, response); Tracker.getInstance().trackResponse(request, response); } }