package org.zalando.riptide; import com.google.common.collect.ImmutableList; import org.springframework.http.client.AsyncClientHttpRequestFactory; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor; import org.springframework.web.client.RestTemplate; import javax.annotation.Nullable; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.function.Supplier; import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkArgument; public final class RestBuilder { // package private so we can trick code coverage static class Converters { private static final ImmutableList<HttpMessageConverter<?>> DEFAULT = ImmutableList.copyOf(new RestTemplate().getMessageConverters()); } static class Plugins { private static final ImmutableList<Plugin> DEFAULT = ImmutableList.of(new OriginalStackTracePlugin()); } private static final UrlResolution DEFAULT_RESOLUTION = UrlResolution.RFC; private AsyncClientHttpRequestFactory requestFactory; private final List<HttpMessageConverter<?>> converters = new ArrayList<>(); private Supplier<URI> baseUrlProvider = () -> null; private UrlResolution resolution = DEFAULT_RESOLUTION; private final List<Plugin> plugins = new ArrayList<>(); RestBuilder() { } public RestBuilder requestFactory(final AsyncClientHttpRequestFactory requestFactory) { this.requestFactory = requestFactory; return this; } public RestBuilder defaultConverters() { return converters(Converters.DEFAULT); } public RestBuilder converters(final Iterable<HttpMessageConverter<?>> converters) { converters.forEach(this::converter); return this; } public RestBuilder converter(final HttpMessageConverter<?> converter) { this.converters.add(converter); return this; } public RestBuilder baseUrl(@Nullable final String baseUrl) { return baseUrl(baseUrl == null ? null : URI.create(baseUrl)); } public RestBuilder baseUrl(@Nullable final URI baseUrl) { checkAbsoluteBaseUrl(baseUrl); return baseUrl(() -> baseUrl); } public RestBuilder baseUrl(final Supplier<URI> baseUrlProvider) { this.baseUrlProvider = () -> checkAbsoluteBaseUrl(baseUrlProvider.get()); return this; } private URI checkAbsoluteBaseUrl(@Nullable final URI baseUrl) { checkArgument(baseUrl == null || baseUrl.isAbsolute(), "Base URL is not absolute"); return baseUrl; } public RestBuilder urlResolution(@Nullable final UrlResolution resolution) { this.resolution = firstNonNull(resolution, DEFAULT_RESOLUTION); return this; } public RestBuilder defaultPlugins() { return plugins(Plugins.DEFAULT); } public RestBuilder plugins(final Iterable<Plugin> plugins) { plugins.forEach(this::plugin); return this; } public RestBuilder plugin(final Plugin plugin) { this.plugins.add(plugin); return this; } public RestBuilder configure(final RestConfigurer configurer) { configurer.configure(this); return this; } public static RestConfigurer simpleRequestFactory(final ExecutorService executor) { return builder -> { final SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); factory.setTaskExecutor(new ConcurrentTaskExecutor(executor)); builder.requestFactory(factory); }; } public Rest build() { return new Rest(requestFactory, converters(), baseUrlProvider, resolution, plugin()); } private List<HttpMessageConverter<?>> converters() { return converters.isEmpty() ? Converters.DEFAULT : converters; } private Plugin plugin() { return plugins().stream().reduce(Plugin::merge).orElse(NoopPlugin.INSTANCE); } private List<Plugin> plugins() { return plugins.isEmpty() ? Plugins.DEFAULT : plugins; } }