diff --git a/azure-client-runtime/src/main/java/com/microsoft/azure/AzureClient.java b/azure-client-runtime/src/main/java/com/microsoft/azure/AzureClient.java index 2f3569fc51c59..36237bf806893 100644 --- a/azure-client-runtime/src/main/java/com/microsoft/azure/AzureClient.java +++ b/azure-client-runtime/src/main/java/com/microsoft/azure/AzureClient.java @@ -7,6 +7,7 @@ package com.microsoft.azure; +import com.microsoft.rest.RestClient; import com.microsoft.rest.ServiceCall; import com.microsoft.rest.ServiceCallback; import com.microsoft.rest.ServiceException; @@ -14,7 +15,6 @@ import com.microsoft.rest.ServiceResponseCallback; import com.microsoft.rest.ServiceResponseWithHeaders; import com.microsoft.rest.credentials.ServiceClientCredentials; -import com.microsoft.rest.serializer.JacksonMapperAdapter; import java.io.IOException; import java.lang.reflect.Type; @@ -24,11 +24,9 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import okhttp3.OkHttpClient; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Response; -import retrofit2.Retrofit; import retrofit2.http.GET; import retrofit2.http.Url; @@ -42,32 +40,18 @@ public class AzureClient extends AzureServiceClient { * used if null. */ private Integer longRunningOperationRetryTimeout; - /** - * The credentials to use for authentication for long running operations. - */ - private ServiceClientCredentials credentials; /** * The executor for asynchronous requests. */ private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); - /** - * Initializes an instance of this class. - */ - public AzureClient() { - super(); - } - /** * Initializes an instance of this class with customized client metadata. * - * @param clientBuilder customized http client. - * @param retrofitBuilder customized retrofit builder - * @param mapperAdapter the adapter for the Jackson object mapper + * @param restClient the REST client to connect to Azure */ - public AzureClient(OkHttpClient.Builder clientBuilder, Retrofit.Builder retrofitBuilder, JacksonMapperAdapter mapperAdapter) { - super(clientBuilder, retrofitBuilder); - this.mapperAdapter = mapperAdapter; + public AzureClient(RestClient restClient) { + super(restClient); } /** @@ -98,13 +82,13 @@ public ServiceResponse getPutOrPatchResult(Response respons CloudException exception = new CloudException(statusCode + " is not a valid polling status code"); exception.setResponse(response); if (responseBody != null) { - exception.setBody((CloudError) mapperAdapter.deserialize(responseBody.string(), CloudError.class)); + exception.setBody((CloudError) restClient().mapperAdapter().deserialize(responseBody.string(), CloudError.class)); responseBody.close(); } throw exception; } - PollingState pollingState = new PollingState<>(response, this.getLongRunningOperationRetryTimeout(), resourceType, mapperAdapter); + PollingState pollingState = new PollingState<>(response, this.getLongRunningOperationRetryTimeout(), resourceType, restClient().mapperAdapter()); String url = response.raw().request().url().toString(); // Check provisioning state @@ -151,7 +135,7 @@ public ServiceResponseWithHeaders getPutOrPatchResultWi ServiceResponse bodyResponse = getPutOrPatchResult(response, resourceType); return new ServiceResponseWithHeaders<>( bodyResponse.getBody(), - mapperAdapter.deserialize(mapperAdapter.serialize(bodyResponse.getResponse().headers()), headerType), + restClient().mapperAdapter().deserialize(restClient().mapperAdapter().serialize(bodyResponse.getResponse().headers()), headerType), bodyResponse.getResponse() ); } @@ -186,7 +170,7 @@ public AsyncPollingTask getPutOrPatchResultAsync(Response r exception.setResponse(response); try { if (responseBody != null) { - exception.setBody((CloudError) mapperAdapter.deserialize(responseBody.string(), CloudError.class)); + exception.setBody((CloudError) restClient().mapperAdapter().deserialize(responseBody.string(), CloudError.class)); responseBody.close(); } } catch (Exception e) { /* ignore serialization errors on top of service errors */ } @@ -196,7 +180,7 @@ public AsyncPollingTask getPutOrPatchResultAsync(Response r PollingState pollingState; try { - pollingState = new PollingState<>(response, this.getLongRunningOperationRetryTimeout(), resourceType, mapperAdapter); + pollingState = new PollingState<>(response, this.getLongRunningOperationRetryTimeout(), resourceType, restClient().mapperAdapter()); } catch (IOException e) { callback.failure(e); return null; @@ -235,7 +219,7 @@ public void success(ServiceResponse result) { try { callback.success(new ServiceResponseWithHeaders<>( result.getBody(), - mapperAdapter.deserialize(mapperAdapter.serialize(result.getResponse().headers()), headerType), + restClient().mapperAdapter().deserialize(restClient().mapperAdapter().serialize(result.getResponse().headers()), headerType), result.getResponse() )); } catch (IOException e) { @@ -273,13 +257,13 @@ public ServiceResponse getPostOrDeleteResult(Response respo CloudException exception = new CloudException(statusCode + " is not a valid polling status code"); exception.setResponse(response); if (responseBody != null) { - exception.setBody((CloudError) mapperAdapter.deserialize(responseBody.string(), CloudError.class)); + exception.setBody((CloudError) restClient().mapperAdapter().deserialize(responseBody.string(), CloudError.class)); responseBody.close(); } throw exception; } - PollingState pollingState = new PollingState<>(response, this.getLongRunningOperationRetryTimeout(), resourceType, mapperAdapter); + PollingState pollingState = new PollingState<>(response, this.getLongRunningOperationRetryTimeout(), resourceType, restClient().mapperAdapter()); // Check provisioning state while (!AzureAsyncOperation.getTerminalStatuses().contains(pollingState.getStatus())) { @@ -325,7 +309,7 @@ public ServiceResponseWithHeaders getPostOrDeleteResult ServiceResponse bodyResponse = getPostOrDeleteResult(response, resourceType); return new ServiceResponseWithHeaders<>( bodyResponse.getBody(), - mapperAdapter.deserialize(mapperAdapter.serialize(bodyResponse.getResponse().headers()), headerType), + restClient().mapperAdapter().deserialize(restClient().mapperAdapter().serialize(bodyResponse.getResponse().headers()), headerType), bodyResponse.getResponse() ); } @@ -360,7 +344,7 @@ public AsyncPollingTask getPostOrDeleteResultAsync(Response exception.setResponse(response); try { if (responseBody != null) { - exception.setBody((CloudError) mapperAdapter.deserialize(responseBody.string(), CloudError.class)); + exception.setBody((CloudError) restClient().mapperAdapter().deserialize(responseBody.string(), CloudError.class)); responseBody.close(); } } catch (Exception e) { /* ignore serialization errors on top of service errors */ } @@ -370,7 +354,7 @@ public AsyncPollingTask getPostOrDeleteResultAsync(Response PollingState pollingState; try { - pollingState = new PollingState<>(response, this.getLongRunningOperationRetryTimeout(), resourceType, mapperAdapter); + pollingState = new PollingState<>(response, this.getLongRunningOperationRetryTimeout(), resourceType, restClient().mapperAdapter()); } catch (IOException e) { callback.failure(e); return null; @@ -408,7 +392,7 @@ public void success(ServiceResponse result) { try { callback.success(new ServiceResponseWithHeaders<>( result.getBody(), - mapperAdapter.deserialize(mapperAdapter.serialize(result.getResponse().headers()), headerType), + restClient().mapperAdapter().deserialize(restClient().mapperAdapter().serialize(result.getResponse().headers()), headerType), result.getResponse() )); } catch (IOException e) { @@ -584,7 +568,7 @@ private void updateStateFromAzureAsyncOperationHeader(PollingState pollin AzureAsyncOperation body = null; if (response.body() != null) { - body = mapperAdapter.deserialize(response.body().string(), AzureAsyncOperation.class); + body = restClient().mapperAdapter().deserialize(response.body().string(), AzureAsyncOperation.class); response.body().close(); } @@ -592,7 +576,7 @@ private void updateStateFromAzureAsyncOperationHeader(PollingState pollin CloudException exception = new CloudException("no body"); exception.setResponse(response); if (response.errorBody() != null) { - exception.setBody((CloudError) mapperAdapter.deserialize(response.errorBody().string(), CloudError.class)); + exception.setBody((CloudError) restClient().mapperAdapter().deserialize(response.errorBody().string(), CloudError.class)); response.errorBody().close(); } throw exception; @@ -624,14 +608,14 @@ public void success(ServiceResponse result) { try { AzureAsyncOperation body = null; if (result.getBody() != null) { - body = mapperAdapter.deserialize(result.getBody().string(), AzureAsyncOperation.class); + body = restClient().mapperAdapter().deserialize(result.getBody().string(), AzureAsyncOperation.class); result.getBody().close(); } if (body == null || body.getStatus() == null) { CloudException exception = new CloudException("no body"); exception.setResponse(result.getResponse()); if (result.getResponse().errorBody() != null) { - exception.setBody((CloudError) mapperAdapter.deserialize(result.getResponse().errorBody().string(), CloudError.class)); + exception.setBody((CloudError) restClient().mapperAdapter().deserialize(result.getResponse().errorBody().string(), CloudError.class)); result.getResponse().errorBody().close(); } failure(exception); @@ -663,18 +647,17 @@ private Response poll(String url) throws CloudException, IOExcepti if (port == -1) { port = endpoint.getDefaultPort(); } - AsyncService service = this.retrofitBuilder - .baseUrl(endpoint.getProtocol() + "://" + endpoint.getHost() + ":" + port).build().create(AsyncService.class); + AsyncService service = restClient().retrofit().create(AsyncService.class); Response response = service.get(endpoint.getFile()).execute(); int statusCode = response.code(); if (statusCode != 200 && statusCode != 201 && statusCode != 202 && statusCode != 204) { CloudException exception = new CloudException(statusCode + " is not a valid polling status code"); exception.setResponse(response); if (response.body() != null) { - exception.setBody((CloudError) mapperAdapter.deserialize(response.body().string(), CloudError.class)); + exception.setBody((CloudError) restClient().mapperAdapter().deserialize(response.body().string(), CloudError.class)); response.body().close(); } else if (response.errorBody() != null) { - exception.setBody((CloudError) mapperAdapter.deserialize(response.errorBody().string(), CloudError.class)); + exception.setBody((CloudError) restClient().mapperAdapter().deserialize(response.errorBody().string(), CloudError.class)); response.errorBody().close(); } throw exception; @@ -701,8 +684,7 @@ private Call pollAsync(String url, final ServiceCallback call = service.get(endpoint.getFile()); call.enqueue(new ServiceResponseCallback(callback) { @Override @@ -713,10 +695,10 @@ public void onResponse(Call call, Response response) CloudException exception = new CloudException(statusCode + " is not a valid polling status code"); exception.setResponse(response); if (response.body() != null) { - exception.setBody((CloudError) mapperAdapter.deserialize(response.body().string(), CloudError.class)); + exception.setBody((CloudError) restClient().mapperAdapter().deserialize(response.body().string(), CloudError.class)); response.body().close(); } else if (response.errorBody() != null) { - exception.setBody((CloudError) mapperAdapter.deserialize(response.errorBody().string(), CloudError.class)); + exception.setBody((CloudError) restClient().mapperAdapter().deserialize(response.errorBody().string(), CloudError.class)); response.errorBody().close(); } callback.failure(exception); @@ -749,24 +731,6 @@ public void setLongRunningOperationRetryTimeout(Integer longRunningOperationRetr this.longRunningOperationRetryTimeout = longRunningOperationRetryTimeout; } - /** - * Gets the credentials used for authentication. - * - * @return the credentials. - */ - public ServiceClientCredentials getCredentials() { - return credentials; - } - - /** - * Sets the credentials used for authentication. - * - * @param credentials the credentials. - */ - public void setCredentials(ServiceClientCredentials credentials) { - this.credentials = credentials; - } - /** * The Retrofit service used for polling. */ diff --git a/azure-client-runtime/src/main/java/com/microsoft/azure/AzureServiceClient.java b/azure-client-runtime/src/main/java/com/microsoft/azure/AzureServiceClient.java index 5feea13fa74d8..666cdc982dbb1 100644 --- a/azure-client-runtime/src/main/java/com/microsoft/azure/AzureServiceClient.java +++ b/azure-client-runtime/src/main/java/com/microsoft/azure/AzureServiceClient.java @@ -8,9 +8,11 @@ package com.microsoft.azure; import com.microsoft.azure.serializer.AzureJacksonMapperAdapter; +import com.microsoft.rest.RestClient; import com.microsoft.rest.ServiceClient; import com.microsoft.rest.UserAgentInterceptor; import com.microsoft.rest.retry.RetryHandler; +import com.microsoft.rest.serializer.JacksonMapperAdapter; import java.net.CookieManager; import java.net.CookiePolicy; @@ -23,42 +25,17 @@ * ServiceClient is the abstraction for accessing REST operations and their payload data types. */ public abstract class AzureServiceClient extends ServiceClient { - /** - * Initializes a new instance of the ServiceClient class. - */ - protected AzureServiceClient() { - super(); + protected AzureServiceClient(String baseUrl) { + this(new RestClient.Builder(baseUrl) + .withMapperAdapter(new AzureJacksonMapperAdapter()).build()); } /** * Initializes a new instance of the ServiceClient class. * - * @param clientBuilder the builder to build up an OkHttp client - * @param retrofitBuilder the builder to build up a rest adapter - */ - protected AzureServiceClient(OkHttpClient.Builder clientBuilder, Retrofit.Builder retrofitBuilder) { - super(clientBuilder, retrofitBuilder); - } - - /** - * This method initializes the builders for Http client and Retrofit with common - * behaviors for all service clients. + * @param restClient the REST client */ - @Override - protected void initialize() { - // Add retry handler - CookieManager cookieManager = new CookieManager(); - cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL); - - // Set up OkHttp client - this.clientBuilder = clientBuilder - .cookieJar(new JavaNetCookieJar(cookieManager)) - .addInterceptor(new RetryHandler()) - .addInterceptor(new UserAgentInterceptor()); - // Set up rest adapter - this.mapperAdapter = new AzureJacksonMapperAdapter(); - this.retrofitBuilder = retrofitBuilder - .client(clientBuilder.build()) - .addConverterFactory(mapperAdapter.getConverterFactory()); + protected AzureServiceClient(RestClient restClient) { + super(restClient); } } diff --git a/client-runtime/src/main/java/com/microsoft/rest/BaseUrlInterceptor.java b/client-runtime/src/main/java/com/microsoft/rest/BaseUrlInterceptor.java new file mode 100644 index 0000000000000..e715cfe085384 --- /dev/null +++ b/client-runtime/src/main/java/com/microsoft/rest/BaseUrlInterceptor.java @@ -0,0 +1,45 @@ +/** + * + * 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 java.io.IOException; + +import okhttp3.HttpUrl; +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.internal.Version; +import retrofit2.BaseUrl; + +/** + * User agent interceptor for putting a 'User-Agent' header in the request. + */ +public class BaseUrlInterceptor implements Interceptor { + private String baseUrl; + + public BaseUrlInterceptor() { + } + + public void setBaseUrl(String baseUrl) { + this.baseUrl = baseUrl; + } + + @Override + public Response intercept(Chain chain) throws IOException { + Request request = chain.request(); + if (baseUrl != null) { + HttpUrl newUrl = request.url().newBuilder() + .host(baseUrl) + .build(); + request = request.newBuilder() + .url(newUrl) + .build(); + } + return chain.proceed(request); + } +} diff --git a/client-runtime/src/main/java/com/microsoft/rest/CustomHeadersInterceptor.java b/client-runtime/src/main/java/com/microsoft/rest/CustomHeadersInterceptor.java new file mode 100644 index 0000000000000..b49da885c2cd6 --- /dev/null +++ b/client-runtime/src/main/java/com/microsoft/rest/CustomHeadersInterceptor.java @@ -0,0 +1,135 @@ +/** + * + * 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 okhttp3.Headers; +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * An instance of this class enables adding custom headers in client requests + * when added to the {@link okhttp3.OkHttpClient} interceptors. + */ +public class CustomHeadersInterceptor implements Interceptor { + /** + * A mapping of custom headers. + */ + private Map> headers; + + /** + * Initialize an instance of {@link CustomHeadersInterceptor} class. + */ + public CustomHeadersInterceptor() { + headers = new HashMap>(); + } + + /** + * Initialize an instance of {@link CustomHeadersInterceptor} class. + * + * @param key the key for the header + * @param value the value of the header + */ + public CustomHeadersInterceptor(String key, String value) { + this(); + addHeader(key, value); + } + + /** + * Add a single header key-value pair. If one with the name already exists, + * it gets replaced. + * + * @param name the name of the header. + * @param value the value of the header. + * @return the interceptor instance itself. + */ + public CustomHeadersInterceptor replaceHeader(String name, String value) { + this.headers.put(name, Collections.singletonList(value)); + return this; + } + + /** + * Add a single header key-value pair. If one with the name already exists, + * both stay in the header map. + * + * @param name the name of the header. + * @param value the value of the header. + * @return the interceptor instance itself. + */ + public CustomHeadersInterceptor addHeader(String name, String value) { + if (this.headers.containsKey(name)) { + this.headers.get(name).add(value); + } else { + this.headers.put(name, Collections.singletonList(value)); + } + return this; + } + + /** + * Add all headers in a {@link Headers} object. + * + * @param headers an OkHttp {@link Headers} object. + * @return the interceptor instance itself. + */ + public CustomHeadersInterceptor addHeaders(Headers headers) { + this.headers.putAll(headers.toMultimap()); + return this; + } + + /** + * Add all headers in a header map. + * + * @param headers a map of headers. + * @return the interceptor instance itself. + */ + public CustomHeadersInterceptor addHeaderMap(Map headers) { + for (Map.Entry header : headers.entrySet()) { + this.headers.put(header.getKey(), Collections.singletonList(header.getValue())); + } + return this; + } + + /** + * Add all headers in a header multimap. + * + * @param headers a multimap of headers. + * @return the interceptor instance itself. + */ + public CustomHeadersInterceptor addHeaderMultimap(Map> headers) { + this.headers.putAll(headers); + return this; + } + + /** + * Remove a header. + * + * @param name the name of the header to remove. + * @return the interceptor instance itself. + */ + public CustomHeadersInterceptor removeHeader(String name) { + this.headers.remove(name); + return this; + } + + @Override + public Response intercept(Chain chain) throws IOException { + Request.Builder builder = chain.request().newBuilder(); + for (Map.Entry> header : headers.entrySet()) { + for (String value : header.getValue()) { + builder = builder.header(header.getKey(), value); + } + } + return chain.proceed(builder.build()); + } +} diff --git a/client-runtime/src/main/java/com/microsoft/rest/RestClient.java b/client-runtime/src/main/java/com/microsoft/rest/RestClient.java new file mode 100644 index 0000000000000..4342a513e2e0b --- /dev/null +++ b/client-runtime/src/main/java/com/microsoft/rest/RestClient.java @@ -0,0 +1,153 @@ +/** + * + * 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.ServiceClientCredentials; +import com.microsoft.rest.retry.RetryHandler; +import com.microsoft.rest.serializer.JacksonMapperAdapter; + +import java.net.CookieManager; +import java.net.CookiePolicy; + +import okhttp3.Interceptor; +import okhttp3.JavaNetCookieJar; +import okhttp3.OkHttpClient; +import retrofit2.Retrofit; + +/** + * An instance of this class stores the client information for making REST calls. + */ +public class RestClient { + private OkHttpClient httpClient; + private Retrofit retrofit; + private ServiceClientCredentials credentials; + private CustomHeadersInterceptor customHeadersInterceptor; + private BaseUrlInterceptor baseUrlInterceptor; + private JacksonMapperAdapter mapperAdapter; + + private RestClient(OkHttpClient httpClient, + Retrofit retrofit, + ServiceClientCredentials credentials, + CustomHeadersInterceptor customHeadersInterceptor, + BaseUrlInterceptor baseUrlInterceptor, + JacksonMapperAdapter mapperAdapter) { + this.httpClient = httpClient; + this.retrofit = retrofit; + this.credentials = credentials; + this.customHeadersInterceptor = customHeadersInterceptor; + this.baseUrlInterceptor = baseUrlInterceptor; + this.mapperAdapter = mapperAdapter; + } + + public CustomHeadersInterceptor headers() { + return customHeadersInterceptor; + } + + public JacksonMapperAdapter mapperAdapter() { + return mapperAdapter; + } + + public OkHttpClient httpClient() { + return httpClient; + } + + public Retrofit retrofit() { + return retrofit; + } + + public String baseUrl() { + return retrofit.baseUrl().url().toString(); + } + + public void baseUrl(String baseUrl) { + baseUrlInterceptor.setBaseUrl(baseUrl); + } + + public ServiceClientCredentials credentials() { + return this.credentials; + } + + public static class Builder { + private OkHttpClient.Builder httpClientBuilder; + private Retrofit.Builder retrofitBuilder; + private ServiceClientCredentials credentials; + private CustomHeadersInterceptor customHeadersInterceptor; + private BaseUrlInterceptor baseUrlInterceptor; + private JacksonMapperAdapter mapperAdapter; + + public Builder(String baseUrl) { + this(baseUrl, new OkHttpClient.Builder(), new Retrofit.Builder()); + } + + public Builder(String baseUrl, OkHttpClient.Builder httpClientBuilder, Retrofit.Builder retrofitBuilder) { + if (baseUrl == null) { + throw new IllegalArgumentException("baseUrl == null"); + } + if (httpClientBuilder == null) { + throw new IllegalArgumentException("httpClientBuilder == null"); + } + if (retrofitBuilder == null) { + throw new IllegalArgumentException("retrofitBuilder == null"); + } + CookieManager cookieManager = new CookieManager(); + cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL); + customHeadersInterceptor = new CustomHeadersInterceptor(); + baseUrlInterceptor = new BaseUrlInterceptor(); + // Set up OkHttp client + this.httpClientBuilder = httpClientBuilder + .cookieJar(new JavaNetCookieJar(cookieManager)) + .addInterceptor(new RetryHandler()) + .addInterceptor(new UserAgentInterceptor()) + .addInterceptor(customHeadersInterceptor) + .addInterceptor(baseUrlInterceptor); + // Set up rest adapter + this.retrofitBuilder = retrofitBuilder.baseUrl(baseUrl); + } + + public Builder withBaseUrl(String baseUrl) { + this.retrofitBuilder.baseUrl(baseUrl); + return this; + } + + public Builder withUserAgent(String userAgent) { + this.httpClientBuilder.addInterceptor(new UserAgentInterceptor(userAgent)); + return this; + } + + public Builder withMapperAdapter(JacksonMapperAdapter mapperAdapter) { + if (mapperAdapter != null) { + this.mapperAdapter = mapperAdapter; + this.retrofitBuilder = retrofitBuilder.addConverterFactory(mapperAdapter.getConverterFactory()); + } + return this; + } + + public Builder withCredentials(ServiceClientCredentials credentials) { + this.credentials = credentials; + if (credentials != null) { + credentials.applyCredentialsFilter(httpClientBuilder); + } + return this; + } + + public Builder withInterceptor(Interceptor interceptor) { + this.httpClientBuilder.addInterceptor(interceptor); + return this; + } + + public RestClient build() { + OkHttpClient httpClient = httpClientBuilder.build(); + return new RestClient(httpClient, + retrofitBuilder.client(httpClient).build(), + credentials, + customHeadersInterceptor, + baseUrlInterceptor, + mapperAdapter); + } + } +} diff --git a/client-runtime/src/main/java/com/microsoft/rest/ServiceClient.java b/client-runtime/src/main/java/com/microsoft/rest/ServiceClient.java index 5c7a8cd89b28c..ef2f8cae9cfec 100644 --- a/client-runtime/src/main/java/com/microsoft/rest/ServiceClient.java +++ b/client-runtime/src/main/java/com/microsoft/rest/ServiceClient.java @@ -7,20 +7,8 @@ package com.microsoft.rest; -import com.microsoft.rest.retry.RetryHandler; import com.microsoft.rest.serializer.JacksonMapperAdapter; -import java.net.CookieManager; -import java.net.CookiePolicy; -import java.util.List; - -import okhttp3.Interceptor; -import okhttp3.JavaNetCookieJar; -import okhttp3.OkHttpClient; -import okhttp3.logging.HttpLoggingInterceptor; -import okhttp3.logging.HttpLoggingInterceptor.Level; -import retrofit2.Retrofit; - /** * ServiceClient is the abstraction for accessing REST operations and their payload data types. */ @@ -28,89 +16,33 @@ public abstract class ServiceClient { /** * The builder for building the OkHttp client. */ - protected OkHttpClient.Builder clientBuilder; - - /** - * The builder for building Retrofit services. - */ - protected Retrofit.Builder retrofitBuilder; - - /** - * The OkHttp client. - */ - protected OkHttpClient httpClient; - - /** - * The rest adapter. - */ - protected Retrofit retrofit; - - /** - * The adapter for {@link com.fasterxml.jackson.databind.ObjectMapper} for serialization - * and deserialization operations. - */ - protected JacksonMapperAdapter mapperAdapter; + private RestClient restClient; /** * Initializes a new instance of the ServiceClient class. */ - protected ServiceClient() { - this(new OkHttpClient.Builder(), new Retrofit.Builder()); + protected ServiceClient(String baseUrl) { + this(new RestClient.Builder(baseUrl) + .withMapperAdapter(new JacksonMapperAdapter()).build()); } /** * Initializes a new instance of the ServiceClient class. * - * @param clientBuilder the builder to build up an OkHttp client - * @param retrofitBuilder the builder to build up a rest adapter + * @param restClient the builder to build up an REST client */ - protected ServiceClient(OkHttpClient.Builder clientBuilder, Retrofit.Builder retrofitBuilder) { - if (clientBuilder == null) { - throw new IllegalArgumentException("clientBuilder == null"); - } - if (retrofitBuilder == null) { - throw new IllegalArgumentException("retrofitBuilder == null"); + protected ServiceClient(RestClient restClient) { + if (restClient == null) { + throw new IllegalArgumentException("restClient == null"); } - - this.clientBuilder = clientBuilder; - this.retrofitBuilder = retrofitBuilder; + this.restClient = restClient; } /** * Get the list of interceptors the OkHttp client will execute. * @return the list of interceptors */ - public List getClientInterceptors() { - return this.clientBuilder.interceptors(); - } - - /** - * Gets the adapter for {@link com.fasterxml.jackson.databind.ObjectMapper} for serialization - * and deserialization operations.. - * - * @return the adapter. - */ - public JacksonMapperAdapter getMapperAdapter() { - return this.mapperAdapter; - } - - /** - * This method initializes the builders for Http client and Retrofit with common - * behaviors for all service clients. - */ - protected void initialize() { - // Add retry handler - CookieManager cookieManager = new CookieManager(); - cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL); - - // Set up OkHttp client - this.clientBuilder = clientBuilder - .cookieJar(new JavaNetCookieJar(cookieManager)) - .addInterceptor(new RetryHandler()) - .addInterceptor(new UserAgentInterceptor()); - // Set up rest adapter - this.mapperAdapter = new JacksonMapperAdapter(); - this.retrofitBuilder = retrofitBuilder - .addConverterFactory(mapperAdapter.getConverterFactory()); + public RestClient restClient() { + return this.restClient; } } diff --git a/client-runtime/src/test/java/com/microsoft/rest/CredentialsTests.java b/client-runtime/src/test/java/com/microsoft/rest/CredentialsTests.java index d6bfbf3e148e9..19b7a8ca2a4f5 100644 --- a/client-runtime/src/test/java/com/microsoft/rest/CredentialsTests.java +++ b/client-runtime/src/test/java/com/microsoft/rest/CredentialsTests.java @@ -9,23 +9,28 @@ import com.microsoft.rest.credentials.BasicAuthenticationCredentials; import com.microsoft.rest.credentials.TokenCredentials; -import okhttp3.Interceptor; -import okhttp3.OkHttpClient; -import okhttp3.Protocol; -import okhttp3.Response; -import retrofit2.Retrofit; import org.junit.Assert; import org.junit.Test; import java.io.IOException; +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; +import retrofit2.Retrofit; + public class CredentialsTests { @Test public void basicCredentialsTest() throws Exception { OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); Retrofit.Builder retrofitBuilder = new Retrofit.Builder(); - clientBuilder.addInterceptor(new Interceptor() { + BasicAuthenticationCredentials credentials = new BasicAuthenticationCredentials("user", "pass"); + RestClient.Builder restBuilder = new RestClient.Builder("http://localhost", clientBuilder, retrofitBuilder) + .withCredentials(credentials) + .withInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { String header = chain.request().header("Authorization"); @@ -37,29 +42,32 @@ public Response intercept(Chain chain) throws IOException { .build(); } }); - ServiceClient serviceClient = new ServiceClient(clientBuilder, retrofitBuilder) { }; - BasicAuthenticationCredentials credentials = new BasicAuthenticationCredentials("user", "pass"); - credentials.applyCredentialsFilter(serviceClient.clientBuilder); + ServiceClient serviceClient = new ServiceClient(restBuilder.build()) { }; + Response response = serviceClient.restClient().httpClient().newCall(new Request.Builder().url("http://localhost").build()).execute(); + Assert.assertEquals(200, response.code()); } @Test public void tokenCredentialsTest() throws Exception { OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); Retrofit.Builder retrofitBuilder = new Retrofit.Builder(); - clientBuilder.addInterceptor(new Interceptor() { - @Override - public Response intercept(Chain chain) throws IOException { - String header = chain.request().header("Authorization"); - Assert.assertEquals("Bearer this_is_a_token", header); - return new Response.Builder() - .request(chain.request()) - .code(200) - .protocol(Protocol.HTTP_1_1) - .build(); - } - }); - ServiceClient serviceClient = new ServiceClient(clientBuilder, retrofitBuilder) { }; TokenCredentials credentials = new TokenCredentials(null, "this_is_a_token"); - credentials.applyCredentialsFilter(serviceClient.clientBuilder); + RestClient.Builder restBuilder = new RestClient.Builder("http://localhost", clientBuilder, retrofitBuilder) + .withCredentials(credentials) + .withInterceptor(new Interceptor() { + @Override + public Response intercept(Chain chain) throws IOException { + String header = chain.request().header("Authorization"); + Assert.assertEquals("Bearer this_is_a_token", header); + return new Response.Builder() + .request(chain.request()) + .code(200) + .protocol(Protocol.HTTP_1_1) + .build(); + } + }); + ServiceClient serviceClient = new ServiceClient(restBuilder.build()) { }; + Response response = serviceClient.restClient().httpClient().newCall(new Request.Builder().url("http://localhost").build()).execute(); + Assert.assertEquals(200, response.code()); } } diff --git a/client-runtime/src/test/java/com/microsoft/rest/RetryHandlerTests.java b/client-runtime/src/test/java/com/microsoft/rest/RetryHandlerTests.java index b0ddd36a1860b..050e7374aba18 100644 --- a/client-runtime/src/test/java/com/microsoft/rest/RetryHandlerTests.java +++ b/client-runtime/src/test/java/com/microsoft/rest/RetryHandlerTests.java @@ -41,8 +41,9 @@ public Response intercept(Chain chain) throws IOException { .build(); } }); - ServiceClient serviceClient = new ServiceClient(clientBuilder, retrofitBuilder) { }; - Response response = serviceClient.clientBuilder.build().newCall( + RestClient.Builder restBuilder = new RestClient.Builder("http://localhost", clientBuilder, retrofitBuilder); + ServiceClient serviceClient = new ServiceClient(restBuilder.build()) { }; + Response response = serviceClient.restClient().httpClient().newCall( new Request.Builder().url("http://localhost").get().build()).execute(); Assert.assertEquals(501, response.code()); } @@ -66,8 +67,9 @@ public Response intercept(Chain chain) throws IOException { .build(); } }); - ServiceClient serviceClient = new ServiceClient(clientBuilder, retrofitBuilder) { }; - Response response = serviceClient.clientBuilder.build().newCall( + RestClient.Builder restBuilder = new RestClient.Builder("http://localhost", clientBuilder, retrofitBuilder); + ServiceClient serviceClient = new ServiceClient(restBuilder.build()) { }; + Response response = serviceClient.restClient().httpClient().newCall( new Request.Builder().url("http://localhost").get().build()).execute(); Assert.assertEquals(500, response.code()); } diff --git a/client-runtime/src/test/java/com/microsoft/rest/ServiceClientTests.java b/client-runtime/src/test/java/com/microsoft/rest/ServiceClientTests.java index ee116d1bfa52d..5497d607549d7 100644 --- a/client-runtime/src/test/java/com/microsoft/rest/ServiceClientTests.java +++ b/client-runtime/src/test/java/com/microsoft/rest/ServiceClientTests.java @@ -7,16 +7,18 @@ package com.microsoft.rest; -import okhttp3.Interceptor; -import okhttp3.OkHttpClient; -import okhttp3.Response; -import retrofit2.Retrofit; - import org.junit.Assert; import org.junit.Test; import java.io.IOException; +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; +import retrofit2.Retrofit; + public class ServiceClientTests { @Test public void filterTests() throws Exception { @@ -29,10 +31,17 @@ public void filterTests() throws Exception { public Response intercept(Chain chain) throws IOException { Assert.assertEquals("1", chain.request().header("filter1")); Assert.assertEquals("2", chain.request().header("filter2")); - return chain.proceed(chain.request()); + return new Response.Builder() + .request(chain.request()) + .code(200) + .protocol(Protocol.HTTP_1_1) + .build(); } }); - ServiceClient serviceClient = new ServiceClient(clientBuilder, retrofitBuilder) { }; + RestClient.Builder restBuilder = new RestClient.Builder("http://localhost", clientBuilder, retrofitBuilder); + ServiceClient serviceClient = new ServiceClient(restBuilder.build()) { }; + Response response = serviceClient.restClient().httpClient().newCall(new Request.Builder().url("http://localhost").build()).execute(); + Assert.assertEquals(200, response.code()); } public class FirstFilter implements Interceptor { diff --git a/client-runtime/src/test/java/com/microsoft/rest/UserAgentTests.java b/client-runtime/src/test/java/com/microsoft/rest/UserAgentTests.java index a87c951dfc36f..e6b938f933683 100644 --- a/client-runtime/src/test/java/com/microsoft/rest/UserAgentTests.java +++ b/client-runtime/src/test/java/com/microsoft/rest/UserAgentTests.java @@ -35,7 +35,8 @@ public Response intercept(Chain chain) throws IOException { .build(); } }); - ServiceClient serviceClient = new ServiceClient(clientBuilder, retrofitBuilder) { }; + RestClient.Builder restBuilder = new RestClient.Builder("http://localhost", clientBuilder, retrofitBuilder); + ServiceClient serviceClient = new ServiceClient(restBuilder.build()) { }; } @Test @@ -55,6 +56,7 @@ public Response intercept(Chain chain) throws IOException { .build(); } }); - ServiceClient serviceClient = new ServiceClient(clientBuilder, retrofitBuilder) { }; + RestClient.Builder restBuilder = new RestClient.Builder("http://localhost", clientBuilder, retrofitBuilder); + ServiceClient serviceClient = new ServiceClient(restBuilder.build()) { }; } }