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.
org.jboss.resteasy.client.jaxrs.internal.ClientInvocation Maven / Gradle / Ivy
package org.jboss.resteasy.client.jaxrs.internal;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.function.Function;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.ClientErrorException;
import javax.ws.rs.ForbiddenException;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.NotAcceptableException;
import javax.ws.rs.NotAllowedException;
import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.NotSupportedException;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.RedirectionException;
import javax.ws.rs.ServerErrorException;
import javax.ws.rs.ServiceUnavailableException;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.ClientResponseFilter;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.InvocationCallback;
import javax.ws.rs.client.ResponseProcessingException;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Variant;
import javax.ws.rs.ext.Providers;
import javax.ws.rs.ext.WriterInterceptor;
import org.jboss.resteasy.client.exception.WebApplicationExceptionWrapper;
import org.jboss.resteasy.client.jaxrs.ClientHttpEngine;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.engines.AsyncClientHttpEngine;
import org.jboss.resteasy.client.jaxrs.engines.AsyncClientHttpEngine.ResultExtractor;
import org.jboss.resteasy.client.jaxrs.engines.ReactiveClientHttpEngine;
import org.jboss.resteasy.client.jaxrs.i18n.LogMessages;
import org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker;
import org.jboss.resteasy.core.ResteasyContext;
import org.jboss.resteasy.core.ResteasyContext.CloseableContext;
import org.jboss.resteasy.core.interception.jaxrs.AbstractWriterInterceptorContext;
import org.jboss.resteasy.core.interception.jaxrs.ClientWriterInterceptorContext;
import org.jboss.resteasy.plugins.providers.sse.EventInput;
import org.jboss.resteasy.specimpl.MultivaluedTreeMap;
import org.jboss.resteasy.spi.util.Types;
import org.jboss.resteasy.tracing.RESTEasyTracingLogger;
import org.jboss.resteasy.util.DelegatingOutputStream;
import org.reactivestreams.Publisher;
/**
* @author Bill Burke
* @version $Revision: 1 $
*/
public class ClientInvocation implements Invocation
{
protected RESTEasyTracingLogger tracingLogger;
protected ResteasyClient client;
protected ClientRequestHeaders headers;
protected String method;
protected Object entity;
protected Type entityGenericType;
protected Class> entityClass;
protected Annotation[] entityAnnotations;
protected ClientConfiguration configuration;
protected URI uri;
protected boolean chunked;
protected ClientInvoker clientInvoker;
protected WebTarget actualTarget;
// todo need a better solution for this. Apache Http Client 4 does not let you obtain the OutputStream before executing this request.
// That is problematic for wrapping the output stream in e.g. a RequestFilter for transparent compressing.
protected DelegatingOutputStream delegatingOutputStream = new DelegatingOutputStream();
protected OutputStream entityStream = delegatingOutputStream;
public ClientInvocation(final ResteasyClient client, final URI uri, final ClientRequestHeaders headers, final ClientConfiguration parent)
{
this.uri = uri;
this.client = client;
this.configuration = new ClientConfiguration(parent);
this.headers = headers;
initTracingSupport();
}
private void initTracingSupport() {
final RESTEasyTracingLogger tracingLogger;
if (RESTEasyTracingLogger.isTracingConfigALL(configuration)) {
tracingLogger = RESTEasyTracingLogger.create(this.toString(),
configuration,
this.toString());
} else {
tracingLogger = RESTEasyTracingLogger.empty();
}
this.tracingLogger = tracingLogger;
}
protected ClientInvocation(final ClientInvocation clientInvocation)
{
this.client = clientInvocation.client;
this.configuration = new ClientConfiguration(clientInvocation.configuration);
this.headers = new ClientRequestHeaders(this.configuration);
MultivaluedTreeMap.copy(clientInvocation.headers.getHeaders(), this.headers.headers);
this.method = clientInvocation.method;
this.entity = clientInvocation.entity;
this.entityGenericType = clientInvocation.entityGenericType;
this.entityClass = clientInvocation.entityClass;
this.entityAnnotations = clientInvocation.entityAnnotations;
this.uri = clientInvocation.uri;
this.chunked = clientInvocation.chunked;
this.tracingLogger = clientInvocation.tracingLogger;
this.clientInvoker = clientInvocation.clientInvoker;
}
/**
* Extracts result from response throwing an appropriate exception if not a successful response.
*
* @param responseType generic type
* @param response response entity
* @param annotations array of annotations
* @param type
* @return extracted result of type T
*/
public static T extractResult(GenericType responseType, Response response, Annotation[] annotations)
{
int status = response.getStatus();
if (status >= 200 && status < 300)
{
try
{
if (response.getMediaType() == null)
{
return null;
}
else
{
T rtn = response.readEntity(responseType, annotations);
if (InputStream.class.isInstance(rtn) || Reader.class.isInstance(rtn)
|| EventInput.class.isInstance(rtn) || Publisher.class.isInstance(rtn))
{
if (response instanceof ClientResponse)
{
ClientResponse clientResponse = (ClientResponse) response;
clientResponse.noReleaseConnection();
}
}
return rtn;
}
}
catch (WebApplicationException wae)
{
try
{
response.close();
}
catch (Exception e)
{
}
throw wae;
}
catch (Throwable throwable)
{
try
{
response.close();
}
catch (Exception e)
{
}
throw new ResponseProcessingException(response, throwable);
}
finally
{
if (response.getMediaType() == null)
response.close();
}
}
try
{
// Buffer the entity for any exception thrown as the response may have any entity the user wants
// We don't want to leave the connection open though.
String s = String.class.cast(response.getHeaders().getFirst("resteasy.buffer.exception.entity"));
if (s == null || Boolean.parseBoolean(s))
{
response.bufferEntity();
}
else
{
// close connection
if (response instanceof ClientResponse)
{
try
{
ClientResponse.class.cast(response).releaseConnection();
}
catch (IOException e)
{
// Ignore
}
}
}
if (status >= 300 && status < 400)
{
throw WebApplicationExceptionWrapper.wrap(new RedirectionException(response));
}
return handleErrorStatus(response);
}
finally
{
// close if no content
if (response.getMediaType() == null)
response.close();
}
}
/**
* Throw an exception. Expecting a status of 400 or greater.
*
* @param response response entity
* @param type
* @return unreachable
*/
public static T handleErrorStatus(Response response)
{
final int status = response.getStatus();
switch (status)
{
case 400 :
throw WebApplicationExceptionWrapper.wrap(new BadRequestException(response));
case 401 :
throw WebApplicationExceptionWrapper.wrap(new NotAuthorizedException(response));
case 403 :
throw WebApplicationExceptionWrapper.wrap(new ForbiddenException(response));
case 404 :
throw WebApplicationExceptionWrapper.wrap(new NotFoundException(response));
case 405 :
throw WebApplicationExceptionWrapper.wrap(new NotAllowedException(response));
case 406 :
throw WebApplicationExceptionWrapper.wrap(new NotAcceptableException(response));
case 415 :
throw WebApplicationExceptionWrapper.wrap(new NotSupportedException(response));
case 500 :
throw WebApplicationExceptionWrapper.wrap(new InternalServerErrorException(response));
case 503 :
throw WebApplicationExceptionWrapper.wrap(new ServiceUnavailableException(response));
default :
break;
}
if (status >= 400 && status < 500)
throw WebApplicationExceptionWrapper.wrap(new ClientErrorException(response));
if (status >= 500)
throw WebApplicationExceptionWrapper.wrap(new ServerErrorException(response));
throw WebApplicationExceptionWrapper.wrap(new WebApplicationException(response));
}
public ClientConfiguration getClientConfiguration()
{
return configuration;
}
public ResteasyClient getClient()
{
return client;
}
public DelegatingOutputStream getDelegatingOutputStream()
{
return delegatingOutputStream;
}
public void setDelegatingOutputStream(DelegatingOutputStream delegatingOutputStream)
{
this.delegatingOutputStream = delegatingOutputStream;
}
public OutputStream getEntityStream()
{
return entityStream;
}
public void setEntityStream(OutputStream entityStream)
{
this.entityStream = entityStream;
}
public URI getUri()
{
return uri;
}
public void setUri(URI uri)
{
this.uri = uri;
}
public Annotation[] getEntityAnnotations()
{
return entityAnnotations;
}
public void setEntityAnnotations(Annotation[] entityAnnotations)
{
this.entityAnnotations = entityAnnotations;
}
public String getMethod()
{
return method;
}
public void setMethod(String method)
{
this.method = method;
}
public void setHeaders(ClientRequestHeaders headers)
{
this.headers = headers;
}
public Map getMutableProperties()
{
return configuration.getMutableProperties();
}
public Object getEntity()
{
return entity;
}
public Type getEntityGenericType()
{
return entityGenericType;
}
public Class> getEntityClass()
{
return entityClass;
}
public ClientRequestHeaders getHeaders()
{
return headers;
}
public void setEntity(Entity> entity)
{
if (entity == null)
{
this.entity = null;
this.entityAnnotations = null;
this.entityClass = null;
this.entityGenericType = null;
}
else
{
Object ent = entity.getEntity();
setEntityObject(ent);
this.entityAnnotations = entity.getAnnotations();
Variant v = entity.getVariant();
headers.setMediaType(v.getMediaType());
headers.setLanguage(v.getLanguage());
headers.header("Content-Encoding", null);
headers.header("Content-Encoding", v.getEncoding());
}
}
public void setEntityObject(Object ent)
{
if (ent instanceof GenericEntity)
{
GenericEntity> genericEntity = (GenericEntity>) ent;
entityClass = genericEntity.getRawType();
entityGenericType = genericEntity.getType();
this.entity = genericEntity.getEntity();
}
else
{
if (ent == null)
{
this.entity = null;
this.entityClass = null;
this.entityGenericType = null;
}
else
{
this.entity = ent;
this.entityClass = ent.getClass();
this.entityGenericType = ent.getClass();
}
}
}
public void writeRequestBody(OutputStream outputStream) throws IOException
{
if (entity == null)
{
return;
}
WriterInterceptor[] interceptors = getWriterInterceptors();
AbstractWriterInterceptorContext ctx = new ClientWriterInterceptorContext(interceptors,
configuration.getProviderFactory(), entity, entityClass, entityGenericType, entityAnnotations,
headers.getMediaType(), headers.getHeaders(), outputStream, getMutableProperties(), tracingLogger);
final long timestamp = tracingLogger.timestamp("WI_SUMMARY");
try {
ctx.proceed();
} finally {
tracingLogger.logDuration("WI_SUMMARY", timestamp,
ctx.getProcessedInterceptorCount());
}
}
public WriterInterceptor[] getWriterInterceptors()
{
return configuration.getWriterInterceptors(null, null);
}
public ClientRequestFilter[] getRequestFilters()
{
return configuration.getRequestFilters(null, null);
}
public ClientResponseFilter[] getResponseFilters()
{
return configuration.getResponseFilters(null, null);
}
// Invocation methods
public Configuration getConfiguration()
{
return configuration;
}
public boolean isChunked()
{
return chunked;
}
public void setChunked(boolean chunked)
{
this.chunked = chunked;
}
@Override
public ClientResponse invoke()
{
try(CloseableContext ctx = pushProvidersContext())
{
ClientRequestContextImpl requestContext = new ClientRequestContextImpl(this);
ClientResponse aborted = filterRequest(requestContext);
// spec requires that aborted response go through filter/interceptor chains.
ClientResponse response = (aborted != null) ? aborted : (ClientResponse)client.httpEngine().invoke(this);
return filterResponse(requestContext, response);
}
catch (ResponseProcessingException e)
{
if (e.getResponse() != null)
{
e.getResponse().close();
}
throw e;
}
}
@SuppressWarnings("unchecked")
@Override
public T invoke(Class responseType)
{
Response response = invoke();
if (Response.class.equals(responseType))
return (T) response;
return extractResult(new GenericType(responseType), response, null);
}
@SuppressWarnings("unchecked")
@Override
public T invoke(GenericType responseType)
{
Response response = invoke();
if (responseType.getRawType().equals(Response.class))
return (T) response;
return extractResult(responseType, response, null);
}
@Override
public Future submit()
{
return doSubmit(false, null, result -> result);
}
@Override
public Future submit(final Class responseType)
{
return doSubmit(false, null, getResponseTypeExtractor(responseType));
}
@Override
public Future submit(final GenericType responseType)
{
return doSubmit(false, null, getGenericTypeExtractor(responseType));
}
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public Future submit(final InvocationCallback callback)
{
GenericType genericType = (GenericType) new GenericType()
{
};
Type[] typeInfo = Types.getActualTypeArgumentsOfAnInterface(callback.getClass(), InvocationCallback.class);
if (typeInfo != null)
{
genericType = new GenericType(typeInfo[0]);
}
return doSubmit(true, callback, getGenericTypeExtractor(genericType));
}
private Future doSubmit(boolean buffered, InvocationCallback callback, ResultExtractor extractor) {
if (client.httpEngine() instanceof AsyncClientHttpEngine)
{
return submitRequest(extractor, buffered)
.whenComplete((response, error) -> {
if (callback != null) {
if (error != null) {
callback.failed(error);
} else {
try {
callback.completed(response);
} catch (Throwable t) {
LogMessages.LOGGER.exceptionIgnored(t);
}
}
}
})
.toCompletableFuture();
}
else
{
return executorSubmit(asyncInvocationExecutor(), callback, extractor);
}
}
public ExecutorService asyncInvocationExecutor() {
return client.asyncInvocationExecutor();
}
private Function, Publisher> getPublisherExtractorFunction(boolean buffered) {
final ClientHttpEngine httpEngine = client.httpEngine();
return (httpEngine instanceof ReactiveClientHttpEngine)
? ext -> ((ReactiveClientHttpEngine) httpEngine).submitRx(this, buffered, ext) : null;
}
@SuppressWarnings("unchecked")
protected static ResultExtractor getGenericTypeExtractor(GenericType responseType) {
return response -> {
if (responseType.getRawType().equals(Response.class))
return (T) response;
return ClientInvocation.extractResult(responseType, response, null);
};
}
@SuppressWarnings("unchecked")
protected static ResultExtractor getResponseTypeExtractor(Class responseType) {
return response -> {
if (Response.class.equals(responseType))
return (T) response;
return ClientInvocation.extractResult(new GenericType(responseType), response, null);
};
}
public CompletableFuture submitCF()
{
return doSubmit(response -> response, false);
}
public CompletableFuture submitCF(final Class responseType)
{
return doSubmit(getResponseTypeExtractor(responseType), true);
}
public CompletableFuture submitCF(final GenericType responseType)
{
return doSubmit(getGenericTypeExtractor(responseType), true);
}
private CompletableFuture doSubmit(ResultExtractor extractor, boolean buffered) {
if (client.httpEngine() instanceof AsyncClientHttpEngine)
{
return submitRequest(extractor, buffered)
.toCompletableFuture();
}
else
{
return executorSubmit(asyncInvocationExecutor(), null, extractor);
}
}
class ReactiveInvocation {
private final ReactiveClientHttpEngine reactiveEngine;
ReactiveInvocation(final ReactiveClientHttpEngine reactiveEngine) {
this.reactiveEngine = reactiveEngine;
}
public Publisher submit()
{
return doSubmitRx(response -> response, false);
}
public Publisher submit(final Class responseType)
{
return doSubmitRx(getResponseTypeExtractor(responseType), true);
}
public Publisher submit(final GenericType responseType)
{
return doSubmitRx(getGenericTypeExtractor(responseType), true);
}
private Publisher doSubmitRx(ResultExtractor extractor, boolean buffered) {
return rxSubmit(
reactiveEngine,
getPublisherExtractorFunction(buffered),
extractor
);
}
private Publisher rxSubmit(
final ReactiveClientHttpEngine reactiveEngine,
final Function, Publisher> asyncHttpEngineSubmitFn,
final ResultExtractor extractor
) {
final ClientRequestContextImpl requestContext = new ClientRequestContextImpl(ClientInvocation.this);
try(CloseableContext ctx = pushProvidersContext())
{
ClientResponse aborted = filterRequest(requestContext);
if (aborted != null)
{
// spec requires that aborted response go through filter/interceptor chains.
aborted = filterResponse(requestContext, aborted);
T result = extractor.extractResult(aborted);
return reactiveEngine.just(result);
}
}
catch (Exception ex)
{
return reactiveEngine.error(ex);
}
return asyncHttpEngineSubmitFn.apply(response -> {
try(CloseableContext ctx = pushProvidersContext())
{
return extractor.extractResult(filterResponse(requestContext, response));
}
});
}
}
/**
* If the client's HTTP engine implements {@link ReactiveClientHttpEngine} then you can access
* the latter's {@link Publisher} via this method.
*/
public Optional reactive() {
if (client.httpEngine() instanceof ReactiveClientHttpEngine) {
return Optional.of(new ReactiveInvocation((ReactiveClientHttpEngine)client.httpEngine()));
}
return Optional.empty();
}
@Override
public Invocation property(String name, Object value)
{
configuration.property(name, value);
return this;
}
public ClientInvoker getClientInvoker() {
return clientInvoker;
}
public void setClientInvoker(ClientInvoker clientInvoker) {
this.clientInvoker = clientInvoker;
}
// internals
private CloseableContext pushProvidersContext()
{
CloseableContext ret = ResteasyContext.addCloseableContextDataLevel();
ResteasyContext.pushContext(Providers.class, configuration);
return ret;
}
protected ClientResponse filterRequest(ClientRequestContextImpl requestContext)
{
ClientRequestFilter[] requestFilters = getRequestFilters();
ClientResponse aborted = null;
if (requestFilters != null && requestFilters.length > 0)
{
for (ClientRequestFilter filter : requestFilters)
{
try
{
filter.filter(requestContext);
if (requestContext.getAbortedWithResponse() != null)
{
aborted = new AbortedResponse(configuration, requestContext.getAbortedWithResponse());
break;
}
}
catch (ProcessingException e)
{
throw e;
}
catch (Throwable e)
{
throw new ProcessingException(e);
}
}
}
return aborted;
}
protected ClientResponse filterResponse(ClientRequestContextImpl requestContext, ClientResponse response)
{
response.setProperties(configuration.getMutableProperties());
ClientResponseFilter[] responseFilters = getResponseFilters();
if (responseFilters != null && responseFilters.length > 0)
{
ClientResponseContextImpl responseContext = new ClientResponseContextImpl(response);
for (ClientResponseFilter filter : responseFilters)
{
try
{
filter.filter(requestContext, responseContext);
}
catch (ResponseProcessingException e)
{
throw e;
}
catch (Throwable e)
{
throw new ResponseProcessingException(response, e);
}
}
}
return response;
}
private CompletionStage submitRequest(final ResultExtractor extractor, final boolean buffered) {
final ClientRequestContextImpl requestContext = new ClientRequestContextImpl(this);
return CompletableFuture.supplyAsync(() -> {
try (CloseableContext ctx = pushProvidersContext()) {
ClientResponse aborted = filterRequest(requestContext);
if (aborted != null) {
// spec requires that aborted response go through filter/interceptor chains.
aborted = filterResponse(requestContext, aborted);
return extractor.extractResult(aborted);
}
}
return null;
}
, asyncInvocationExecutor())
.thenCompose(aborted -> {
if (aborted != null) {
return CompletableFuture.completedFuture(aborted);
}
final ResultExtractor wrapped = (response) -> extractor.extractResult(filterResponse(requestContext, response));
return ((AsyncClientHttpEngine) client.httpEngine()).submit(ClientInvocation.this, buffered, wrapped);
});
}
private CompletableFuture executorSubmit(ExecutorService executor, final InvocationCallback callback,
final ResultExtractor extractor)
{
return CompletableFuture.supplyAsync(() -> {
// FIXME: why does this have no context?
// ensure the future and the callback see the same result
ClientResponse response = null;
try
{
response = invoke(); // does filtering too
T result = extractor.extractResult(response);
callCompletedNoThrow(callback, result);
return result;
}
catch (Exception e)
{
callFailedNoThrow(callback, e);
throw e;
}
finally
{
if (response != null && callback != null)
response.close();
}
}, executor);
}
private static void callCompletedNoThrow(InvocationCallback callback, T result)
{
if (callback != null)
{
try
{
callback.completed(result);
}
catch (Exception e)
{
//logger.error("ignoring exception in InvocationCallback", e);
}
}
}
private static void callFailedNoThrow(InvocationCallback callback, Exception exception)
{
if (callback != null)
{
try
{
callback.failed(exception);
}
catch (Exception e)
{
//logger.error("ignoring exception in InvocationCallback", e);
}
}
}
public RESTEasyTracingLogger getTracingLogger() {
return tracingLogger;
}
public void setActualTarget(WebTarget target)
{
this.actualTarget = target;
}
public WebTarget getActualTarget()
{
return this.actualTarget;
}
}