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.
io.micronaut.jaxrs.client.JaxRsInvocation Maven / Gradle / Ivy
/*
* Copyright 2017-2024 original authors
*
* 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
*
* https://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 io.micronaut.jaxrs.client;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.io.buffer.ByteBuffer;
import io.micronaut.core.type.Argument;
import io.micronaut.http.HttpHeaders;
import io.micronaut.http.HttpMethod;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.MediaType;
import io.micronaut.http.MutableHttpHeaders;
import io.micronaut.http.MutableHttpRequest;
import io.micronaut.http.MutableHttpResponse;
import io.micronaut.http.client.exceptions.HttpClientResponseException;
import io.micronaut.jaxrs.common.JaxRsArgumentUtil;
import io.micronaut.jaxrs.common.JaxRsMutableResponse;
import io.micronaut.jaxrs.common.JaxRsResponse;
import jakarta.ws.rs.ProcessingException;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.client.AsyncInvoker;
import jakarta.ws.rs.client.ClientRequestFilter;
import jakarta.ws.rs.client.ClientResponseFilter;
import jakarta.ws.rs.client.CompletionStageRxInvoker;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.client.Invocation;
import jakarta.ws.rs.client.InvocationCallback;
import jakarta.ws.rs.core.GenericEntity;
import jakarta.ws.rs.core.GenericType;
import jakarta.ws.rs.core.Response;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.function.BiConsumer;
/**
* The implementation of {@link Invocation}, {@link CompletionStageRxInvoker} and {@link AsyncInvoker}.
*
* @author Denis Stepanov
* @since 4.6
*/
@Internal
final class JaxRsInvocation implements Invocation, CompletionStageRxInvoker, AsyncInvoker {
@NonNull
private final JaxRsClient client;
@NonNull
private final URI uri;
@NonNull
private final String method;
@Nullable
private final Entity> entity;
private final MutableHttpHeaders mutableHttpHeaders;
private final JaxRsConfiguration configuration;
JaxRsInvocation(JaxRsClient client,
@NonNull URI uri,
String method,
@Nullable Entity> entity,
MutableHttpHeaders mutableHttpHeaders,
JaxRsConfiguration configuration) {
this.client = client;
this.uri = uri;
this.method = method;
this.entity = entity;
this.mutableHttpHeaders = mutableHttpHeaders;
this.configuration = configuration.copy();
}
@Override
public Invocation property(String name, Object value) {
configuration.addProperty(name, value);
return this;
}
@Override
public Response invoke() {
return asyncBlock(async(Argument.of(Response.class)));
}
@Override
public T invoke(Class aClass) {
return asyncBlock(async(Argument.of(aClass)));
}
@Override
public T invoke(GenericType genericType) {
return invoke(JaxRsArgumentUtil.from(genericType));
}
@Override
public Future submit() {
return async(Argument.of(Response.class));
}
@Override
public Future submit(Class aClass) {
return async(Argument.of(aClass));
}
@Override
public Future submit(GenericType genericType) {
return async(JaxRsArgumentUtil.from(genericType));
}
@Override
public Future submit(InvocationCallback invocationCallback) {
return async(JaxRsArgumentUtil.from(invocationCallback)).whenComplete(withCallback(invocationCallback));
}
private T invoke(Argument type) {
return asyncBlock(async(type));
}
private T asyncBlock(CompletableFuture future) {
try {
return future.get();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
if (e.getCause() instanceof RuntimeException runtimeException) {
throw runtimeException;
}
throw new ProcessingException(e);
}
}
T invokeExchange(Argument type, Entity> entity) {
return asyncBlock(async(method, type, entity));
}
private CompletableFuture async(Argument type) {
return async((String) null, type, null);
}
private CompletableFuture async(HttpMethod method, Argument type) {
return async(method.name(), type, null);
}
private CompletableFuture async(String method, Argument type) {
return async(method, type, null);
}
private CompletableFuture async(HttpMethod method, Argument type, Entity> entity) {
return async(method.name(), type, entity);
}
private CompletableFuture async(String method, Argument type, Entity> entity) {
var future = new CompletableFuture();
try {
var requestBodyType = Argument.of(Object.class);
if (entity == null) {
entity = this.entity;
}
MutableHttpRequest request = createRequest(method, entity);
if (entity != null) {
Object entityValue = entity.getEntity();
if (entityValue instanceof GenericEntity> genericEntity) {
requestBodyType = (Argument) JaxRsArgumentUtil.from(genericEntity);
} else {
requestBodyType = (Argument) Argument.of(entityValue.getClass(), JaxRsArgumentUtil.createAnnotationMetadata(entity.getAnnotations()));
}
}
List requestFilters = configuration.getRequestFilters();
JaxRsClientRequestContext requestContext = new JaxRsClientRequestContext(client, configuration, request, requestBodyType);
if (!requestFilters.isEmpty()) {
for (ClientRequestFilter requestFilter : requestFilters) {
requestFilter.filter(requestContext);
Response response = requestContext.getResponse();
if (response != null) {
response = filterResponse(response, requestContext);
if (type.getType().equals(Response.class)) {
future.complete((T) response);
} else {
if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) {
future.complete(response.readEntity(type.getType()));
} else {
future.completeExceptionally(new WebApplicationException(response));
}
}
return future;
}
}
}
client.getHttpClient().exchange(request)
.subscribe(new Subscriber<>() {
@Override
public void onSubscribe(Subscription subscription) {
subscription.request(1L);
}
@Override
public void onNext(HttpResponse response) {
try {
JaxRsMutableResponse jaxRsMutableResponse = filterResponse(response.toMutableResponse(), requestContext);
complete(jaxRsMutableResponse);
} catch (Exception e) {
future.completeExceptionally(new ProcessingException(e));
}
}
@Override
public void onError(Throwable throwable) {
if (throwable instanceof HttpClientResponseException httpClientResponseException) {
HttpResponse> response = httpClientResponseException.getResponse();
if (isResponseReturn()) {
try {
MutableHttpResponse> mutableResponse = response.toMutableResponse();
JaxRsMutableResponse jaxRsMutableResponse = filterResponse(mutableResponse, requestContext);
complete(jaxRsMutableResponse);
} catch (Exception e) {
future.completeExceptionally(new ProcessingException(e));
}
} else {
future.completeExceptionally(new WebApplicationException(new JaxRsResponse(response)));
}
} else {
future.completeExceptionally(new ProcessingException(throwable));
}
}
@Override
public void onComplete() {
if (!future.isDone()) {
future.completeExceptionally(new ProcessingException("Expected a response"));
}
}
private void complete(JaxRsMutableResponse jaxRsMutableResponse) {
if (isResponseReturn()) {
future.complete((T) jaxRsMutableResponse);
} else {
future.complete(jaxRsMutableResponse.readEntity(type));
}
}
private boolean isResponseReturn() {
return type.getType().equals(Response.class);
}
});
} catch (Exception e) {
future.completeExceptionally(new ProcessingException(e));
}
return future;
}
private Response filterResponse(Response response, JaxRsClientRequestContext requestContext) {
if (response instanceof JaxRsMutableResponse jaxRsMutableResponse) {
jaxRsMutableResponse = jaxRsMutableResponse.withEntityReader(configuration.createHttpMessageEntityReader());
filterResponse(jaxRsMutableResponse, requestContext);
return jaxRsMutableResponse;
}
if (response instanceof JaxRsResponse jaxRsResponse) {
jaxRsResponse = jaxRsResponse.withEntityReader(configuration.createHttpMessageEntityReader());
MutableHttpResponse> mutableResponse = jaxRsResponse.getResponse().toMutableResponse();
JaxRsMutableResponse jaxRsMutableResponse = new JaxRsMutableResponse(mutableResponse, configuration.createHttpMessageEntityReader());
filterResponse(jaxRsMutableResponse, requestContext);
return jaxRsMutableResponse;
}
return response;
}
private JaxRsMutableResponse filterResponse(@NonNull MutableHttpResponse> mutableHttpResponse, @Nullable JaxRsClientRequestContext requestContext) {
JaxRsMutableResponse response = new JaxRsMutableResponse(mutableHttpResponse, configuration.createHttpMessageEntityReader());
filterResponse(response, requestContext);
return response;
}
private void filterResponse(@NonNull JaxRsMutableResponse jaxRsMutableResponse, @Nullable JaxRsClientRequestContext requestContext) {
try {
List filters = configuration.getResponseFilters();
if (!filters.isEmpty()) {
JaxRsClientResponseContext responseContext = new JaxRsClientResponseContext(jaxRsMutableResponse);
for (ClientResponseFilter filter : filters) {
filter.filter(requestContext, responseContext);
}
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
private MutableHttpRequest createRequest(@Nullable String method, @Nullable Entity> entity) {
if (method == null) {
method = this.method;
}
HttpMethod httpMethod = HttpMethod.valueOf(method);
MutableHttpRequest mutableHttpRequest = httpMethod ==
HttpMethod.CUSTOM ? HttpRequest.create(HttpMethod.CUSTOM, uri.toString(), method) : HttpRequest.create(httpMethod, uri.toString());
if (entity != null) {
mutableHttpRequest = mutableHttpRequest.contentType(MediaType.of(entity.getMediaType().toString()));
mutableHttpRequest = mutableHttpRequest.body(entity.getEntity());
Locale language = entity.getLanguage();
if (language != null) {
mutableHttpRequest.getHeaders().set(HttpHeaders.CONTENT_LANGUAGE, language.toLanguageTag());
}
}
if (mutableHttpHeaders != null) {
mutableHttpHeaders.forEachValue(mutableHttpRequest::header);
}
Argument bodyArgument;
Object body;
if (entity != null) {
bodyArgument = (Argument) JaxRsArgumentUtil.from(entity);
body = entity.getEntity();
} else {
body = mutableHttpRequest.getBody().orElse(null);
if (body != null) {
bodyArgument = (Argument) Argument.of(body.getClass());
} else {
bodyArgument = null;
}
}
configuration.writeBody(mutableHttpRequest, bodyArgument, body);
return mutableHttpRequest;
}
@Override
public CompletableFuture get() {
return async(HttpMethod.GET, Argument.of(Response.class));
}
@Override
public CompletableFuture get(Class responseType) {
return async(HttpMethod.GET, Argument.of(responseType));
}
@Override
public CompletableFuture get(GenericType responseType) {
return async(HttpMethod.GET, JaxRsArgumentUtil.from(responseType));
}
@Override
public Future get(InvocationCallback callback) {
return async(HttpMethod.GET, JaxRsArgumentUtil.from(callback)).whenComplete(withCallback(callback));
}
@Override
public CompletableFuture put(Entity> entity) {
return async(HttpMethod.PUT, Argument.of(Response.class), entity);
}
@Override
public CompletableFuture put(Entity> entity, Class clazz) {
return async(HttpMethod.PUT, Argument.of(clazz), entity);
}
@Override
public CompletableFuture put(Entity> entity, GenericType type) {
return async(HttpMethod.PUT, JaxRsArgumentUtil.from(type), entity);
}
@Override
public Future put(Entity> entity, InvocationCallback callback) {
return async(HttpMethod.PUT, JaxRsArgumentUtil.from(callback), entity).whenComplete(withCallback(callback));
}
@Override
public CompletableFuture post(Entity> entity) {
return async(HttpMethod.POST, Argument.of(Response.class), entity);
}
@Override
public CompletableFuture post(Entity> entity, Class clazz) {
return async(HttpMethod.POST, Argument.of(clazz), entity);
}
@Override
public CompletableFuture post(Entity> entity, GenericType type) {
return async(HttpMethod.POST, JaxRsArgumentUtil.from(type), entity);
}
@Override
public Future post(Entity> entity, InvocationCallback callback) {
return async(HttpMethod.POST, JaxRsArgumentUtil.from(callback), entity).whenComplete(withCallback(callback));
}
private BiConsumer withCallback(InvocationCallback callback) {
return (response, throwable) -> {
if (throwable != null) {
callback.failed(throwable);
} else {
callback.completed(response);
}
};
}
@Override
public CompletableFuture delete() {
return async(HttpMethod.DELETE, Argument.of(Response.class), entity);
}
@Override
public CompletableFuture delete(Class clazz) {
return async(HttpMethod.DELETE, Argument.of(clazz));
}
@Override
public CompletableFuture delete(GenericType type) {
return async(HttpMethod.DELETE, JaxRsArgumentUtil.from(type));
}
@Override
public Future delete(InvocationCallback callback) {
return async(HttpMethod.DELETE, JaxRsArgumentUtil.from(callback)).whenComplete(withCallback(callback));
}
@Override
public CompletableFuture head() {
return async(HttpMethod.HEAD, Argument.of(Response.class));
}
@Override
public Future head(InvocationCallback callback) {
return async(HttpMethod.HEAD, JaxRsArgumentUtil.from(callback)).whenComplete(withCallback(callback));
}
@Override
public CompletableFuture options() {
return async(HttpMethod.OPTIONS, Argument.of(Response.class));
}
@Override
public CompletableFuture options(Class responseType) {
return async(HttpMethod.OPTIONS, Argument.of(responseType));
}
@Override
public CompletableFuture options(GenericType responseType) {
return async(HttpMethod.OPTIONS, JaxRsArgumentUtil.from(responseType));
}
@Override
public Future options(InvocationCallback callback) {
return async(HttpMethod.OPTIONS, JaxRsArgumentUtil.from(callback)).whenComplete(withCallback(callback));
}
@Override
public CompletableFuture trace() {
return async(HttpMethod.TRACE, Argument.of(Response.class));
}
@Override
public CompletableFuture trace(Class responseType) {
return async(HttpMethod.TRACE, Argument.of(responseType));
}
@Override
public CompletableFuture trace(GenericType responseType) {
return async(HttpMethod.TRACE, JaxRsArgumentUtil.from(responseType));
}
@Override
public Future trace(InvocationCallback callback) {
return async(HttpMethod.TRACE, JaxRsArgumentUtil.from(callback)).whenComplete(withCallback(callback));
}
@Override
public CompletableFuture method(String name) {
return async(name, Argument.of(Response.class));
}
@Override
public CompletableFuture method(String name, Class responseType) {
return async(name, Argument.of(responseType));
}
@Override
public CompletableFuture method(String name, GenericType responseType) {
return async(name, JaxRsArgumentUtil.from(responseType));
}
@Override
public Future method(String name, InvocationCallback callback) {
return async(name, JaxRsArgumentUtil.from(callback)).whenComplete(withCallback(callback));
}
@Override
public CompletableFuture method(String name, Entity> entity) {
return async(name, Argument.of(Response.class), entity);
}
@Override
public CompletableFuture method(String name, Entity> entity, Class responseType) {
return async(name, Argument.of(responseType), entity);
}
@Override
public CompletableFuture method(String name, Entity> entity, GenericType responseType) {
return async(name, JaxRsArgumentUtil.from(responseType), entity);
}
@Override
public Future method(String name, Entity> entity, InvocationCallback callback) {
return async(name, JaxRsArgumentUtil.from(callback), entity).whenComplete(withCallback(callback));
}
}