/**
* This file is part of Graylog.
*
* Graylog is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Graylog is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Graylog. If not, see <http://www.gnu.org/licenses/>.
*/
package org.graylog2.indexer.cluster.jest;
import com.google.gson.JsonObject;
import io.searchbox.action.Action;
import io.searchbox.client.JestClient;
import io.searchbox.client.JestResult;
import io.searchbox.client.http.JestHttpClient;
import org.apache.http.client.config.RequestConfig;
import org.graylog2.indexer.ElasticsearchException;
import org.graylog2.indexer.FieldTypeException;
import org.graylog2.indexer.IndexNotFoundException;
import org.graylog2.indexer.QueryParsingException;
import org.graylog2.indexer.gson.GsonUtils;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static org.graylog2.indexer.gson.GsonUtils.asInteger;
import static org.graylog2.indexer.gson.GsonUtils.asJsonArray;
import static org.graylog2.indexer.gson.GsonUtils.asJsonObject;
import static org.graylog2.indexer.gson.GsonUtils.asString;
public class JestUtils {
private JestUtils() {
}
public static <T extends JestResult> T execute(JestClient client, RequestConfig requestConfig,
Action<T> request, Supplier<String> errorMessage) {
final T result;
try {
if (client instanceof JestHttpClient) {
result = ((JestHttpClient) client).execute(request, requestConfig);
} else {
result = client.execute(request);
}
} catch (IOException e) {
throw new ElasticsearchException(errorMessage.get(), e);
}
if (result.isSucceeded()) {
return result;
} else {
throw specificException(errorMessage, result.getJsonObject());
}
}
public static <T extends JestResult> T execute(JestClient client, Action<T> request, Supplier<String> errorMessage) {
return execute(client, null, request, errorMessage);
}
private static ElasticsearchException specificException(Supplier<String> errorMessage, JsonObject jsonObject) {
final List<JsonObject> rootCauses = extractRootCauses(jsonObject);
final List<String> reasons = extractReasons(rootCauses);
for (JsonObject rootCause : rootCauses) {
final String type = asString(rootCause.get("type"));
if (type == null) {
continue;
}
switch(type) {
case "query_parsing_exception":
return buildQueryParsingException(errorMessage, rootCause, reasons);
case "index_not_found_exception":
final String indexName = asString(rootCause.get("resource.id"));
return buildIndexNotFoundException(errorMessage, indexName);
case "illegal_argument_exception":
final String reason = asString(rootCause.get("reason"));
if (reason != null && reason.startsWith("Expected numeric type on field")) {
return buildFieldTypeException(errorMessage, reason);
}
break;
}
}
if (reasons.isEmpty()) {
return new ElasticsearchException(errorMessage.get(), Collections.singletonList(jsonObject.toString()));
}
return new ElasticsearchException(errorMessage.get(), reasons);
}
private static FieldTypeException buildFieldTypeException(Supplier<String> errorMessage, String reason) {
return new FieldTypeException(errorMessage.get(), reason);
}
private static List<String> extractReasons(List<JsonObject> rootCauses) {
return rootCauses.stream()
.map(rootCause -> asString(rootCause.get("reason")))
.collect(Collectors.toList());
}
private static List<JsonObject> extractRootCauses(JsonObject jsonObject) {
return Optional.of(jsonObject)
.map(json -> asJsonObject(json.get("error")))
.map(error -> asJsonArray(error.get("root_cause")))
.map(Iterable::spliterator)
.map(x -> StreamSupport.stream(x, false))
.orElse(Stream.empty())
.map(GsonUtils::asJsonObject)
.collect(Collectors.toList());
}
private static QueryParsingException buildQueryParsingException(Supplier<String> errorMessage,
JsonObject rootCause,
List<String> reasons) {
final Integer line = asInteger(rootCause.get("line"));
final Integer column = asInteger(rootCause.get("col"));
final String index = asString(rootCause.get("index"));
return new QueryParsingException(errorMessage.get(), line, column, index, reasons);
}
private static IndexNotFoundException buildIndexNotFoundException(Supplier<String> errorMessage, String index) {
return new IndexNotFoundException(errorMessage.get(), Collections.singletonList("Index not found for query: " + index + ". Try recalculating your index ranges."));
}
}