/** * Copyright 2014 Yahoo! Inc. 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. See accompanying * LICENSE file. */ package com.yahoo.sql4d.sql4ddriver; import com.google.common.collect.Lists; import com.yahoo.sql4d.query.nodes.Interval; import java.io.IOException; import static java.lang.String.format; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import scala.util.Either; import scala.util.Left; import scala.util.Right; import scala.Tuple2; /** * Coordinator commands. * * @author srikalyan */ public class CoordinatorAccessor extends DruidNodeAccessor { private final String coordinatorUrl = "http://%s:%d/"; private final String coordinatorHost; private int coordinatorPort = 8082; public CoordinatorAccessor(String host, int port, int maxConns) { super(host, port, maxConns); this.coordinatorHost = host; this.coordinatorPort = port; } /** * All commands. If data is null then GET else POST. * * @return */ private Either<String, Either<JSONArray, JSONObject>> fireCommand(String endPoint, String optData, Map<String, String> reqHeaders) { CloseableHttpResponse resp = null; String respStr; String url = format(coordinatorUrl + endPoint, coordinatorHost, coordinatorPort); try { if (optData != null) {// POST resp = postJson(url, optData, reqHeaders); } else {// GET resp = get(url, reqHeaders); } respStr = IOUtils.toString(resp.getEntity().getContent()); } catch (IOException ex) { return new Left<>(format("Http %s, faced exception %s\n", resp, ex)); } finally { returnClient(resp); } try { return new Right<>(Util.asJsonType(respStr)); } catch (JSONException je) { return new Left<>(format("Recieved data %s not in json format, faced exception %s\n", respStr, je)); } } /** * Retrieve all the datasources provisioned in Druid. * @param reqHeaders * @return */ public Either<String, List<String>> dataSources(Map<String, String> reqHeaders) { Either<String, Either<JSONArray, JSONObject>> resp = fireCommand("druid/coordinator/v1/metadata/datasources", null, reqHeaders); if (resp.isLeft()) { return new Left<>(resp.left().get()); } Either<JSONArray, JSONObject> goodResp = resp.right().get(); if (goodResp.isLeft()) { JSONArray dataSources = goodResp.left().get(); List<String> dataSourceList = new ArrayList<>(); for (int i = 0; i < dataSources.length(); i++) { dataSourceList.add(dataSources.getString(i)); } return new Right<>(dataSourceList); } return new Left<>(resp.left().get()); } /** * Left is error Right is Tuple <dimensions, metrics> * * @param name * @param reqHeaders * @return */ public Either<String, Tuple2<List<String>, List<String>>> aboutDataSource(String name, Map<String, String> reqHeaders) { Either<String, Either<JSONArray, JSONObject>> resp = fireCommand("druid/coordinator/v1/metadata/datasources/" + name, null, reqHeaders); if (resp.isLeft()) { return new Left<>(resp.left().get()); } Either<JSONArray, JSONObject> goodResp = resp.right().get(); if (goodResp.isRight()) { JSONObject data = goodResp.right().get(); if (data.has("segments")) { JSONArray segmentsArray = data.getJSONArray("segments"); if (segmentsArray.length() == 0) { return new Left<>("No segments received.."); } JSONObject firstItem = segmentsArray.getJSONObject(0); String dims = firstItem.getString("dimensions"); String metrics = firstItem.getString("metrics"); return new Right<>(new Tuple2<>(Arrays.asList(dims.split(",")), Arrays.asList(metrics.split(",")))); } else { return new Left<>("No segments key in the response.."); } } return new Left<>("Unexpected response " + goodResp.left().get().toString()); } public List<String> getDimensions(String name, Map<String, String> reqHeaders) { Either<String,Tuple2<List<String>,List<String>>> aboutDataSource = aboutDataSource(name, reqHeaders); if (aboutDataSource.isLeft()) { return Lists.newArrayList(); } return aboutDataSource.right().get()._1(); } public List<String> getMetrics(String name, Map<String, String> reqHeaders) { Either<String,Tuple2<List<String>,List<String>>> aboutDataSource = aboutDataSource(name, reqHeaders); if (aboutDataSource.isLeft()) { return Lists.newArrayList(); } return aboutDataSource.right().get()._2(); } /** * Ex:response looks something like this. * [{"dataSource":"abf2","interval":"2014-10-31T00:00:00.000-07:00/2014-11-01T00:00:00.000-07:00","version":"2014-11-13T21:53:23.982-08:00","loadSpec":{..},..}, * {"dataSource":"abf2","interval":"2014-11-01T00:00:00.000-07:00/2014-11-02T00:00:00.000-07:00","version":"2014-11-13T22:01:23.554-08:00","loadSpec":{..},..} * .... ] * * @param dataSource * @param reqHeaders * @return List[intervals] Ex: * ["2014-10-31T00:00:00.000-07:00/2014-11-01T00:00:00.000-07:00", * "2014-11-01T00:00:00.000-07:00/2014-11-02T00:00:00.000-07:00" .. ] */ public Either<String, List<Interval>> segments(String dataSource, Map<String, String> reqHeaders) { Either<String, Either<JSONArray, JSONObject>> resp = fireCommand("druid/coordinator/v1/metadata/datasources/" + dataSource + "/segments?full", null, reqHeaders); if (resp.isLeft()) { return new Left<>(resp.left().get()); } Either<JSONArray, JSONObject> mayBgoodResp = resp.right().get(); if (mayBgoodResp.isLeft()) { JSONArray segments = mayBgoodResp.left().get(); List<Interval> segmentsList = new ArrayList<>(); for (int i = 0; i < segments.length(); i++) { JSONObject segment = segments.getJSONObject(i); segmentsList.add(new Interval(segment.getString("interval"))); } return new Right<>(segmentsList); } return new Left<>(mayBgoodResp.right().get().toString()); } }