/* * Copyright © 2012 The Feign Authors (feign@commonhaus.dev) * * 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 feign; import feign.InvocationHandlerFactory.MethodHandler; import feign.Logger.Level; import feign.Request.Options; import feign.Target.HardCodedTarget; import feign.codec.Decoder; import feign.codec.Encoder; import feign.codec.ErrorDecoder; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.Supplier; /** * Enhances {@link Feign} to provide support for asynchronous clients. Context (for example for * session cookies or tokens) is explicit, as calls for the same session may be done across several * threads.
*
* {@link Retryer} is not supported in this model, as that is a blocking API. {@link * ExceptionPropagationPolicy} is made redundant as {@link RetryableException} is never thrown.
* Alternative approaches to retrying can be handled through {@link AsyncClient clients}.
*
* Target interface methods must return {@link CompletableFuture} with a non-wildcard type. As the * completion is done by the {@link AsyncClient}, it is important that any subsequent processing on * the thread be short - generally, this should involve notifying some other thread of the work to * be done (for example, creating and submitting a task to an {@link ExecutorService}). */ @Experimental public final class AsyncFeign { public static AsyncBuilder builder() { return new AsyncBuilder<>(); } /** * @deprecated use {@link #builder()} instead. */ @Deprecated() public static AsyncBuilder asyncBuilder() { return builder(); } private static class LazyInitializedExecutorService { private static final ExecutorService instance = Executors.newCachedThreadPool( r -> { final Thread result = new Thread(r); result.setDaemon(true); return result; }); } public static class AsyncBuilder extends BaseBuilder, AsyncFeign> { private AsyncContextSupplier defaultContextSupplier = () -> null; private AsyncClient client = new DefaultAsyncClient<>( new DefaultClient(null, null), LazyInitializedExecutorService.instance); private MethodInfoResolver methodInfoResolver = MethodInfo::new; @Deprecated public AsyncBuilder defaultContextSupplier(Supplier supplier) { this.defaultContextSupplier = supplier::get; return this; } public AsyncBuilder client(AsyncClient client) { this.client = client; return this; } public AsyncBuilder methodInfoResolver(MethodInfoResolver methodInfoResolver) { this.methodInfoResolver = methodInfoResolver; return this; } @Override public AsyncBuilder mapAndDecode(ResponseMapper mapper, Decoder decoder) { return super.mapAndDecode(mapper, decoder); } @Override public AsyncBuilder decoder(Decoder decoder) { return super.decoder(decoder); } @Override @Deprecated public AsyncBuilder decode404() { return super.decode404(); } @Override public AsyncBuilder dismiss404() { return super.dismiss404(); } @Override public AsyncBuilder errorDecoder(ErrorDecoder errorDecoder) { return super.errorDecoder(errorDecoder); } @Override public AsyncBuilder doNotCloseAfterDecode() { return super.doNotCloseAfterDecode(); } @Override public AsyncBuilder decodeVoid() { return super.decodeVoid(); } public AsyncBuilder defaultContextSupplier(AsyncContextSupplier supplier) { this.defaultContextSupplier = supplier; return this; } public T target(Class apiType, String url) { return target(new HardCodedTarget<>(apiType, url)); } public T target(Class apiType, String url, C context) { return target(new HardCodedTarget<>(apiType, url), context); } public T target(Target target) { return build().newInstance(target); } public T target(Target target, C context) { return build().newInstance(target, context); } @Override public AsyncBuilder logLevel(Level logLevel) { return super.logLevel(logLevel); } @Override public AsyncBuilder contract(Contract contract) { return super.contract(contract); } @Override public AsyncBuilder logger(Logger logger) { return super.logger(logger); } @Override public AsyncBuilder encoder(Encoder encoder) { return super.encoder(encoder); } @Override public AsyncBuilder queryMapEncoder(QueryMapEncoder queryMapEncoder) { return super.queryMapEncoder(queryMapEncoder); } @Override public AsyncBuilder options(Options options) { return super.options(options); } @Override public AsyncBuilder requestInterceptor(RequestInterceptor requestInterceptor) { return super.requestInterceptor(requestInterceptor); } @Override public AsyncBuilder requestInterceptors(Iterable requestInterceptors) { return super.requestInterceptors(requestInterceptors); } @Override public AsyncBuilder invocationHandlerFactory( InvocationHandlerFactory invocationHandlerFactory) { return super.invocationHandlerFactory(invocationHandlerFactory); } @Override public AsyncFeign internalBuild() { AsyncResponseHandler responseHandler = (AsyncResponseHandler) Capability.enrich( new AsyncResponseHandler( logLevel, logger, decoder, errorDecoder, dismiss404, closeAfterDecode, decodeVoid, responseInterceptorChain()), AsyncResponseHandler.class, capabilities); final MethodHandler.Factory methodHandlerFactory = new AsynchronousMethodHandler.Factory<>( client, retryer, requestInterceptors, responseHandler, logger, logLevel, propagationPolicy, methodInfoResolver, new RequestTemplateFactoryResolver(encoder, queryMapEncoder), options); final ReflectiveFeign feign = new ReflectiveFeign<>( contract, methodHandlerFactory, invocationHandlerFactory, defaultContextSupplier); return new AsyncFeign<>(feign); } } private final ReflectiveFeign feign; private AsyncFeign(ReflectiveFeign feign) { this.feign = feign; } public T newInstance(Target target) { return feign.newInstance(target); } public T newInstance(Target target, C context) { return feign.newInstance(target, context); } }