All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.jersey.client.JerseyInvocation Maven / Gradle / Ivy

Go to download

A bundle project producing JAX-RS RI bundles. The primary artifact is an "all-in-one" OSGi-fied JAX-RS RI bundle (jaxrs-ri.jar). Attached to that are two compressed JAX-RS RI archives. The first archive (jaxrs-ri.zip) consists of binary RI bits and contains the API jar (under "api" directory), RI libraries (under "lib" directory) as well as all external RI dependencies (under "ext" directory). The secondary archive (jaxrs-ri-src.zip) contains buildable JAX-RS RI source bundle and contains the API jar (under "api" directory), RI sources (under "src" directory) as well as all external RI dependencies (under "ext" directory). The second archive also contains "build.xml" ANT script that builds the RI sources. To build the JAX-RS RI simply unzip the archive, cd to the created jaxrs-ri directory and invoke "ant" from the command line.

There is a newer version: 3.1.10
Show newest version
/*
 * Copyright (c) 2011, 2019 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.jersey.client;

import java.lang.reflect.Type;
import java.net.URI;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.logging.Logger;

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.CompletionStageRxInvoker;
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.RxInvoker;
import javax.ws.rs.client.RxInvokerProvider;
import javax.ws.rs.client.SyncInvoker;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;

import org.glassfish.jersey.client.internal.LocalizationMessages;
import org.glassfish.jersey.internal.MapPropertiesDelegate;
import org.glassfish.jersey.internal.inject.Providers;
import org.glassfish.jersey.internal.util.Producer;
import org.glassfish.jersey.internal.util.PropertiesHelper;
import org.glassfish.jersey.internal.util.ReflectionHelper;
import org.glassfish.jersey.process.internal.RequestScope;
import org.glassfish.jersey.spi.ExecutorServiceProvider;

/**
 * Jersey implementation of {@link javax.ws.rs.client.Invocation JAX-RS client-side
 * request invocation} contract.
 *
 * @author Marek Potociar
 */
public class JerseyInvocation implements javax.ws.rs.client.Invocation {

    private static final Logger LOGGER = Logger.getLogger(JerseyInvocation.class.getName());

    private final ClientRequest requestContext;
    // Copy request context when invoke or submit methods are invoked.
    private final boolean copyRequestContext;

    private JerseyInvocation(final Builder builder) {
        this(builder, false);
    }

    private JerseyInvocation(final Builder builder, final boolean copyRequestContext) {
        validateHttpMethodAndEntity(builder.requestContext);

        this.requestContext = new ClientRequest(builder.requestContext);
        this.copyRequestContext = copyRequestContext;
    }

    private enum EntityPresence {
        MUST_BE_NULL,
        MUST_BE_PRESENT,
        OPTIONAL
    }

    private static final Map METHODS = initializeMap();

    private static Map initializeMap() {
        final Map map = new HashMap<>();

        map.put("DELETE", EntityPresence.MUST_BE_NULL);
        map.put("GET", EntityPresence.MUST_BE_NULL);
        map.put("HEAD", EntityPresence.MUST_BE_NULL);
        map.put("OPTIONS", EntityPresence.MUST_BE_NULL);
        map.put("PATCH", EntityPresence.MUST_BE_PRESENT);
        map.put("POST", EntityPresence.OPTIONAL); // we allow to post null instead of entity
        map.put("PUT", EntityPresence.MUST_BE_PRESENT);
        map.put("TRACE", EntityPresence.MUST_BE_NULL);
        return map;
    }

    private void validateHttpMethodAndEntity(final ClientRequest request) {
        boolean suppressExceptions;
        suppressExceptions = PropertiesHelper.isProperty(
                request.getConfiguration().getProperty(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION));

        final Object shcvProperty = request.getProperty(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION);
        if (shcvProperty != null) { // override global configuration with request-specific
            suppressExceptions = PropertiesHelper.isProperty(shcvProperty);
        }

        final String method = request.getMethod();

        final EntityPresence entityPresence = METHODS.get(method.toUpperCase());
        if (entityPresence == EntityPresence.MUST_BE_NULL && request.hasEntity()) {
            if (suppressExceptions) {
                LOGGER.warning(LocalizationMessages.ERROR_HTTP_METHOD_ENTITY_NOT_NULL(method));
            } else {
                throw new IllegalStateException(LocalizationMessages.ERROR_HTTP_METHOD_ENTITY_NOT_NULL(method));
            }
        } else if (entityPresence == EntityPresence.MUST_BE_PRESENT && !request.hasEntity()) {
            if (suppressExceptions) {
                LOGGER.warning(LocalizationMessages.ERROR_HTTP_METHOD_ENTITY_NULL(method));
            } else {
                throw new IllegalStateException(LocalizationMessages.ERROR_HTTP_METHOD_ENTITY_NULL(method));
            }
        }
    }

    /**
     * Jersey-specific {@link javax.ws.rs.client.Invocation.Builder client invocation builder}.
     */
    public static class Builder implements javax.ws.rs.client.Invocation.Builder {

        private final ClientRequest requestContext;

        /**
         * Create new Jersey-specific client invocation builder.
         *
         * @param uri           invoked request URI.
         * @param configuration Jersey client configuration.
         */
        protected Builder(final URI uri, final ClientConfig configuration) {
            this.requestContext = new ClientRequest(uri, configuration, new MapPropertiesDelegate());
        }

        /**
         * Returns a reference to the mutable request context to be invoked.
         *
         * @return mutable request context to be invoked.
         */
        ClientRequest request() {
            return requestContext;
        }

        private void storeEntity(final Entity entity) {
            if (entity != null) {
                requestContext.variant(entity.getVariant());
                requestContext.setEntity(entity.getEntity());
                requestContext.setEntityAnnotations(entity.getAnnotations());
            }
        }

        @Override
        public JerseyInvocation build(final String method) {
            requestContext.setMethod(method);
            return new JerseyInvocation(this, true);
        }

        @Override
        public JerseyInvocation build(final String method, final Entity entity) {
            requestContext.setMethod(method);
            storeEntity(entity);
            return new JerseyInvocation(this, true);
        }

        @Override
        public JerseyInvocation buildGet() {
            requestContext.setMethod("GET");
            return new JerseyInvocation(this, true);
        }

        @Override
        public JerseyInvocation buildDelete() {
            requestContext.setMethod("DELETE");
            return new JerseyInvocation(this, true);
        }

        @Override
        public JerseyInvocation buildPost(final Entity entity) {
            requestContext.setMethod("POST");
            storeEntity(entity);
            return new JerseyInvocation(this, true);
        }

        @Override
        public JerseyInvocation buildPut(final Entity entity) {
            requestContext.setMethod("PUT");
            storeEntity(entity);
            return new JerseyInvocation(this, true);
        }

        @Override
        public javax.ws.rs.client.AsyncInvoker async() {
            return new AsyncInvoker(this);
        }

        @Override
        public Builder accept(final String... mediaTypes) {
            requestContext.accept(mediaTypes);
            return this;
        }

        @Override
        public Builder accept(final MediaType... mediaTypes) {
            requestContext.accept(mediaTypes);
            return this;
        }

        @Override
        public Invocation.Builder acceptEncoding(final String... encodings) {
            requestContext.getHeaders().addAll(HttpHeaders.ACCEPT_ENCODING, (Object[]) encodings);
            return this;
        }

        @Override
        public Builder acceptLanguage(final Locale... locales) {
            requestContext.acceptLanguage(locales);
            return this;
        }

        @Override
        public Builder acceptLanguage(final String... locales) {
            requestContext.acceptLanguage(locales);
            return this;
        }

        @Override
        public Builder cookie(final Cookie cookie) {
            requestContext.cookie(cookie);
            return this;
        }

        @Override
        public Builder cookie(final String name, final String value) {
            requestContext.cookie(new Cookie(name, value));
            return this;
        }

        @Override
        public Builder cacheControl(final CacheControl cacheControl) {
            requestContext.cacheControl(cacheControl);
            return this;
        }

        @Override
        public Builder header(final String name, final Object value) {
            final MultivaluedMap headers = requestContext.getHeaders();

            if (value == null) {
                headers.remove(name);
            } else {
                headers.add(name, value);
            }

            if (HttpHeaders.USER_AGENT.equalsIgnoreCase(name)) {
                requestContext.ignoreUserAgent(value == null);
            }

            return this;
        }

        @Override
        public Builder headers(final MultivaluedMap headers) {
            requestContext.replaceHeaders(headers);
            return this;
        }

        @Override
        public Response get() throws ProcessingException {
            return method("GET");
        }

        @Override
        public  T get(final Class responseType) throws ProcessingException, WebApplicationException {
            return method("GET", responseType);
        }

        @Override
        public  T get(final GenericType responseType) throws ProcessingException, WebApplicationException {
            return method("GET", responseType);
        }

        @Override
        public Response put(final Entity entity) throws ProcessingException {
            return method("PUT", entity);
        }

        @Override
        public  T put(final Entity entity, final Class responseType)
                throws ProcessingException, WebApplicationException {
            return method("PUT", entity, responseType);
        }

        @Override
        public  T put(final Entity entity, final GenericType responseType)
                throws ProcessingException, WebApplicationException {
            return method("PUT", entity, responseType);
        }

        @Override
        public Response post(final Entity entity) throws ProcessingException {
            return method("POST", entity);
        }

        @Override
        public  T post(final Entity entity, final Class responseType)
                throws ProcessingException, WebApplicationException {
            return method("POST", entity, responseType);
        }

        @Override
        public  T post(final Entity entity, final GenericType responseType)
                throws ProcessingException, WebApplicationException {
            return method("POST", entity, responseType);
        }

        @Override
        public Response delete() throws ProcessingException {
            return method("DELETE");
        }

        @Override
        public  T delete(final Class responseType) throws ProcessingException, WebApplicationException {
            return method("DELETE", responseType);
        }

        @Override
        public  T delete(final GenericType responseType) throws ProcessingException, WebApplicationException {
            return method("DELETE", responseType);
        }

        @Override
        public Response head() throws ProcessingException {
            return method("HEAD");
        }

        @Override
        public Response options() throws ProcessingException {
            return method("OPTIONS");
        }

        @Override
        public  T options(final Class responseType) throws ProcessingException, WebApplicationException {
            return method("OPTIONS", responseType);
        }

        @Override
        public  T options(final GenericType responseType) throws ProcessingException, WebApplicationException {
            return method("OPTIONS", responseType);
        }

        @Override
        public Response trace() throws ProcessingException {
            return method("TRACE");
        }

        @Override
        public  T trace(final Class responseType) throws ProcessingException, WebApplicationException {
            return method("TRACE", responseType);
        }

        @Override
        public  T trace(final GenericType responseType) throws ProcessingException, WebApplicationException {
            return method("TRACE", responseType);
        }

        @Override
        public Response method(final String name) throws ProcessingException {
            requestContext.setMethod(name);
            return new JerseyInvocation(this).invoke();
        }

        @Override
        public  T method(final String name, final Class responseType) throws ProcessingException, WebApplicationException {
            if (responseType == null) {
                throw new IllegalArgumentException(LocalizationMessages.RESPONSE_TYPE_IS_NULL());
            }
            requestContext.setMethod(name);
            return new JerseyInvocation(this).invoke(responseType);
        }

        @Override
        public  T method(final String name, final GenericType responseType)
                throws ProcessingException, WebApplicationException {
            if (responseType == null) {
                throw new IllegalArgumentException(LocalizationMessages.RESPONSE_TYPE_IS_NULL());
            }
            requestContext.setMethod(name);
            return new JerseyInvocation(this).invoke(responseType);
        }

        @Override
        public Response method(final String name, final Entity entity) throws ProcessingException {
            requestContext.setMethod(name);
            storeEntity(entity);
            return new JerseyInvocation(this).invoke();
        }

        @Override
        public  T method(final String name, final Entity entity, final Class responseType)
                throws ProcessingException, WebApplicationException {
            if (responseType == null) {
                throw new IllegalArgumentException(LocalizationMessages.RESPONSE_TYPE_IS_NULL());
            }
            requestContext.setMethod(name);
            storeEntity(entity);
            return new JerseyInvocation(this).invoke(responseType);
        }

        @Override
        public  T method(final String name, final Entity entity, final GenericType responseType)
                throws ProcessingException, WebApplicationException {
            if (responseType == null) {
                throw new IllegalArgumentException(LocalizationMessages.RESPONSE_TYPE_IS_NULL());
            }
            requestContext.setMethod(name);
            storeEntity(entity);
            return new JerseyInvocation(this).invoke(responseType);
        }

        @Override
        public Builder property(final String name, final Object value) {
            requestContext.setProperty(name, value);
            return this;
        }

        @Override
        public CompletionStageRxInvoker rx() {
            return new JerseyCompletionStageRxInvoker(this, executorService());
        }

        @Override
        public  T rx(Class clazz) {
            return createRxInvoker(clazz, executorService());
        }

        private  T rx(Class clazz, ExecutorService executorService) {
            if (executorService == null) {
                throw new IllegalArgumentException(LocalizationMessages.NULL_INPUT_PARAMETER("executorService"));
            }

            return createRxInvoker(clazz, executorService);
        }

        // get executor service from explicit configuration; if not available, get executor service from provider
        private ExecutorService executorService() {
            final ExecutorService result = request().getClientConfig().getExecutorService();

            if (result != null) {
                return result;
            }

            return this.requestContext.getInjectionManager()
                    .getInstance(ExecutorServiceProvider.class)
                    .getExecutorService();
        }

        /**
         * Create {@link RxInvoker} from provided {@code RxInvoker} subclass.
         * 

* The method does a lookup for {@link RxInvokerProvider}, which provides given {@code RxInvoker} subclass * and if found, calls {@link RxInvokerProvider#getRxInvoker(SyncInvoker, ExecutorService)} * * @param clazz {@code RxInvoker} subclass to be created. * @param executorService to be passed to the factory method invocation. * @param {@code RxInvoker} subclass to be returned. * @return thread safe instance of {@code RxInvoker} subclass. * @throws IllegalStateException when provider for given class is not registered. */ private T createRxInvoker(Class clazz, ExecutorService executorService) { if (clazz == null) { throw new IllegalArgumentException(LocalizationMessages.NULL_INPUT_PARAMETER("clazz")); } Iterable allProviders = Providers.getAllProviders( this.requestContext.getInjectionManager(), RxInvokerProvider.class); for (RxInvokerProvider invokerProvider : allProviders) { if (invokerProvider.isProviderFor(clazz)) { RxInvoker rxInvoker = invokerProvider.getRxInvoker(this, executorService); if (rxInvoker == null) { throw new IllegalStateException(LocalizationMessages.CLIENT_RX_PROVIDER_NULL()); } return (T) rxInvoker; } } throw new IllegalStateException( LocalizationMessages.CLIENT_RX_PROVIDER_NOT_REGISTERED(clazz.getSimpleName())); } } private static class AsyncInvoker implements javax.ws.rs.client.AsyncInvoker { private final JerseyInvocation.Builder builder; private AsyncInvoker(final JerseyInvocation.Builder request) { this.builder = request; this.builder.requestContext.setAsynchronous(true); } @Override public Future get() { return method("GET"); } @Override public Future get(final Class responseType) { return method("GET", responseType); } @Override public Future get(final GenericType responseType) { return method("GET", responseType); } @Override public Future get(final InvocationCallback callback) { return method("GET", callback); } @Override public Future put(final Entity entity) { return method("PUT", entity); } @Override public Future put(final Entity entity, final Class responseType) { return method("PUT", entity, responseType); } @Override public Future put(final Entity entity, final GenericType responseType) { return method("PUT", entity, responseType); } @Override public Future put(final Entity entity, final InvocationCallback callback) { return method("PUT", entity, callback); } @Override public Future post(final Entity entity) { return method("POST", entity); } @Override public Future post(final Entity entity, final Class responseType) { return method("POST", entity, responseType); } @Override public Future post(final Entity entity, final GenericType responseType) { return method("POST", entity, responseType); } @Override public Future post(final Entity entity, final InvocationCallback callback) { return method("POST", entity, callback); } @Override public Future delete() { return method("DELETE"); } @Override public Future delete(final Class responseType) { return method("DELETE", responseType); } @Override public Future delete(final GenericType responseType) { return method("DELETE", responseType); } @Override public Future delete(final InvocationCallback callback) { return method("DELETE", callback); } @Override public Future head() { return method("HEAD"); } @Override public Future head(final InvocationCallback callback) { return method("HEAD", callback); } @Override public Future options() { return method("OPTIONS"); } @Override public Future options(final Class responseType) { return method("OPTIONS", responseType); } @Override public Future options(final GenericType responseType) { return method("OPTIONS", responseType); } @Override public Future options(final InvocationCallback callback) { return method("OPTIONS", callback); } @Override public Future trace() { return method("TRACE"); } @Override public Future trace(final Class responseType) { return method("TRACE", responseType); } @Override public Future trace(final GenericType responseType) { return method("TRACE", responseType); } @Override public Future trace(final InvocationCallback callback) { return method("TRACE", callback); } @Override public Future method(final String name) { builder.requestContext.setMethod(name); return new JerseyInvocation(builder).submit(); } @Override public Future method(final String name, final Class responseType) { if (responseType == null) { throw new IllegalArgumentException(LocalizationMessages.RESPONSE_TYPE_IS_NULL()); } builder.requestContext.setMethod(name); return new JerseyInvocation(builder).submit(responseType); } @Override public Future method(final String name, final GenericType responseType) { if (responseType == null) { throw new IllegalArgumentException(LocalizationMessages.RESPONSE_TYPE_IS_NULL()); } builder.requestContext.setMethod(name); return new JerseyInvocation(builder).submit(responseType); } @Override public Future method(final String name, final InvocationCallback callback) { builder.requestContext.setMethod(name); return new JerseyInvocation(builder).submit(callback); } @Override public Future method(final String name, final Entity entity) { builder.requestContext.setMethod(name); builder.storeEntity(entity); return new JerseyInvocation(builder).submit(); } @Override public Future method(final String name, final Entity entity, final Class responseType) { if (responseType == null) { throw new IllegalArgumentException(LocalizationMessages.RESPONSE_TYPE_IS_NULL()); } builder.requestContext.setMethod(name); builder.storeEntity(entity); return new JerseyInvocation(builder).submit(responseType); } @Override public Future method(final String name, final Entity entity, final GenericType responseType) { if (responseType == null) { throw new IllegalArgumentException(LocalizationMessages.RESPONSE_TYPE_IS_NULL()); } builder.requestContext.setMethod(name); builder.storeEntity(entity); return new JerseyInvocation(builder).submit(responseType); } @Override public Future method(final String name, final Entity entity, final InvocationCallback callback) { builder.requestContext.setMethod(name); builder.storeEntity(entity); return new JerseyInvocation(builder).submit(callback); } } private ClientRequest requestForCall(final ClientRequest requestContext) { return copyRequestContext ? new ClientRequest(requestContext) : requestContext; } @Override public Response invoke() throws ProcessingException, WebApplicationException { final ClientRuntime runtime = request().getClientRuntime(); final RequestScope requestScope = runtime.getRequestScope(); return requestScope.runInScope( (Producer) () -> new InboundJaxrsResponse(runtime.invoke(requestForCall(requestContext)), requestScope)); } @Override public T invoke(final Class responseType) throws ProcessingException, WebApplicationException { if (responseType == null) { throw new IllegalArgumentException(LocalizationMessages.RESPONSE_TYPE_IS_NULL()); } final ClientRuntime runtime = request().getClientRuntime(); final RequestScope requestScope = runtime.getRequestScope(); //noinspection Duplicates return requestScope.runInScope(() -> { try { return translate(runtime.invoke(requestForCall(requestContext)), requestScope, responseType); } catch (final ProcessingException ex) { if (ex.getCause() instanceof WebApplicationException) { throw (WebApplicationException) ex.getCause(); } throw ex; } }); } @Override public T invoke(final GenericType responseType) throws ProcessingException, WebApplicationException { if (responseType == null) { throw new IllegalArgumentException(LocalizationMessages.RESPONSE_TYPE_IS_NULL()); } final ClientRuntime runtime = request().getClientRuntime(); final RequestScope requestScope = runtime.getRequestScope(); //noinspection Duplicates return requestScope.runInScope(() -> { try { return translate(runtime.invoke(requestForCall(requestContext)), requestScope, responseType); } catch (final ProcessingException ex) { if (ex.getCause() instanceof WebApplicationException) { throw (WebApplicationException) ex.getCause(); } throw ex; } }); } @Override public Future submit() { final CompletableFuture responseFuture = new CompletableFuture<>(); final ClientRuntime runtime = request().getClientRuntime(); runtime.submit(runtime.createRunnableForAsyncProcessing(requestForCall(requestContext), new ResponseCallback() { @Override public void completed(final ClientResponse response, final RequestScope scope) { if (!responseFuture.isCancelled()) { responseFuture.complete(new InboundJaxrsResponse(response, scope)); } else { response.close(); } } @Override public void failed(final ProcessingException error) { if (!responseFuture.isCancelled()) { responseFuture.completeExceptionally(error); } } })); return responseFuture; } @Override public Future submit(final Class responseType) { if (responseType == null) { throw new IllegalArgumentException(LocalizationMessages.RESPONSE_TYPE_IS_NULL()); } final CompletableFuture responseFuture = new CompletableFuture<>(); //noinspection Duplicates final ClientRuntime runtime = request().getClientRuntime(); runtime.submit(runtime.createRunnableForAsyncProcessing(requestForCall(requestContext), new ResponseCallback() { @Override public void completed(final ClientResponse response, final RequestScope scope) { if (responseFuture.isCancelled()) { response.close(); return; } try { responseFuture.complete(translate(response, scope, responseType)); } catch (final ProcessingException ex) { failed(ex); } } @Override public void failed(final ProcessingException error) { if (responseFuture.isCancelled()) { return; } if (error.getCause() instanceof WebApplicationException) { responseFuture.completeExceptionally(error.getCause()); } else { responseFuture.completeExceptionally(error); } } })); return responseFuture; } private T translate(final ClientResponse response, final RequestScope scope, final Class responseType) throws ProcessingException { if (responseType == Response.class) { return responseType.cast(new InboundJaxrsResponse(response, scope)); } if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) { try { return response.readEntity(responseType); } catch (final ProcessingException ex) { if (ex.getClass() == ProcessingException.class) { throw new ResponseProcessingException(new InboundJaxrsResponse(response, scope), ex.getCause()); } throw new ResponseProcessingException(new InboundJaxrsResponse(response, scope), ex); } catch (final WebApplicationException ex) { throw new ResponseProcessingException(new InboundJaxrsResponse(response, scope), ex); } catch (final Exception ex) { throw new ResponseProcessingException(new InboundJaxrsResponse(response, scope), LocalizationMessages.UNEXPECTED_ERROR_RESPONSE_PROCESSING(), ex); } } else { throw convertToException(new InboundJaxrsResponse(response, scope)); } } @Override public Future submit(final GenericType responseType) { if (responseType == null) { throw new IllegalArgumentException(LocalizationMessages.RESPONSE_TYPE_IS_NULL()); } final CompletableFuture responseFuture = new CompletableFuture<>(); //noinspection Duplicates final ClientRuntime runtime = request().getClientRuntime(); runtime.submit(runtime.createRunnableForAsyncProcessing(requestForCall(requestContext), new ResponseCallback() { @Override public void completed(final ClientResponse response, final RequestScope scope) { if (responseFuture.isCancelled()) { response.close(); return; } try { responseFuture.complete(translate(response, scope, responseType)); } catch (final ProcessingException ex) { failed(ex); } } @Override public void failed(final ProcessingException error) { if (responseFuture.isCancelled()) { return; } if (error.getCause() instanceof WebApplicationException) { responseFuture.completeExceptionally(error.getCause()); } else { responseFuture.completeExceptionally(error); } } })); return responseFuture; } private T translate(final ClientResponse response, final RequestScope scope, final GenericType responseType) throws ProcessingException { if (responseType.getRawType() == Response.class) { //noinspection unchecked return (T) new InboundJaxrsResponse(response, scope); } if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) { try { return response.readEntity(responseType); } catch (final ProcessingException ex) { throw new ResponseProcessingException( new InboundJaxrsResponse(response, scope), ex.getCause() != null ? ex.getCause() : ex); } catch (final WebApplicationException ex) { throw new ResponseProcessingException(new InboundJaxrsResponse(response, scope), ex); } catch (final Exception ex) { throw new ResponseProcessingException(new InboundJaxrsResponse(response, scope), LocalizationMessages.UNEXPECTED_ERROR_RESPONSE_PROCESSING(), ex); } } else { throw convertToException(new InboundJaxrsResponse(response, scope)); } } @Override public Future submit(final InvocationCallback callback) { return submit(null, callback); } /** * Submit the request for an asynchronous invocation and register an * {@link InvocationCallback} to process the future result of the invocation. *

* Response type in this case is taken from {@code responseType} param (if not {@code null}) rather * than from {@code callback}. This allows to pass callbacks like {@code new InvocationCallback<>() {...}}. *

* * @param response type * @param responseType response type that is used instead of obtaining types from {@code callback}. * @param callback invocation callback for asynchronous processing of the * request invocation result. * @return future response object of the specified type as a result of the * request invocation. */ public Future submit(final GenericType responseType, final InvocationCallback callback) { final CompletableFuture responseFuture = new CompletableFuture<>(); try { final ReflectionHelper.DeclaringClassInterfacePair pair = ReflectionHelper.getClass(callback.getClass(), InvocationCallback.class); final Type callbackParamType; final Class callbackParamClass; if (responseType == null) { // If we don't have response use callback to obtain param types. final Type[] typeArguments = ReflectionHelper.getParameterizedTypeArguments(pair); if (typeArguments == null || typeArguments.length == 0) { callbackParamType = Object.class; } else { callbackParamType = typeArguments[0]; } callbackParamClass = ReflectionHelper.erasure(callbackParamType); } else { callbackParamType = responseType.getType(); callbackParamClass = ReflectionHelper.erasure(responseType.getRawType()); } final ResponseCallback responseCallback = new ResponseCallback() { @Override public void completed(final ClientResponse response, final RequestScope scope) { if (responseFuture.isCancelled()) { response.close(); failed(new ProcessingException( new CancellationException(LocalizationMessages.ERROR_REQUEST_CANCELLED()))); return; } final T result; if (callbackParamClass == Response.class) { result = callbackParamClass.cast(new InboundJaxrsResponse(response, scope)); responseFuture.complete(result); callback.completed(result); } else if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) { result = response.readEntity(new GenericType(callbackParamType)); responseFuture.complete(result); callback.completed(result); } else { failed(convertToException(new InboundJaxrsResponse(response, scope))); } } @Override public void failed(final ProcessingException error) { try { if (error.getCause() instanceof WebApplicationException) { responseFuture.completeExceptionally(error.getCause()); } else if (!responseFuture.isCancelled()) { responseFuture.completeExceptionally(error); } } finally { callback.failed(error.getCause() instanceof CancellationException ? error.getCause() : error); } } }; final ClientRuntime runtime = request().getClientRuntime(); runtime.submit(runtime.createRunnableForAsyncProcessing(requestForCall(requestContext), responseCallback)); } catch (final Throwable error) { final ProcessingException ce; //noinspection ChainOfInstanceofChecks if (error instanceof ProcessingException) { ce = (ProcessingException) error; responseFuture.completeExceptionally(ce); } else if (error instanceof WebApplicationException) { ce = new ProcessingException(error); responseFuture.completeExceptionally(error); } else { ce = new ProcessingException(error); responseFuture.completeExceptionally(ce); } callback.failed(ce); } return responseFuture; } @Override public JerseyInvocation property(final String name, final Object value) { requestContext.setProperty(name, value); return this; } private ProcessingException convertToException(final Response response) { try { // Buffer and close entity input stream (if any) to prevent // leaking connections (see JERSEY-2157). response.bufferEntity(); final WebApplicationException webAppException; final int statusCode = response.getStatus(); final Response.Status status = Response.Status.fromStatusCode(statusCode); if (status == null) { final Response.Status.Family statusFamily = response.getStatusInfo().getFamily(); webAppException = createExceptionForFamily(response, statusFamily); } else { switch (status) { case BAD_REQUEST: webAppException = new BadRequestException(response); break; case UNAUTHORIZED: webAppException = new NotAuthorizedException(response); break; case FORBIDDEN: webAppException = new ForbiddenException(response); break; case NOT_FOUND: webAppException = new NotFoundException(response); break; case METHOD_NOT_ALLOWED: webAppException = new NotAllowedException(response); break; case NOT_ACCEPTABLE: webAppException = new NotAcceptableException(response); break; case UNSUPPORTED_MEDIA_TYPE: webAppException = new NotSupportedException(response); break; case INTERNAL_SERVER_ERROR: webAppException = new InternalServerErrorException(response); break; case SERVICE_UNAVAILABLE: webAppException = new ServiceUnavailableException(response); break; default: final Response.Status.Family statusFamily = response.getStatusInfo().getFamily(); webAppException = createExceptionForFamily(response, statusFamily); } } return new ResponseProcessingException(response, webAppException); } catch (final Throwable t) { return new ResponseProcessingException(response, LocalizationMessages.RESPONSE_TO_EXCEPTION_CONVERSION_FAILED(), t); } } private WebApplicationException createExceptionForFamily(final Response response, final Response.Status.Family statusFamily) { final WebApplicationException webAppException; switch (statusFamily) { case REDIRECTION: webAppException = new RedirectionException(response); break; case CLIENT_ERROR: webAppException = new ClientErrorException(response); break; case SERVER_ERROR: webAppException = new ServerErrorException(response); break; default: webAppException = new WebApplicationException(response); } return webAppException; } /** * Returns a reference to the mutable request context to be invoked. * * @return mutable request context to be invoked. */ ClientRequest request() { return requestContext; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy