/*
* 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);
}
}