com.github.ftrossbach.kiqr.client.service.rest.GenericBlockingRestKiqrClientImpl Maven / Gradle / Ivy
/**
* Copyright © 2017 Florian Troßbach ([email protected])
*
* 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.github.ftrossbach.kiqr.client.service.rest;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.ftrossbach.kiqr.client.service.GenericBlockingKiqrClient;
import com.github.ftrossbach.kiqr.client.service.ConnectionException;
import com.github.ftrossbach.kiqr.client.service.QueryExecutionException;
import com.github.ftrossbach.kiqr.commons.config.querymodel.requests.*;
import org.apache.http.HttpResponse;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.util.EntityUtils;
import org.apache.kafka.common.serialization.Serde;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* Created by ftr on 01/03/2017.
*/
public class GenericBlockingRestKiqrClientImpl implements GenericBlockingKiqrClient {
private final String host;
private final int port;
private final ObjectMapper mapper;
public GenericBlockingRestKiqrClientImpl(String host, int port) {
this.host = host;
this.port = port;
mapper = new ObjectMapper();
}
@Override
public Optional getScalarKeyValue(String store, Class keyClass, K key, Class valueClass, Serde keySerde, Serde valueSerde) {
return execute(() -> getUriBuilder()
.setPath(String.format("/api/v1/kv/%s/values/%s", store, Base64.getEncoder().encodeToString(keySerde.serializer().serialize("", key))))
.addParameter("keySerde", keySerde.getClass().getName())
.addParameter("valueSerde", valueSerde.getClass().getName())
.build(), bytes -> {
ScalarKeyValueQueryResponse resp = mapper.readValue(bytes, ScalarKeyValueQueryResponse.class);
return Optional.of(deserialize(valueClass, valueSerde, resp.getValue()));
}, () -> Optional.empty());
}
@Override
public Map getAllKeyValues(String store, Class keyClass, Class valueClass, Serde keySerde, Serde valueSerde) {
return execute(() -> getUriBuilder()
.setPath(String.format("/api/v1/kv/%s", store))
.addParameter("keySerde", keySerde.getClass().getName())
.addParameter("valueSerde", valueSerde.getClass().getName())
.build(), bytes -> {
MultiValuedKeyValueQueryResponse resp = mapper.readValue(bytes, MultiValuedKeyValueQueryResponse.class);
return resp.getResults().entrySet().stream()
.map(entry -> {
return new Pair(deserialize(keyClass, keySerde, entry.getKey()),
deserialize(valueClass, valueSerde, entry.getValue()));
}).collect(Collectors.toMap(Pair::getKey, pair -> pair.getValue()));
}, () -> Collections.emptyMap());
}
@Override
public Map getRangeKeyValues(String store, Class keyClass, Class valueClass, Serde keySerde, Serde valueSerde, K from, K to) {
return execute(() -> getUriBuilder()
.setPath(String.format("/api/v1/kv/%s", store))
.addParameter("keySerde", keySerde.getClass().getName())
.addParameter("valueSerde", valueSerde.getClass().getName())
.addParameter("from", Base64.getEncoder().encodeToString(keySerde.serializer().serialize("", from)))
.addParameter("to", Base64.getEncoder().encodeToString(keySerde.serializer().serialize("", to)))
.build(), bytes -> {
MultiValuedKeyValueQueryResponse resp = mapper.readValue(bytes, MultiValuedKeyValueQueryResponse.class);
return resp.getResults().entrySet().stream()
.map(entry -> {
return new Pair(deserialize(keyClass, keySerde, entry.getKey()),
deserialize(valueClass, valueSerde, entry.getValue()));
}).collect(Collectors.toMap(Pair::getKey, pair -> pair.getValue()));
}, () -> Collections.emptyMap());
}
@Override
public Map getWindow(String store, Class keyClass, K key, Class valueClass, Serde keySerde, Serde valueSerde, long from, long to) {
return execute(() -> getUriBuilder()
.setPath(String.format("/api/v1/window/%s/%s", store, Base64.getEncoder().encodeToString(keySerde.serializer().serialize("", key))))
.addParameter("keySerde", keySerde.getClass().getName())
.addParameter("valueSerde", valueSerde.getClass().getName())
.addParameter("from", String.valueOf(from))
.addParameter("to", String.valueOf(to))
.build(), bytes -> {
WindowedQueryResponse resp = mapper.readValue(bytes, WindowedQueryResponse.class);
return new TreeMap(resp.getValues().entrySet().stream()
.map(entry -> {
return new Pair(entry.getKey(),
deserialize(valueClass, valueSerde, entry.getValue()));
}).collect(Collectors.toMap(Pair::getKey, pair -> pair.getValue())));
}, () -> Collections.emptyMap());
}
@Override
public Optional count(String store) {
return execute(() -> getUriBuilder()
.setPath(String.format("/api/v1/kv/%s/count", store))
.build(), bytes -> {
Map stringObjectMap = mapper.readValue(bytes, Map.class);
Object count = stringObjectMap.get("count");
if(count instanceof Integer){
return Optional.of(((Integer) count).longValue());
} else {
return Optional.of((Long) count);
}
}, () -> Optional.empty());
}
@Override
public Map getSession(String store, Class keyClass, K key, Class valueClass, Serde keySerde, Serde valueSerde) {
return execute(() -> getUriBuilder()
.setPath(String.format("/api/v1/session/%s/%s", store, Base64.getEncoder().encodeToString(keySerde.serializer().serialize("", key))))
.addParameter("keySerde", keySerde.getClass().getName())
.addParameter("valueSerde", valueSerde.getClass().getName())
.build(), bytes -> {
SessionQueryResponse resp = mapper.readValue(bytes, SessionQueryResponse.class);
return new TreeMap(resp.getValues().stream()
.map(window -> {
return new Pair(window,
deserialize(valueClass, valueSerde,window.getValue()));
}).collect(Collectors.toMap(Pair::getKey, pair -> pair.getValue())));
}, () -> Collections.emptyMap());
}
private U execute(URISupplier uriSupplier, MappingFunction responseMapper, Supplier notFoundMapper) {
try {
URI uri = uriSupplier.get();
Request request = Request.Get(uri);
HttpResponse response = request.execute().returnResponse();
if (response.getStatusLine().getStatusCode() == 200) {
byte[] returnJson = EntityUtils.toByteArray(response.getEntity());
return responseMapper.apply(returnJson);
} else if (response.getStatusLine().getStatusCode() == 404) {
return notFoundMapper.get();
} else if (response.getStatusLine().getStatusCode() == 400) {
throw new IllegalArgumentException("Bad Request");
} else {
throw new QueryExecutionException("Something went wrong, status code: " + response.getStatusLine().getStatusCode());
}
} catch (URISyntaxException | IOException e) {
throw new ConnectionException("Error creating connection", e);
}
}
private URIBuilder getUriBuilder() {
return new URIBuilder().setScheme("http").setHost(host).setPort(port);
}
private T deserialize(Class clazz, Serde serde, String b64Representation) {
byte[] bytes = Base64.getDecoder().decode(b64Representation);
return serde.deserializer().deserialize("", bytes);
}
private static final class Pair {
private final K key;
private final V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
}
}