From 4bb09c6fe05af26565ba8bce0772f7a4f8dc2155 Mon Sep 17 00:00:00 2001 From: Jianghao Lu Date: Thu, 9 Feb 2017 21:17:47 -0800 Subject: [PATCH] Fit and finish for RestClient.newBuilder() --- .../java/com/microsoft/rest/RestClient.java | 44 ++++- .../CustomHeadersInterceptor.java | 7 + .../microsoft/rest/retry/RetryHandler.java | 7 + .../com/microsoft/rest/RestClientTests.java | 163 ++++++++++++++++++ 4 files changed, 212 insertions(+), 9 deletions(-) create mode 100644 client-runtime/src/test/java/com/microsoft/rest/RestClientTests.java diff --git a/client-runtime/src/main/java/com/microsoft/rest/RestClient.java b/client-runtime/src/main/java/com/microsoft/rest/RestClient.java index 6a3651edc7445..79c51c6447d40 100644 --- a/client-runtime/src/main/java/com/microsoft/rest/RestClient.java +++ b/client-runtime/src/main/java/com/microsoft/rest/RestClient.java @@ -159,6 +159,8 @@ public static class Builder { private ResponseBuilder.Factory responseBuilderFactory; /** The logging interceptor to use. */ private LoggingInterceptor loggingInterceptor; + /** The strategy used for retry failed requests. */ + private RetryStrategy retryStrategy; /** * Creates an instance of the builder with a base URL to the service. @@ -168,21 +170,39 @@ public Builder() { } private Builder(RestClient restClient) { - this(); - this.withBaseUrl(restClient.retrofit.baseUrl().toString()) - .withConnectionTimeout(restClient.httpClient.connectTimeoutMillis(), TimeUnit.MILLISECONDS) - .withReadTimeout(restClient.httpClient.readTimeoutMillis(), TimeUnit.MILLISECONDS); + this(restClient.httpClient.newBuilder(), new Retrofit.Builder()); + this.httpClientBuilder.interceptors().clear(); + this.httpClientBuilder.networkInterceptors().clear(); + this.withBaseUrl(restClient.retrofit.baseUrl().toString()); + this.responseBuilderFactory = restClient.responseBuilderFactory; + this.serializerAdapter = restClient.serializerAdapter; if (restClient.credentials != null) { - this.withCredentials(restClient.credentials); + this.credentials = restClient.credentials; } if (restClient.retrofit.callbackExecutor() != null) { this.withCallbackExecutor(restClient.retrofit.callbackExecutor()); } for (Interceptor interceptor : restClient.httpClient.interceptors()) { - this.withInterceptor(interceptor); + if (interceptor instanceof UserAgentInterceptor) { + this.userAgent = ((UserAgentInterceptor) interceptor).userAgent(); + } else if (interceptor instanceof RetryHandler) { + this.retryStrategy = ((RetryHandler) interceptor).strategy(); + } else if (interceptor instanceof RequestIdHeaderInterceptor + || interceptor instanceof BaseUrlHandler) { + } else if (interceptor instanceof CustomHeadersInterceptor) { + this.customHeadersInterceptor = new CustomHeadersInterceptor(); + this.customHeadersInterceptor.addHeaderMultimap(((CustomHeadersInterceptor) interceptor).headers()); + } else { + this.withInterceptor(interceptor); + } } for (Interceptor interceptor : restClient.httpClient.networkInterceptors()) { - this.withNetworkInterceptor(interceptor); + if (interceptor instanceof LoggingInterceptor) { + LoggingInterceptor old = (LoggingInterceptor) interceptor; + this.loggingInterceptor = new LoggingInterceptor(old.logLevel()); + } else { + this.withNetworkInterceptor(interceptor); + } } } @@ -399,7 +419,7 @@ public Builder withResponseBuilderFactory(ResponseBuilder.Factory responseBuilde * @return the builder itself for chaining */ public Builder withRetryStrategy(RetryStrategy strategy) { - this.withInterceptor(new RetryHandler(strategy)); + this.retryStrategy = retryStrategy; return this; } @@ -421,12 +441,18 @@ public RestClient build() { if (responseBuilderFactory == null) { responseBuilderFactory = new ServiceResponseBuilder.Factory(); } + RetryHandler retryHandler; + if (retryStrategy == null) { + retryHandler = new RetryHandler(); + } else { + retryHandler = new RetryHandler(retryStrategy); + } OkHttpClient httpClient = httpClientBuilder .addInterceptor(userAgentInterceptor) .addInterceptor(new RequestIdHeaderInterceptor()) .addInterceptor(new BaseUrlHandler()) .addInterceptor(customHeadersInterceptor) - .addInterceptor(new RetryHandler()) + .addInterceptor(retryHandler) .addNetworkInterceptor(loggingInterceptor) .build(); return new RestClient(httpClient, diff --git a/client-runtime/src/main/java/com/microsoft/rest/interceptors/CustomHeadersInterceptor.java b/client-runtime/src/main/java/com/microsoft/rest/interceptors/CustomHeadersInterceptor.java index deddfa8d50e22..6790f5ecb7708 100644 --- a/client-runtime/src/main/java/com/microsoft/rest/interceptors/CustomHeadersInterceptor.java +++ b/client-runtime/src/main/java/com/microsoft/rest/interceptors/CustomHeadersInterceptor.java @@ -24,6 +24,13 @@ * when added to the {@link okhttp3.OkHttpClient} interceptors. */ public class CustomHeadersInterceptor implements Interceptor { + /** + * @return the currently stored custom headers + */ + public Map> headers() { + return headers; + } + /** * A mapping of custom headers. */ diff --git a/client-runtime/src/main/java/com/microsoft/rest/retry/RetryHandler.java b/client-runtime/src/main/java/com/microsoft/rest/retry/RetryHandler.java index 586673e513bd7..e8b88de75d80d 100644 --- a/client-runtime/src/main/java/com/microsoft/rest/retry/RetryHandler.java +++ b/client-runtime/src/main/java/com/microsoft/rest/retry/RetryHandler.java @@ -40,6 +40,13 @@ public class RetryHandler implements Interceptor { */ private RetryStrategy retryStrategy; + /** + * @return the strategy used by this handler + */ + public RetryStrategy strategy() { + return retryStrategy; + } + /** * Initialized an instance of {@link RetryHandler} class. * Sets default retry strategy base on Exponential Backoff. diff --git a/client-runtime/src/test/java/com/microsoft/rest/RestClientTests.java b/client-runtime/src/test/java/com/microsoft/rest/RestClientTests.java new file mode 100644 index 0000000000000..ff1273f0ec5b8 --- /dev/null +++ b/client-runtime/src/test/java/com/microsoft/rest/RestClientTests.java @@ -0,0 +1,163 @@ +/** + * + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * + */ + +package com.microsoft.rest; + +import com.microsoft.rest.credentials.BasicAuthenticationCredentials; +import com.microsoft.rest.credentials.TokenCredentials; +import com.microsoft.rest.interceptors.UserAgentInterceptor; +import com.microsoft.rest.protocol.ResponseBuilder; +import com.microsoft.rest.protocol.SerializerAdapter; +import com.microsoft.rest.serializer.JacksonAdapter; +import com.microsoft.rest.serializer.JacksonConverterFactory; +import okhttp3.Interceptor; +import okhttp3.Response; +import org.junit.Assert; +import org.junit.Test; +import retrofit2.Converter; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class RestClientTests { + @Test + public void defaultConfigs() { + RestClient restClient = new RestClient.Builder().build(); + Assert.assertEquals("https://management.azure.com/", restClient.retrofit().baseUrl().toString()); + Assert.assertEquals(LogLevel.NONE, restClient.logLevel()); + Assert.assertTrue(restClient.responseBuilderFactory() instanceof ServiceResponseBuilder.Factory); + Assert.assertTrue(restClient.serializerAdapter() instanceof JacksonAdapter); + Assert.assertNull(restClient.credentials()); + } + + @Test + public void newBuilderKeepsConfigs() { + RestClient restClient = new RestClient.Builder() + .withBaseUrl("http://localhost") + .withCredentials(new TokenCredentials("Bearer", "token")) + .withLogLevel(LogLevel.BASIC) + .withInterceptor(new Interceptor() { + @Override + public Response intercept(Chain chain) throws IOException { + return chain.proceed(chain.request()); + } + }) + .withUserAgent("user") + .withNetworkInterceptor(new Interceptor() { + @Override + public Response intercept(Chain chain) throws IOException { + return chain.proceed(chain.request()); + } + }) + .withConnectionTimeout(100, TimeUnit.MINUTES) + .build(); + RestClient newClient = restClient.newBuilder().build(); + Assert.assertEquals(restClient.retrofit().baseUrl().toString(), newClient.retrofit().baseUrl().toString()); + Assert.assertEquals(restClient.logLevel(), newClient.logLevel()); + Assert.assertEquals(restClient.logLevel().isPrettyJson(), newClient.logLevel().isPrettyJson()); + Assert.assertEquals(restClient.serializerAdapter(), newClient.serializerAdapter()); + Assert.assertEquals(restClient.responseBuilderFactory(), newClient.responseBuilderFactory()); + Assert.assertEquals(restClient.credentials(), newClient.credentials()); + for (Interceptor interceptor : + newClient.httpClient().interceptors()) { + if (interceptor instanceof UserAgentInterceptor) { + Assert.assertEquals("user", ((UserAgentInterceptor) interceptor).userAgent()); + } + } + Assert.assertEquals(restClient.httpClient().interceptors().size(), newClient.httpClient().interceptors().size()); + Assert.assertEquals(restClient.httpClient().networkInterceptors().size(), newClient.httpClient().networkInterceptors().size()); + Assert.assertEquals(TimeUnit.MINUTES.toMillis(100), newClient.httpClient().connectTimeoutMillis()); + } + + @Test + public void newBuilderClonesProperties() { + RestClient restClient = new RestClient.Builder() + .withBaseUrl("http://localhost") + .withCredentials(new TokenCredentials("Bearer", "token")) + .withLogLevel(LogLevel.BASIC.withPrettyJson(true)) + .withInterceptor(new Interceptor() { + @Override + public Response intercept(Chain chain) throws IOException { + return chain.proceed(chain.request()); + } + }) + .withUserAgent("user") + .withNetworkInterceptor(new Interceptor() { + @Override + public Response intercept(Chain chain) throws IOException { + return chain.proceed(chain.request()); + } + }) + .withConnectionTimeout(100, TimeUnit.MINUTES) + .build(); + RestClient newClient = restClient.newBuilder() + .withBaseUrl("https://contoso.com") + .withCredentials(new BasicAuthenticationCredentials("user", "pass")) + .withLogLevel(LogLevel.BODY_AND_HEADERS) + .withUserAgent("anotheruser") + .withConnectionTimeout(200, TimeUnit.SECONDS) + .withSerializerAdapter(new SerializerAdapter() { + @Override + public Object serializer() { + return null; + } + + @Override + public Converter.Factory converterFactory() { + return JacksonConverterFactory.create(); + } + + @Override + public String serialize(Object object) throws IOException { + return null; + } + + @Override + public String serializeRaw(Object object) { + return null; + } + + @Override + public String serializeList(List list, CollectionFormat format) { + return null; + } + + @Override + public U deserialize(String value, Type type) throws IOException { + return null; + } + }) + .withResponseBuilderFactory(new ResponseBuilder.Factory() { + @Override + public ResponseBuilder newInstance(SerializerAdapter serializerAdapter) { + return null; + } + }) + .build(); + Assert.assertNotEquals(restClient.retrofit().baseUrl().toString(), newClient.retrofit().baseUrl().toString()); + Assert.assertNotEquals(restClient.logLevel(), newClient.logLevel()); + Assert.assertNotEquals(restClient.logLevel().isPrettyJson(), newClient.logLevel().isPrettyJson()); + Assert.assertNotEquals(restClient.serializerAdapter(), newClient.serializerAdapter()); + Assert.assertNotEquals(restClient.responseBuilderFactory(), newClient.responseBuilderFactory()); + Assert.assertNotEquals(restClient.credentials(), newClient.credentials()); + for (Interceptor interceptor : + restClient.httpClient().interceptors()) { + if (interceptor instanceof UserAgentInterceptor) { + Assert.assertEquals("user", ((UserAgentInterceptor) interceptor).userAgent()); + } + } + for (Interceptor interceptor : + newClient.httpClient().interceptors()) { + if (interceptor instanceof UserAgentInterceptor) { + Assert.assertEquals("anotheruser", ((UserAgentInterceptor) interceptor).userAgent()); + } + } + Assert.assertNotEquals(restClient.httpClient().connectTimeoutMillis(), newClient.httpClient().connectTimeoutMillis()); + } +}