Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
estonlabs.cxtl.common.http.JsonRestClient Maven / Gradle / Ivy
package estonlabs.cxtl.common.http;
import estonlabs.cxtl.common.codec.Codec;
import estonlabs.cxtl.common.codec.JacksonCodec;
import estonlabs.cxtl.common.exception.CxtlEventException;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import okhttp3.*;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;
import reactor.core.publisher.MonoSink;
import java.io.IOException;
import java.net.Proxy;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
/**
* okhttp3 backed Rest Client for sending and receiving JSON
*/
public class JsonRestClient implements RestClient{
private static final Logger LOGGER = LoggerFactory.getLogger(JsonRestClient.class);
private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
private final String urlPrefix;
private final OkHttpClient client;
private final Codec codec;
public JsonRestClient(URI uri, Codec codec, Proxy proxy) {
this.urlPrefix = uri.toString();
this.codec = codec;
OkHttpClient.Builder httpClientBuilder = new OkHttpClient().newBuilder();
this.client = (proxy == null ? httpClientBuilder: httpClientBuilder.proxy(proxy)).build();
}
@Override
public Mono> deleteAsForm(HeaderBuilder header, String path, IN message, Class responseType) {
final Map map = codec.toMap(message);
final String strMessage = JacksonCodec.mapToStringBuilder(map,new StringBuilder()).toString();
RequestBody body = formBody(map);
LOGGER.info("DELETE: {}{} [{}]",urlPrefix,path, strMessage);
return delete(header, path, responseType, strMessage, body);
}
@NonNull
private Mono> delete(HeaderBuilder header, String path, Class responseType, String strMessage, RequestBody body) {
Request.Builder requestBuilder = buildRequest(header, path, strMessage);
return Mono.fromSupplier(() ->
requestBuilder
.delete(body)
.build()).flatMap(r ->
Mono.create(sink ->
client.newCall(r).enqueue(
new FnCallback<>(sink, new Event<>(path + "(" + strMessage + ")" + headers(r)), res -> codec.fromJson(res, responseType)))));
}
@Override
public Mono> deleteAsParams(HeaderBuilder header, String path, IN message, Class responseType) {
final Map map = codec.toMap(message);
final StringBuilder strMessage = JacksonCodec.mapToStringBuilder(map,new StringBuilder());
return parameterisedRequest(Method.DELETE,header, path, strMessage, json -> codec.fromJson(json, responseType));
}
@Override
public Mono> putEmpty(HeaderBuilder header, String path, Class responseType) {
return parameterisedRequest(Method.PUT,header, path, null, json -> codec.fromJson(json, responseType));
}
@Override
public Mono> postEmpty(HeaderBuilder header, String path, Class responseType) {
return parameterisedRequest(Method.POST,header, path, null, json -> codec.fromJson(json, responseType));
}
@Override
public Mono> postAsJson(HeaderBuilder header, String path, IN message, Class responseType) {
final String json = codec.toJson(message);
RequestBody body = RequestBody.create(json, MEDIA_TYPE);
return post(header, path, responseType, json, body);
}
@Override
public Mono> postAsForm(HeaderBuilder header, String path, IN message, Class responseType) {
final Map map = codec.toMap(message);
final String strMessage = JacksonCodec.mapToStringBuilder(map,new StringBuilder()).toString();
RequestBody body = formBody(map);
return post(header, path, responseType, strMessage, body);
}
@Override
public Mono> postAsParams(HeaderBuilder header, String path, IN message, Class responseType) {
final Map map = codec.toMap(message);
final StringBuilder strMessage = JacksonCodec.mapToStringBuilder(map,new StringBuilder());
return parameterisedRequest(Method.POST,header, path, strMessage, json -> codec.fromJson(json, responseType));
}
@Override
public Mono> get(HeaderBuilder header, String path, Class responseType) {
return parameterisedRequest(Method.GET, header,path,null, json -> codec.fromJson(json, responseType));
}
@Override
public Mono> get(HeaderBuilder header, String path, IN message, Class responseType) {
return parameterisedRequest(Method.GET, header,path,message, json -> codec.fromJson(json, responseType));
}
@Override
public Mono>> getMany(HeaderBuilder header, String path, IN message, Class responseType) {
return parameterisedRequest(Method.GET, header,path,message, json -> codec.fromJsonArray(json, responseType));
}
@Override
public Mono> postAsJson(String path, IN message, Class responseType) {
return postAsJson(null,path,message,responseType);
}
@Override
public Mono> get(String path, IN message, Class responseType) {
return get(null,path,message,responseType);
}
@Override
public Mono> get(String path, Class responseType) {
return get(null,path,null,responseType);
}
@Override
public Mono>> getMany(String path, IN message, Class responseType) {
return getMany(null, path,message,responseType);
}
@Override
public Mono>> getMany(HeaderBuilder header, String path, Class responseType) {
return getMany(header, path,null,responseType);
}
@Override
public Mono>> getMany(String path, Class responseType) {
return getMany(null, path,null,responseType);
}
@Override
public Codec getCodec() {
return codec;
}
@Override
public Mono> put(HeaderBuilder header, String path, IN message, Class responseType) {
RequestBody body = RequestBody.create(codec.toJson(message), MEDIA_TYPE);
return parameterisedRequest(Method.PUT, header, path, body, json -> codec.fromJson(json, responseType));
}
@NonNull
private Mono> post(HeaderBuilder header, String path, Class responseType, String message, RequestBody body) {
LOGGER.info("POST: {}{} [{}]",urlPrefix, path, message);
Request.Builder requestBuilder = buildRequest(header, path, message);
return Mono.fromSupplier(() ->
requestBuilder
.post(body)
.build()).flatMap(r ->
Mono.create(sink ->
client.newCall(r).enqueue(
new FnCallback<>(sink, new Event<>(path + "(" + message + ")" + headers(r)), res -> codec.fromJson(res, responseType)))));
}
private Mono> doParameterisedRequest(
Method method,
Map headers,
String path,
String queryString,
RequestBody body,
Function postProcessor
){
HttpUrl url = Objects.requireNonNull(HttpUrl.parse(urlPrefix + path)).newBuilder()
.encodedQuery(queryString)
.build();
Request.Builder builder = new Request.Builder();
builder.url(url);
headers.forEach(builder::addHeader);
builder.method(method.name(), body);
LOGGER.debug("{}: {}",method, url);
return Mono.fromSupplier(builder::build)
.flatMap(r->
Mono.create( sink ->
client.newCall(r).enqueue(
new FnCallback<>(sink, new Event<>(method + ": " + url), postProcessor))));
}
@Override
public Mono> parameterisedRequest(
Method method,
Map headers,
String path,
String queryString,
RequestBody payload,
Class responseType
) {
return doParameterisedRequest(method, headers, path, queryString, payload, json -> codec.fromJson(json, responseType));
}
@Override
public Mono>> parameterisedRequestMany(
Method method,
Map headers,
String path,
String queryString,
RequestBody payload,
Class responseType
) {
return doParameterisedRequest(method, headers, path, queryString, payload, json -> codec.fromJsonArray(json, responseType));
}
private Mono> parameterisedRequest(Method method, HeaderBuilder header, String path, StringBuilder queryString, Function fn) {
String params = queryString==null?null:queryString.toString();
String url = queryString==null?path:queryString.insert(0,"?").insert(0,path).toString();
LOGGER.debug("{}/{}: {}", urlPrefix,method.name(),url);
return Mono.fromSupplier(() ->
addMethod(method, buildRequest(header, url, params)).build())
.flatMap(r->
Mono.create( sink ->
client.newCall(r).enqueue(
new FnCallback<>(sink, new Event<>(url), fn))));
}
@NonNull
private Mono> parameterisedRequest(Method method, HeaderBuilder header, String path, IN message, Function fn) {
return parameterisedRequest(method,header,path,codec.toQueryString(message),fn);
}
private Request.Builder buildRequest(HeaderBuilder header, String path, String message) {
Request.Builder builder = new Request.Builder();
builder.url(urlPrefix + path);
if(header!=null){
return header.addHeaders(builder, message);
}else return builder;
}
@NonNull
private static Request.Builder addMethod(Method method, Request.Builder builder) {
if(method == Method.GET){
builder = builder.get();
}else if(method == Method.POST){
builder = builder.post(RequestBody.create("", MEDIA_TYPE));
}else if(method == Method.PUT){
builder = builder.put(RequestBody.create("", MEDIA_TYPE));
}else{
builder = builder.delete(RequestBody.create("", MEDIA_TYPE));
}
return builder;
}
@NonNull
private static RequestBody formBody(Map map) {
FormBody.Builder builder = new FormBody.Builder();
for(Map.Entry e : map.entrySet()){
builder.add(e.getKey(), e.getValue().toString());
}
return builder.build();
}
@NonNull
private static String headers(Request request){
StringBuilder b = new StringBuilder("[");
Headers headers = request.headers();
headers.names().forEach(n -> b.append(n).append("=").append(headers.values(n)).append(","));
b.append("]");
return b.toString();
}
@RequiredArgsConstructor
private static class FnCallback implements Callback{
private final MonoSink> sink;
private final Event event;
private final Function fn;
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
sink.error(e);
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
try (response; var responseBody = response.body()) {
String json = responseBody.string();
try {
O value = fn.apply(json);
event.response(json, value);
sink.success(event);
} catch (Exception e) {
LOGGER.error("Failed to process {} response: {}",event.getRequestInfo(), json, e);
event.response(json, null);
sink.error(new CxtlEventException(e, event));
}
}
}
}
}