Skip to content

Commit

Permalink
Merge pull request Azure#984 from jianghaolu/builder
Browse files Browse the repository at this point in the history
Wrap OkHttp and Retrofit into RestClient with builder pattern
  • Loading branch information
jianghaolu committed Apr 27, 2016
2 parents f7a6b01 + 8009399 commit 4a965fd
Show file tree
Hide file tree
Showing 10 changed files with 633 additions and 236 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@

package com.microsoft.azure;

import com.microsoft.rest.RestClient;
import com.microsoft.rest.ServiceCall;
import com.microsoft.rest.ServiceCallback;
import com.microsoft.rest.ServiceException;
import com.microsoft.rest.ServiceResponse;
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;
Expand All @@ -24,11 +23,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;

Expand All @@ -42,32 +39,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);
}

/**
Expand Down Expand Up @@ -98,13 +81,13 @@ public <T> ServiceResponse<T> getPutOrPatchResult(Response<ResponseBody> 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<T> pollingState = new PollingState<>(response, this.getLongRunningOperationRetryTimeout(), resourceType, mapperAdapter);
PollingState<T> pollingState = new PollingState<>(response, this.getLongRunningOperationRetryTimeout(), resourceType, restClient().mapperAdapter());
String url = response.raw().request().url().toString();

// Check provisioning state
Expand Down Expand Up @@ -151,7 +134,7 @@ public <T, THeader> ServiceResponseWithHeaders<T, THeader> getPutOrPatchResultWi
ServiceResponse<T> bodyResponse = getPutOrPatchResult(response, resourceType);
return new ServiceResponseWithHeaders<>(
bodyResponse.getBody(),
mapperAdapter.<THeader>deserialize(mapperAdapter.serialize(bodyResponse.getResponse().headers()), headerType),
restClient().mapperAdapter().<THeader>deserialize(restClient().mapperAdapter().serialize(bodyResponse.getResponse().headers()), headerType),
bodyResponse.getResponse()
);
}
Expand Down Expand Up @@ -186,7 +169,7 @@ public <T> AsyncPollingTask<T> getPutOrPatchResultAsync(Response<ResponseBody> 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 */ }
Expand All @@ -196,7 +179,7 @@ public <T> AsyncPollingTask<T> getPutOrPatchResultAsync(Response<ResponseBody> r

PollingState<T> 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;
Expand Down Expand Up @@ -235,7 +218,7 @@ public void success(ServiceResponse<T> result) {
try {
callback.success(new ServiceResponseWithHeaders<>(
result.getBody(),
mapperAdapter.<THeader>deserialize(mapperAdapter.serialize(result.getResponse().headers()), headerType),
restClient().mapperAdapter().<THeader>deserialize(restClient().mapperAdapter().serialize(result.getResponse().headers()), headerType),
result.getResponse()
));
} catch (IOException e) {
Expand Down Expand Up @@ -273,13 +256,13 @@ public <T> ServiceResponse<T> getPostOrDeleteResult(Response<ResponseBody> 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<T> pollingState = new PollingState<>(response, this.getLongRunningOperationRetryTimeout(), resourceType, mapperAdapter);
PollingState<T> pollingState = new PollingState<>(response, this.getLongRunningOperationRetryTimeout(), resourceType, restClient().mapperAdapter());

// Check provisioning state
while (!AzureAsyncOperation.getTerminalStatuses().contains(pollingState.getStatus())) {
Expand Down Expand Up @@ -325,7 +308,7 @@ public <T, THeader> ServiceResponseWithHeaders<T, THeader> getPostOrDeleteResult
ServiceResponse<T> bodyResponse = getPostOrDeleteResult(response, resourceType);
return new ServiceResponseWithHeaders<>(
bodyResponse.getBody(),
mapperAdapter.<THeader>deserialize(mapperAdapter.serialize(bodyResponse.getResponse().headers()), headerType),
restClient().mapperAdapter().<THeader>deserialize(restClient().mapperAdapter().serialize(bodyResponse.getResponse().headers()), headerType),
bodyResponse.getResponse()
);
}
Expand Down Expand Up @@ -360,7 +343,7 @@ public <T> AsyncPollingTask<T> getPostOrDeleteResultAsync(Response<ResponseBody>
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 */ }
Expand All @@ -370,7 +353,7 @@ public <T> AsyncPollingTask<T> getPostOrDeleteResultAsync(Response<ResponseBody>

PollingState<T> 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;
Expand Down Expand Up @@ -408,7 +391,7 @@ public void success(ServiceResponse<T> result) {
try {
callback.success(new ServiceResponseWithHeaders<>(
result.getBody(),
mapperAdapter.<THeader>deserialize(mapperAdapter.serialize(result.getResponse().headers()), headerType),
restClient().mapperAdapter().<THeader>deserialize(restClient().mapperAdapter().serialize(result.getResponse().headers()), headerType),
result.getResponse()
));
} catch (IOException e) {
Expand Down Expand Up @@ -584,15 +567,15 @@ private <T> void updateStateFromAzureAsyncOperationHeader(PollingState<T> 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();
}

if (body == null || body.getStatus() == null) {
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;
Expand Down Expand Up @@ -624,14 +607,14 @@ public void success(ServiceResponse<ResponseBody> 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);
Expand Down Expand Up @@ -663,18 +646,17 @@ private Response<ResponseBody> 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<ResponseBody> 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;
Expand All @@ -701,8 +683,7 @@ private Call<ResponseBody> pollAsync(String url, final ServiceCallback<ResponseB
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);
Call<ResponseBody> call = service.get(endpoint.getFile());
call.enqueue(new ServiceResponseCallback<ResponseBody>(callback) {
@Override
Expand All @@ -713,10 +694,10 @@ public void onResponse(Call<ResponseBody> call, Response<ResponseBody> 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);
Expand Down Expand Up @@ -749,24 +730,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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,57 +8,24 @@
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 java.net.CookieManager;
import java.net.CookiePolicy;

import okhttp3.JavaNetCookieJar;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;

/**
* 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
* @param restClient the REST client
*/
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.
*/
@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);
}
}
Loading

0 comments on commit 4a965fd

Please sign in to comment.