io.vertx.ext.consul.impl.ConsulClientImpl Maven / Gradle / Ivy
/*
* Copyright (c) 2016 The original author or authors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.vertx.ext.consul.impl;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.Future;
import io.vertx.core.MultiMap;
import io.vertx.core.Vertx;
import io.vertx.core.VertxException;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.consul.*;
import io.vertx.ext.consul.policy.AclPolicy;
import io.vertx.ext.consul.token.CloneAclTokenOptions;
import io.vertx.ext.web.client.HttpRequest;
import io.vertx.ext.web.client.HttpResponse;
import io.vertx.ext.web.client.WebClient;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import static io.vertx.ext.consul.impl.Utils.listOf;
import static io.vertx.ext.consul.impl.Utils.urlEncode;
/**
* @author Ruslan Sennov
*/
public class ConsulClientImpl implements ConsulClient {
private static final String TOKEN_HEADER = "X-Consul-Token";
private static final String INDEX_HEADER = "X-Consul-Index";
private static final List DEFAULT_VALID_CODES = Collections.singletonList(HttpResponseStatus.OK.code());
private static final List TXN_VALID_CODES = Arrays.asList(HttpResponseStatus.OK.code(), HttpResponseStatus.CONFLICT.code());
private static final List KV_VALID_CODES = Arrays.asList(HttpResponseStatus.OK.code(), HttpResponseStatus.NOT_FOUND.code());
private final WebClient webClient;
private final String aclToken;
private final String dc;
private final long timeoutMs;
public ConsulClientImpl(Vertx vertx, ConsulClientOptions options) {
Objects.requireNonNull(vertx);
Objects.requireNonNull(options);
webClient = WebClient.create(vertx, options);
aclToken = options.getAclToken();
dc = options.getDc();
timeoutMs = options.getTimeout();
}
@Override
public Future agentInfo() {
return requestObject(HttpMethod.GET, "/v1/agent/self", null, null, (obj, headers) -> obj);
}
@Override
public Future coordinateNodes() {
return coordinateNodesWithOptions(null);
}
@Override
public Future coordinateNodesWithOptions(BlockingQueryOptions options) {
return requestArray(HttpMethod.GET, "/v1/coordinate/nodes", new Query().put(options), null, (arr, headers) -> {
List list = arr.stream().map(obj -> CoordinateParser.parse((JsonObject) obj)).collect(Collectors.toList());
return new CoordinateList().setList(list).setIndex(Long.parseLong(headers.get(INDEX_HEADER)));
});
}
@Override
public Future> coordinateDatacenters() {
return requestArray(HttpMethod.GET, "/v1/coordinate/datacenters", null, null, (arr, headers) ->
arr.stream().map(obj -> CoordinateParser.parseDc((JsonObject) obj)).collect(Collectors.toList()));
}
@Override
public Future> getKeys(String keyPrefix) {
return getKeysWithOptions(keyPrefix, null);
}
@Override
public Future> getKeysWithOptions(String keyPrefix, BlockingQueryOptions options) {
Query query = Query.of("recurse", true).put("keys", true).put(options);
return request(KV_VALID_CODES, HttpMethod.GET, "/v1/kv/" + urlEncode(keyPrefix), query, null, resp -> {
if (resp.statusCode() == HttpResponseStatus.NOT_FOUND.code()) {
return new ArrayList<>();
} else {
return resp.bodyAsJsonArray().stream().map(Object::toString).collect(Collectors.toList());
}
});
}
@Override
public Future getValue(String key) {
return getValueWithOptions(key, null);
}
@Override
public Future getValueWithOptions(String key, BlockingQueryOptions options) {
return request(KV_VALID_CODES, HttpMethod.GET, "/v1/kv/" + urlEncode(key), new Query().put(options), null, resp -> {
if (resp.statusCode() == HttpResponseStatus.NOT_FOUND.code()) {
return new KeyValue();
} else {
return KVParser.parse(resp.bodyAsJsonArray().getJsonObject(0));
}
});
}
@Override
public Future deleteValue(String key) {
return requestVoid(HttpMethod.DELETE, "/v1/kv/" + urlEncode(key), null, null);
}
@Override
public Future getValues(String keyPrefix) {
return getValuesWithOptions(keyPrefix, null);
}
@Override
public Future getValuesWithOptions(String keyPrefix, BlockingQueryOptions options) {
Query query = Query.of("recurse", true).put(options);
return request(KV_VALID_CODES, HttpMethod.GET, "/v1/kv/" + urlEncode(keyPrefix), query, null, resp -> {
if (resp.statusCode() == HttpResponseStatus.NOT_FOUND.code()) {
return new KeyValueList();
} else {
List list = resp.bodyAsJsonArray().stream().map(obj -> KVParser.parse((JsonObject) obj)).collect(Collectors.toList());
return new KeyValueList().setList(list).setIndex(Long.parseLong(resp.headers().get(INDEX_HEADER)));
}
});
}
@Override
public Future deleteValues(String keyPrefix) {
return requestVoid(HttpMethod.DELETE, "/v1/kv/" + urlEncode(keyPrefix), Query.of("recurse", true), null);
}
@Override
public Future putValue(String key, String value) {
return putValueWithOptions(key, value, null);
}
@Override
public Future putValueWithOptions(String key, String value, KeyValueOptions options) {
Query query = new Query();
if (options != null) {
query.put("flags", Long.toUnsignedString(options.getFlags()))
.put("acquire", options.getAcquireSession())
.put("release", options.getReleaseSession());
long cas = options.getCasIndex();
if (cas >= 0) {
query.put("cas", cas);
}
}
return requestString(HttpMethod.PUT, "/v1/kv/" + urlEncode(key), query, value, (bool, headers) -> Boolean.valueOf(bool));
}
@Override
public Future transaction(TxnRequest request) {
String boby = request.toJson().getJsonArray("operations").encode();
return request(TXN_VALID_CODES, HttpMethod.PUT, "/v1/txn", null, boby, resp -> TxnResponseParser.parse(resp.bodyAsJsonObject()));
}
@Override
public Future createAclPolicy(AclPolicy policy) {
if (policy.getRules() == null) {
return Future.failedFuture(new RuntimeException("Missing required request parameter: 'rules'"));
}
if (policy.getName() == null) {
return Future.failedFuture(new RuntimeException("Missing required request parameter: 'name'"));
}
return requestObject(HttpMethod.PUT, "/v1/acl/policy", null, policy.toJson().encode(), (obj, headers) ->
obj.getString("ID")
);
}
@Override
public Future readPolicy(String id) {
return requestObject(HttpMethod.GET, "/v1/acl/policy/" + urlEncode(id), null, null, (obj, headers) ->
new AclPolicy(obj)
);
}
@Override
public Future readPolicyByName(String name) {
return requestObject(HttpMethod.GET, "/v1/acl/policy/name/" + urlEncode(name), null, null, (obj, headers) ->
new AclPolicy(obj)
);
}
@Override
public Future updatePolicy(String id, AclPolicy policy) {
return requestObject(HttpMethod.PUT, "/v1/acl/policy/" + urlEncode(id), null, policy.toJson().encode(), (obj, headers) ->
new AclPolicy(obj)
);
}
@Override
public Future deletePolicy(String id) {
return requestString(HttpMethod.DELETE, "/v1/acl/policy/" + urlEncode(id), null, null, (str, headers) ->
Boolean.parseBoolean(str)
);
}
@Override
public Future> getAclPolicies() {
return requestArray(HttpMethod.GET, "/v1/acl/policies", null, null, (array, header) ->
array.stream()
.map(obj -> new AclPolicy((JsonObject) obj))
.collect(Collectors.toList())
);
}
public Future createAclToken(io.vertx.ext.consul.token.AclToken token) {
return requestObject(HttpMethod.PUT, "/v1/acl/token", null, token.toJson().encode(), (obj, headers) ->
new io.vertx.ext.consul.token.AclToken(obj)
);
}
@Override
public Future updateAclToken(String accessorId, io.vertx.ext.consul.token.AclToken token) {
return requestObject(HttpMethod.PUT, "/v1/acl/token/" + urlEncode(accessorId), null, token.toJson().encode(),
(obj, headers) -> new io.vertx.ext.consul.token.AclToken(obj)
);
}
@Override
public Future cloneAclToken(String accessorId, CloneAclTokenOptions cloneAclTokenOptions) {
return requestObject(HttpMethod.PUT, "/v1/acl/token/" + urlEncode(accessorId) + "/clone", null,
cloneAclTokenOptions.toJson().encode(),
(obj, headers) -> new io.vertx.ext.consul.token.AclToken(obj)
);
}
@Override
public Future> getAclTokens() {
return requestArray(HttpMethod.GET, "/v1/acl/tokens", null, null, (arr, headers) ->
arr.stream()
.map(obj -> new io.vertx.ext.consul.token.AclToken((JsonObject) obj))
.collect(Collectors.toList()));
}
@Override
public Future readAclToken(String accessorId) {
return requestObject(HttpMethod.GET, "/v1/acl/token/" + urlEncode(accessorId), null, null,
(obj, headers) -> new io.vertx.ext.consul.token.AclToken(obj)
);
}
@Override
public Future deleteAclToken(String accessorId) {
return requestString(HttpMethod.DELETE, "/v1/acl/token/" + urlEncode(accessorId), null, null,
(str, headers) -> Boolean.parseBoolean(str)
);
}
@Override
public Future createAclToken(AclToken token) {
return requestObject(HttpMethod.PUT, "/v1/acl/create", null, token.toJson().encode(), (obj, headers) ->
obj.getString("ID"));
}
@Override
public Future updateAclToken(AclToken token) {
return requestObject(HttpMethod.PUT, "/v1/acl/update", null, token.toJson().encode(), (obj, headers) ->
obj.getString("ID"));
}
@Override
public Future cloneAclToken(String id) {
return requestObject(HttpMethod.PUT, "/v1/acl/clone/" + urlEncode(id), null, null, (obj, headers) ->
obj.getString("ID"));
}
@Override
public Future> listAclTokens() {
return requestArray(HttpMethod.GET, "/v1/acl/list", null, null, (arr, headers) ->
arr.stream()
.map(obj -> new AclToken((JsonObject) obj))
.collect(Collectors.toList()));
}
@Override
public Future infoAclToken(String id) {
return requestArray(HttpMethod.GET, "/v1/acl/info/" + urlEncode(id), null, null, (arr, headers) -> {
JsonObject jsonObject = arr.getJsonObject(0);
return new AclToken(jsonObject);
});
}
@Override
public Future destroyAclToken(String id) {
return requestVoid(HttpMethod.PUT, "/v1/acl/destroy/" + urlEncode(id), null, null);
}
@Override
public Future fireEvent(String name) {
return fireEventWithOptions(name, null);
}
@Override
public Future fireEventWithOptions(String name, EventOptions options) {
Query query = new Query();
if (options != null) {
query.put("node", options.getNode()).put("service", options.getService()).put("tag", options.getTag());
}
String body = options == null || options.getPayload() == null ? "" : options.getPayload();
return requestObject(
HttpMethod.PUT,
"/v1/event/fire/" + urlEncode(name),
query,
body,
(jsonObject, headers) -> EventParser.parse(jsonObject)
);
}
@Override
public Future listEvents() {
return listEventsWithOptions(null);
}
@Override
public Future listEventsWithOptions(EventListOptions options) {
Query query = options == null ? null : Query.of(options.getBlockingOptions()).put("name", options.getName());
return requestArray(HttpMethod.GET, "/v1/event/list", query, null, (jsonArray, headers) -> {
List list = jsonArray
.stream()
.map(obj -> EventParser.parse(((JsonObject) obj)))
.collect(Collectors.toList());
return new EventList().setList(list).setIndex(Long.parseUnsignedLong(headers.get(INDEX_HEADER)));
});
}
@Override
public Future registerService(ServiceOptions serviceOptions) {
JsonObject jsonOpts = new JsonObject()
.put("ID", serviceOptions.getId())
.put("Name", serviceOptions.getName())
.put("Tags", serviceOptions.getTags())
.put("Address", serviceOptions.getAddress())
.put("Port", serviceOptions.getPort());
if (serviceOptions.getCheckOptions() != null) {
jsonOpts.put("Check", checkOpts(serviceOptions.getCheckOptions(), "CheckID", false));
}
if (serviceOptions.getCheckListOptions() != null) {
jsonOpts.put("Checks", checkListOpts(serviceOptions.getCheckListOptions(), "CheckID", false));
}
if (serviceOptions.getMeta() != null && !serviceOptions.getMeta().isEmpty()) {
jsonOpts.put("Meta", serviceOptions.getMeta());
}
if (serviceOptions.getConnectOptions() != null) {
jsonOpts.put("Connect", serviceOptions.getConnectOptions().toJson());
}
return requestVoid(HttpMethod.PUT, "/v1/agent/service/register", null, jsonOpts.encode());
}
@Override
public Future maintenanceService(MaintenanceOptions opts) {
Query query = Query.of("enable", opts.isEnable()).put("reason", opts.getReason());
return requestVoid(HttpMethod.PUT, "/v1/agent/service/maintenance/" + urlEncode(opts.getId()), query, null);
}
@Override
public Future deregisterService(String id) {
return requestVoid(HttpMethod.PUT, "/v1/agent/service/deregister/" + urlEncode(id), null, null);
}
@Override
public Future catalogServiceNodes(String service) {
return catalogServiceNodesWithOptions(service, null);
}
@Override
public Future catalogServiceNodesWithOptions(String service, ServiceQueryOptions options) {
Query query = options == null ? null : Query
.of("tag", options.getTag())
.put("near", options.getNear())
.put(options.getBlockingOptions());
return requestArray(HttpMethod.GET, "/v1/catalog/service/" + urlEncode(service), query, null, (arr, headers) -> {
List list = arr.stream().map(obj -> new Service((JsonObject) obj)).collect(Collectors.toList());
return new ServiceList().setList(list).setIndex(Long.parseLong(headers.get(INDEX_HEADER)));
});
}
@Override
public Future> catalogDatacenters() {
return requestArray(HttpMethod.GET, "/v1/catalog/datacenters", null, null, (arr, headers) -> listOf(arr));
}
@Override
public Future catalogNodes() {
return catalogNodesWithOptions(null);
}
@Override
public Future catalogNodesWithOptions(NodeQueryOptions options) {
Query query = options == null ? null : Query.of("near", options.getNear()).put(options.getBlockingOptions());
return requestArray(HttpMethod.GET, "/v1/catalog/nodes", query, null, (arr, headers) -> {
List list = arr.stream().map(obj -> NodeParser.parse((JsonObject) obj)).collect(Collectors.toList());
return new NodeList().setList(list).setIndex(Long.parseLong(headers.get(INDEX_HEADER)));
});
}
@Override
public Future healthChecks(String service) {
return healthChecksWithOptions(service, null);
}
private Query fromCheckQueryOptions(CheckQueryOptions options) {
Query query = new Query();
if (options != null) {
if (options.getBlockingOptions() != null) {
query.put(options.getBlockingOptions());
}
if (options.getNear() != null) {
query.put("near", options.getNear());
}
if (options.getDc() != null && !options.getDc().isEmpty()) {
query.put("dc", options.getDc());
}
if (options.getFilter() != null && !options.getFilter().isEmpty()) {
query.put("filter", options.getFilter());
}
}
return query;
}
@Override
public Future healthChecksWithOptions(String service, CheckQueryOptions options) {
return requestArray(HttpMethod.GET, "/v1/health/checks/" + urlEncode(service), fromCheckQueryOptions(options), null,
(arr, headers) -> {
List list = arr.stream().map(obj -> CheckParser.parse((JsonObject) obj)).collect(Collectors.toList());
return new CheckList().setList(list).setIndex(Long.parseLong(headers.get(INDEX_HEADER)));
});
}
@Override
public Future healthState(HealthState healthState) {
return healthStateWithOptions(healthState, null);
}
@Override
public Future healthStateWithOptions(HealthState healthState, CheckQueryOptions options) {
return requestArray(HttpMethod.GET, "/v1/health/state/" + healthState.key, fromCheckQueryOptions(options), null,
(arr, headers) -> {
List list = arr.stream().map(obj -> CheckParser.parse((JsonObject) obj)).collect(Collectors.toList());
return new CheckList().setList(list).setIndex(Long.parseLong(headers.get(INDEX_HEADER)));
});
}
@Override
public Future healthServiceNodes(String service, boolean passing) {
return healthServiceNodesWithOptions(service, passing, null);
}
@Override
public Future healthServiceNodesWithOptions(
String service,
boolean passing,
ServiceQueryOptions options
) {
Query query = new Query().put("passing", passing ? 1 : null);
if (options != null) {
query.put(options.getBlockingOptions()).put("near", options.getNear()).put("tag", options.getTag());
}
return requestArray(HttpMethod.GET, "/v1/health/service/" + urlEncode(service), query, null, (arr, headers) -> {
List list = arr
.stream()
.map(obj -> ServiceEntryParser.parse((JsonObject) obj))
.collect(Collectors.toList());
return new ServiceEntryList().setList(list).setIndex(Long.parseLong(headers.get(INDEX_HEADER)));
});
}
@Override
public Future healthNodesWithOptions(String node, CheckQueryOptions options) {
return requestArray(HttpMethod.GET, "/v1/health/node/" + urlEncode(node), fromCheckQueryOptions(options),
options.toJson().encode(),
(arr, headers) -> {
List list = arr.stream().map(obj -> CheckParser.parse((JsonObject) obj)).collect(Collectors.toList());
return new CheckList().setList(list).setIndex(Long.parseLong(headers.get(INDEX_HEADER)));
}
);
}
@Override
public Future catalogServices() {
return catalogServicesWithOptions(null);
}
@Override
public Future catalogServicesWithOptions(BlockingQueryOptions options) {
return requestObject(HttpMethod.GET, "/v1/catalog/services", Query.of(options), null, (json, headers) -> {
List list = json.stream().map(ServiceParser::parseCatalogInfo).collect(Collectors.toList());
return new ServiceList().setList(list).setIndex(Long.parseLong(headers.get(INDEX_HEADER)));
});
}
@Override
public Future> localChecks() {
return requestObject(HttpMethod.GET, "/v1/agent/checks", null, null, (json, headers) -> json.stream()
.map(obj -> CheckParser.parse((JsonObject) obj.getValue()))
.collect(Collectors.toList()));
}
@Override
public Future> localServices() {
return requestObject(HttpMethod.GET, "/v1/agent/services", null, null, (json, headers) -> json.stream()
.map(obj -> ServiceParser.parseAgentInfo((JsonObject) obj.getValue()))
.collect(Collectors.toList()));
}
@Override
public Future catalogNodeServices(String node) {
return catalogNodeServicesWithOptions(node, null);
}
@Override
public Future catalogNodeServicesWithOptions(String node, BlockingQueryOptions options) {
return requestObject(
HttpMethod.GET,
"/v1/catalog/node/" + urlEncode(node),
Query.of(options),
null,
(json, headers) -> {
JsonObject nodeInfo = json.getJsonObject("Node");
String nodeName = nodeInfo.getString("Node");
String nodeAddress = nodeInfo.getString("Address");
List list = json.getJsonObject("Services").stream()
.map(obj -> ServiceParser.parseNodeInfo(nodeName, nodeAddress, (JsonObject) obj.getValue()))
.collect(Collectors.toList());
return new ServiceList().setList(list).setIndex(Long.parseLong(headers.get(INDEX_HEADER)));
}
);
}
@Override
public Future registerCheck(CheckOptions checkOptions) {
return requestVoid(HttpMethod.PUT, "/v1/agent/check/register", null, checkOpts(checkOptions, "ID", true).encode());
}
private static JsonObject checkOpts(CheckOptions checkOptions, String checkIdKey, boolean extended) {
JsonObject json = new JsonObject()
.put(checkIdKey, checkOptions.getId())
.put("Name", checkOptions.getName())
.put("Notes", checkOptions.getNotes())
.put("ScriptArgs", checkOptions.getScriptArgs())
.put("HTTP", checkOptions.getHttp())
.put("Header", checkOptions.getHeaders())
.put("TLSSkipVerify", checkOptions.isTlsSkipVerify())
.put("GRPC", checkOptions.getGrpc())
.put("Interval", checkOptions.getInterval())
.put("TTL", checkOptions.getTtl())
.put("TCP", checkOptions.getTcp());
if (checkOptions.getGrpc() != null) {
json.put("GRPCUseTLS", checkOptions.isGrpcTls());
}
if (checkOptions.getDeregisterAfter() != null) {
json.put("DeregisterCriticalServiceAfter", checkOptions.getDeregisterAfter());
}
if (checkOptions.getStatus() != null) {
json.put("Status", checkOptions.getStatus().key);
}
if (extended && checkOptions.getServiceId() != null) {
json.put("ServiceID", checkOptions.getServiceId());
}
return json;
}
private static JsonArray checkListOpts(List listChecks, String checkIdKey, boolean extended) {
JsonArray jsonArray = new JsonArray();
listChecks.stream().map(c -> checkOpts(c, checkIdKey, extended)).forEach(jsonArray::add);
return jsonArray;
}
@Override
public Future deregisterCheck(String checkId) {
return requestVoid(HttpMethod.PUT, "/v1/agent/check/deregister/" + urlEncode(checkId), null, null);
}
@Override
public Future passCheck(String checkId) {
return passCheckWithNote(checkId, null);
}
@Override
public Future passCheckWithNote(String checkId, String note) {
return requestVoid(HttpMethod.PUT, "/v1/agent/check/pass/" + urlEncode(checkId), Query.of("note", note), null);
}
@Override
public Future warnCheck(String checkId) {
return warnCheckWithNote(checkId, null);
}
@Override
public Future warnCheckWithNote(String checkId, String note) {
return requestVoid(HttpMethod.PUT, "/v1/agent/check/warn/" + urlEncode(checkId), Query.of("note", note), null);
}
@Override
public Future failCheck(String checkId) {
return failCheckWithNote(checkId, null);
}
@Override
public Future failCheckWithNote(String checkId, String note) {
return requestVoid(HttpMethod.PUT, "/v1/agent/check/fail/" + urlEncode(checkId), Query.of("note", note), null);
}
@Override
public Future updateCheck(String checkId, CheckStatus status) {
return updateCheckWithNote(checkId, status, null);
}
@Override
public Future updateCheckWithNote(String checkId, CheckStatus status, String note) {
JsonObject put = new JsonObject().put("Status", status.key);
if (note != null) {
put.put("Output", note);
}
return requestVoid(HttpMethod.PUT, "/v1/agent/check/update/" + urlEncode(checkId), null, put.encode());
}
@Override
public Future leaderStatus() {
return requestString(HttpMethod.GET, "/v1/status/leader", null, null, (leader, headers) ->
leader.substring(1, leader.length() - 2));
}
@Override
public Future> peersStatus() {
return requestArray(HttpMethod.GET, "/v1/status/peers", null, null, (arr, headers) -> arr.stream()
.map(obj -> (String) obj)
.collect(Collectors.toList()));
}
@Override
public Future createSession() {
return createSessionWithOptions(null);
}
@Override
public Future createSessionWithOptions(SessionOptions options) {
String body = options == null ? null : options.toJson().encode();
return requestObject(HttpMethod.PUT, "/v1/session/create", null, body, (obj, headers) -> obj.getString("ID"));
}
@Override
public Future infoSession(String id) {
return infoSessionWithOptions(id, null);
}
@Override
public Future infoSessionWithOptions(String id, BlockingQueryOptions options) {
return requestArray(
HttpMethod.GET,
"/v1/session/info/" + urlEncode(id),
Query.of(options),
null,
(sessions, headers) -> {
if (sessions.size() == 0) {
throw new RuntimeException("Unknown session ID: " + id);
} else {
return SessionParser.parse(sessions.getJsonObject(0), Long.parseLong(headers.get(INDEX_HEADER)));
}
}
);
}
@Override
public Future renewSession(String id) {
return requestArray(HttpMethod.PUT, "/v1/session/renew/" + urlEncode(id), null, null, (arr, headers) ->
SessionParser.parse(arr.getJsonObject(0)));
}
@Override
public Future listSessions() {
return listSessionsWithOptions(null);
}
@Override
public Future listSessionsWithOptions(BlockingQueryOptions options) {
return requestArray(HttpMethod.GET, "/v1/session/list", Query.of(options), null, (arr, headers) -> {
List list = arr.stream().map(obj -> SessionParser.parse((JsonObject) obj)).collect(Collectors.toList());
return new SessionList().setList(list).setIndex(Long.parseLong(headers.get(INDEX_HEADER)));
});
}
@Override
public Future listNodeSessions(String nodeId) {
return listNodeSessionsWithOptions(nodeId, null);
}
@Override
public Future listNodeSessionsWithOptions(String nodeId, BlockingQueryOptions options) {
return requestArray(
HttpMethod.GET,
"/v1/session/node/" + urlEncode(nodeId),
Query.of(options),
null,
(arr, headers) -> {
List list = arr
.stream()
.map(obj -> SessionParser.parse((JsonObject) obj))
.collect(Collectors.toList());
return new SessionList().setList(list).setIndex(Long.parseLong(headers.get(INDEX_HEADER)));
}
);
}
@Override
public Future destroySession(String id) {
return requestVoid(HttpMethod.PUT, "/v1/session/destroy/" + urlEncode(id), null, null);
}
@Override
public Future createPreparedQuery(PreparedQueryDefinition definition) {
return requestObject(
HttpMethod.POST,
"/v1/query",
null,
definition.toJson().encode(),
(obj, headers) -> obj.getString("ID")
);
}
@Override
public Future getPreparedQuery(String id) {
return getPreparedQueryList(id).map(list -> list.get(0));
}
@Override
public Future> getAllPreparedQueries() {
return getPreparedQueryList(null);
}
@Override
public Future updatePreparedQuery(PreparedQueryDefinition definition) {
String path = "/v1/query/" + urlEncode(definition.getId());
return requestVoid(HttpMethod.PUT, path, null, definition.toJson().encode());
}
private Future> getPreparedQueryList(String id) {
String path = "/v1/query" + (id == null ? "" : "/" + urlEncode(id));
return requestArray(HttpMethod.GET, path, null, null, (arr, headers) -> arr.stream()
.map(obj -> new PreparedQueryDefinition((JsonObject) obj)).collect(Collectors.toList()));
}
@Override
public Future deletePreparedQuery(String id) {
return requestVoid(HttpMethod.DELETE, "/v1/query/" + urlEncode(id), null, null);
}
@Override
public Future executePreparedQuery(String query) {
return executePreparedQueryWithOptions(query, null);
}
@Override
public Future executePreparedQueryWithOptions(
String query,
PreparedQueryExecuteOptions options
) {
String path = "/v1/query/" + urlEncode(query) + "/execute";
Query q = new Query();
if (options != null) {
q.put("near", options.getNear()).put("limit", options.getLimit());
}
return requestObject(HttpMethod.GET, path, q, null, (obj, headers) -> {
return new PreparedQueryExecuteResponse()
.setService(obj.getString("Service"))
.setDc(obj.getString("Datacenter"))
.setFailovers(obj.getInteger("Failovers"))
.setDnsTtl(obj.getJsonObject("DNS").getString("TTL"))
.setNodes(obj.getJsonArray("Nodes").stream()
.map(o -> ServiceEntryParser.parse((JsonObject) o))
.collect(Collectors.toList()));
});
}
@Override
public Future registerCatalogService(Node nodeOptions, ServiceOptions serviceOptions) {
JsonObject nodeJsonOpts = new JsonObject()
.put("Node", nodeOptions.getName())
.put("Address", nodeOptions.getAddress());
if (notEmptyString(nodeOptions.getId())) {
nodeJsonOpts.put("ID", nodeOptions.getId());
}
Map taggedAddresses = new HashMap<>();
String lanAddress = nodeOptions.getLanAddress();
if (lanAddress != null && !lanAddress.isEmpty()) {
taggedAddresses.put("lan", lanAddress);
}
String wanAddress = nodeOptions.getWanAddress();
if (wanAddress != null && !wanAddress.isEmpty()) {
taggedAddresses.put("wan", wanAddress);
}
if (!taggedAddresses.isEmpty()) {
nodeJsonOpts.put("TaggedAddresses", taggedAddresses);
}
if (notEmptyString(nodeOptions.getDatacenter())) {
nodeJsonOpts.put("Datacenter", nodeOptions.getDatacenter());
}
if (nodeOptions.getNodeMeta() != null && !nodeOptions.getNodeMeta().isEmpty())
nodeJsonOpts.put("NodeMeta", nodeOptions.getNodeMeta());
if (serviceOptions != null) {
JsonObject serviceJsonOpts = new JsonObject()
.put("ID", serviceOptions.getId())
.put("Service", serviceOptions.getName())
.put("Tags", serviceOptions.getTags())
.put("Address", serviceOptions.getAddress())
.put("Port", serviceOptions.getPort())
.put("Meta", serviceOptions.getMeta());
nodeJsonOpts.put("Service", serviceJsonOpts);
}
Map map = nodeJsonOpts.getMap();
for (Map.Entry entry : map.entrySet()) {
if (entry.getValue() == null) {
nodeJsonOpts.remove(entry.getKey());
}
}
return requestVoid(HttpMethod.PUT, "/v1/catalog/register", null, nodeJsonOpts.encode());
}
private boolean notEmptyString(String str) {
return str != null && !str.isEmpty();
}
@Override
public Future deregisterCatalogService(String nodeId, String serviceId) {
JsonObject jsonOpts = new JsonObject()
.put("Node", nodeId)
.put("ServiceID", serviceId);
return requestVoid(HttpMethod.PUT, "/v1/catalog/deregister", null, jsonOpts.encode());
}
@Override
public void close() {
webClient.close();
}
private Future requestArray(
HttpMethod method, String path, Query query, String body,
BiFunction mapper
) {
return request(
DEFAULT_VALID_CODES,
method,
path,
query,
body,
resp -> mapper.apply(resp.bodyAsJsonArray(), resp.headers())
);
}
private Future requestObject(
HttpMethod method, String path, Query query, String body,
BiFunction mapper
) {
return request(
DEFAULT_VALID_CODES,
method,
path,
query,
body,
resp -> mapper.apply(resp.bodyAsJsonObject(), resp.headers())
);
}
private Future requestString(
HttpMethod method, String path, Query query, String body,
BiFunction mapper
) {
return request(
DEFAULT_VALID_CODES,
method,
path,
query,
body,
resp -> mapper.apply(resp.bodyAsString().trim(), resp.headers())
);
}
private Future requestVoid(HttpMethod method, String path, Query query, String body) {
return request(DEFAULT_VALID_CODES, method, path, query, body, resp -> null);
}
private Future request(
List validCodes, HttpMethod method, String path, Query query, String body,
Function, T> mapper
) {
if (query == null) {
query = new Query();
}
if (dc != null) {
query.put("dc", dc);
}
HttpRequest rq = webClient.request(method, path);
query.entrySet().forEach(e -> rq.addQueryParam(e.getKey(), e.getValue()));
if (aclToken != null) {
rq.putHeader(TOKEN_HEADER, aclToken);
}
if (timeoutMs > 0) {
rq.timeout(timeoutMs);
}
return rq.sendBuffer(body == null ? Buffer.buffer() : Buffer.buffer(body))
.map(resp -> {
if (validCodes.contains(resp.statusCode())) {
return mapper.apply(resp);
} else {
throw new VertxException(String.format(
"Status message: '%s'. Body: '%s' ",
resp.statusMessage(),
resp.bodyAsString()
), true);
}
});
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy