forked from Azure/azure-sdk-for-java
-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
core-experimental, support new LRO polling strategy (Azure#34525)
- Loading branch information
1 parent
efff3dc
commit d7be605
Showing
15 changed files
with
873 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
.../azure-core-experimental/src/main/java/com/azure/core/experimental/models/PollResult.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
package com.azure.core.experimental.models; | ||
|
||
import com.azure.core.annotation.Immutable; | ||
import com.azure.core.models.ResponseError; | ||
import com.fasterxml.jackson.annotation.JsonCreator; | ||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
|
||
/** Provides status details for long running operations. */ | ||
@Immutable | ||
public final class PollResult { | ||
@JsonProperty(value = "id", required = true) | ||
private final String operationId; | ||
|
||
@JsonProperty(value = "error") | ||
private ResponseError error; | ||
|
||
/** | ||
* Creates an instance of ResourceOperationStatusUserError class. | ||
* | ||
* @param operationId the unique ID of the operation. | ||
*/ | ||
@JsonCreator | ||
private PollResult(@JsonProperty(value = "id", required = true) String operationId) { | ||
this.operationId = operationId; | ||
} | ||
|
||
/** | ||
* Get the id property: The unique ID of the operation. | ||
* | ||
* @return the unique ID of the operation. | ||
*/ | ||
public String getOperationId() { | ||
return this.operationId; | ||
} | ||
|
||
/** | ||
* Get the error property: Error object that describes the error when status is "Failed". | ||
* | ||
* @return the error object that describes the error when status is "Failed". | ||
*/ | ||
public ResponseError getError() { | ||
return this.error; | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
...zure-core-experimental/src/main/java/com/azure/core/experimental/models/package-info.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
/** | ||
* Package containing core model classes. | ||
*/ | ||
package com.azure.core.experimental.models; |
7 changes: 7 additions & 0 deletions
7
.../azure-core-experimental/src/main/java/com/azure/core/experimental/util/package-info.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
/** | ||
* Package containing core utility classes. | ||
*/ | ||
package com.azure.core.experimental.util; |
142 changes: 142 additions & 0 deletions
142
.../main/java/com/azure/core/experimental/util/polling/OperationLocationPollingStrategy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
package com.azure.core.experimental.util.polling; | ||
|
||
import com.azure.core.exception.AzureException; | ||
import com.azure.core.experimental.util.polling.implementation.PostPollResult; | ||
import com.azure.core.http.HttpHeader; | ||
import com.azure.core.http.HttpHeaderName; | ||
import com.azure.core.http.HttpMethod; | ||
import com.azure.core.http.rest.Response; | ||
import com.azure.core.implementation.ImplUtils; | ||
import com.azure.core.util.BinaryData; | ||
import com.azure.core.util.logging.ClientLogger; | ||
import com.azure.core.util.polling.LongRunningOperationStatus; | ||
import com.azure.core.util.polling.OperationResourcePollingStrategy; | ||
import com.azure.core.util.polling.PollResponse; | ||
import com.azure.core.util.polling.PollingContext; | ||
import com.azure.core.util.polling.PollingStrategyOptions; | ||
import com.azure.core.util.polling.implementation.PollingConstants; | ||
import com.azure.core.util.polling.implementation.PollingUtils; | ||
import com.azure.core.util.serializer.JsonSerializerProviders; | ||
import com.azure.core.util.serializer.ObjectSerializer; | ||
import com.azure.core.util.serializer.TypeReference; | ||
import reactor.core.publisher.Mono; | ||
|
||
import java.time.Duration; | ||
import java.time.OffsetDateTime; | ||
|
||
/** | ||
* Implements an operation location polling strategy, from Operation-Location. | ||
* | ||
* @param <T> the type of the response type from a polling call, or BinaryData if raw response body should be kept | ||
* @param <U> the type of the final result object to deserialize into, or BinaryData if raw response body should be | ||
* kept | ||
*/ | ||
public class OperationLocationPollingStrategy<T, U> extends OperationResourcePollingStrategy<T, U> { | ||
|
||
private static final ClientLogger LOGGER = new ClientLogger(OperationLocationPollingStrategy.class); | ||
|
||
private static final HttpHeaderName OPERATION_LOCATION_HEADER | ||
= HttpHeaderName.fromString("Operation-Location"); | ||
|
||
private static final TypeReference<PostPollResult> POST_POLL_RESULT_TYPE_REFERENCE | ||
= TypeReference.createInstance(PostPollResult.class); | ||
|
||
private final ObjectSerializer serializer; | ||
private final String endpoint; | ||
|
||
/** | ||
* Creates an instance of the operation resource polling strategy. | ||
* | ||
* @param pollingStrategyOptions options to configure this polling strategy. | ||
* @throws NullPointerException if {@code pollingStrategyOptions} is null. | ||
*/ | ||
public OperationLocationPollingStrategy(PollingStrategyOptions pollingStrategyOptions) { | ||
super(OPERATION_LOCATION_HEADER, pollingStrategyOptions); | ||
this.endpoint = pollingStrategyOptions.getEndpoint(); | ||
this.serializer = pollingStrategyOptions.getSerializer() != null | ||
? pollingStrategyOptions.getSerializer() : JsonSerializerProviders.createInstance(true); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
@Override | ||
public Mono<PollResponse<T>> onInitialResponse(Response<?> response, PollingContext<T> pollingContext, | ||
TypeReference<T> pollResponseType) { | ||
HttpHeader operationLocationHeader = response.getHeaders().get(OPERATION_LOCATION_HEADER); | ||
if (operationLocationHeader != null) { | ||
pollingContext.setData(OPERATION_LOCATION_HEADER.getCaseSensitiveName(), | ||
PollingUtils.getAbsolutePath(operationLocationHeader.getValue(), endpoint, LOGGER)); | ||
} | ||
final String httpMethod = response.getRequest().getHttpMethod().name(); | ||
pollingContext.setData(PollingConstants.HTTP_METHOD, httpMethod); | ||
|
||
if (response.getStatusCode() == 200 | ||
|| response.getStatusCode() == 201 | ||
|| response.getStatusCode() == 202 | ||
|| response.getStatusCode() == 204) { | ||
final Duration retryAfter = ImplUtils.getRetryAfterFromHeaders(response.getHeaders(), OffsetDateTime::now); | ||
final Mono<PollResponse<T>> pollResponseMono; | ||
if (HttpMethod.PUT.name().equalsIgnoreCase(httpMethod) | ||
|| HttpMethod.PATCH.name().equalsIgnoreCase(httpMethod)) { | ||
// PUT has initial response body as resultType | ||
// we expect Response<?> be either Response<BinaryData> or Response<U> | ||
// if it is not Response<BinaryData>, PollingUtils.serializeResponse would miss read-only properties | ||
pollResponseMono = PollingUtils.serializeResponse(response.getValue(), serializer) | ||
.map(initialResponseBody -> { | ||
pollingContext.setData( | ||
PollingConstants.INITIAL_RESOURCE_RESPONSE_BODY, initialResponseBody.toString()); | ||
return new PollResponse<>(LongRunningOperationStatus.IN_PROGRESS, null, retryAfter); | ||
}); | ||
} else { | ||
// same as OperationResourcePollingStrategy | ||
pollResponseMono = PollingUtils.convertResponse(response.getValue(), serializer, pollResponseType) | ||
.map(value -> new PollResponse<>(LongRunningOperationStatus.IN_PROGRESS, value, retryAfter)); | ||
} | ||
return pollResponseMono.switchIfEmpty(Mono.fromSupplier(() -> new PollResponse<>( | ||
LongRunningOperationStatus.IN_PROGRESS, null, retryAfter))); | ||
} else { | ||
return Mono.error(new AzureException(String.format("Operation failed or cancelled with status code %d," | ||
+ ", '%s' header: %s, and response body: %s", response.getStatusCode(), OPERATION_LOCATION_HEADER, | ||
operationLocationHeader, PollingUtils.serializeResponse(response.getValue(), serializer)))); | ||
} | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
@Override | ||
public Mono<U> getResult(PollingContext<T> pollingContext, TypeReference<U> resultType) { | ||
if (pollingContext.getLatestResponse().getStatus() == LongRunningOperationStatus.FAILED) { | ||
return Mono.error(new AzureException("Long running operation failed.")); | ||
} else if (pollingContext.getLatestResponse().getStatus() == LongRunningOperationStatus.USER_CANCELLED) { | ||
return Mono.error(new AzureException("Long running operation cancelled.")); | ||
} | ||
String httpMethod = pollingContext.getData(PollingConstants.HTTP_METHOD); | ||
if (HttpMethod.PUT.name().equalsIgnoreCase(httpMethod) | ||
|| HttpMethod.PATCH.name().equalsIgnoreCase(httpMethod)) { | ||
// take the initial response body from PollingContext, and de-serialize as final result | ||
BinaryData initialResponseBody = BinaryData.fromString(pollingContext.getData( | ||
PollingConstants.INITIAL_RESOURCE_RESPONSE_BODY)); | ||
return PollingUtils.deserializeResponse(initialResponseBody, serializer, resultType); | ||
} else if (HttpMethod.POST.name().equalsIgnoreCase(httpMethod)) { | ||
// take the last poll response body from PollingContext, | ||
// and de-serialize the "result" property as final result | ||
BinaryData latestResponseBody = | ||
BinaryData.fromString(pollingContext.getData(PollingConstants.POLL_RESPONSE_BODY)); | ||
return PollingUtils.deserializeResponse(latestResponseBody, serializer, POST_POLL_RESULT_TYPE_REFERENCE) | ||
.flatMap(value -> { | ||
if (value.getResult() != null) { | ||
return PollingUtils.deserializeResponse(value.getResult(), serializer, resultType); | ||
} else { | ||
return Mono.error(new AzureException("Cannot get final result")); | ||
} | ||
}).switchIfEmpty(Mono.error(new AzureException("Cannot get final result"))); | ||
} else { | ||
return Mono.error(new AzureException("Cannot get final result")); | ||
} | ||
} | ||
} |
135 changes: 135 additions & 0 deletions
135
...n/java/com/azure/core/experimental/util/polling/SyncOperationLocationPollingStrategy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
package com.azure.core.experimental.util.polling; | ||
|
||
import com.azure.core.exception.AzureException; | ||
import com.azure.core.experimental.util.polling.implementation.PostPollResult; | ||
import com.azure.core.http.HttpHeader; | ||
import com.azure.core.http.HttpHeaderName; | ||
import com.azure.core.http.HttpMethod; | ||
import com.azure.core.http.rest.Response; | ||
import com.azure.core.implementation.ImplUtils; | ||
import com.azure.core.util.BinaryData; | ||
import com.azure.core.util.logging.ClientLogger; | ||
import com.azure.core.util.polling.LongRunningOperationStatus; | ||
import com.azure.core.util.polling.PollResponse; | ||
import com.azure.core.util.polling.PollingContext; | ||
import com.azure.core.util.polling.PollingStrategyOptions; | ||
import com.azure.core.util.polling.SyncOperationResourcePollingStrategy; | ||
import com.azure.core.util.polling.implementation.PollingConstants; | ||
import com.azure.core.util.polling.implementation.PollingUtils; | ||
import com.azure.core.util.serializer.JsonSerializerProviders; | ||
import com.azure.core.util.serializer.ObjectSerializer; | ||
import com.azure.core.util.serializer.TypeReference; | ||
|
||
import java.time.Duration; | ||
import java.time.OffsetDateTime; | ||
|
||
/** | ||
* Implements a synchronous operation location polling strategy, from Operation-Location. | ||
* | ||
* @param <T> the type of the response type from a polling call, or BinaryData if raw response body should be kept | ||
* @param <U> the type of the final result object to deserialize into, or BinaryData if raw response body should be | ||
* kept | ||
*/ | ||
public class SyncOperationLocationPollingStrategy<T, U> extends SyncOperationResourcePollingStrategy<T, U> { | ||
|
||
private static final ClientLogger LOGGER = new ClientLogger(SyncOperationLocationPollingStrategy.class); | ||
|
||
private static final HttpHeaderName OPERATION_LOCATION_HEADER | ||
= HttpHeaderName.fromString("Operation-Location"); | ||
|
||
private static final TypeReference<PostPollResult> POST_POLL_RESULT_TYPE_REFERENCE | ||
= TypeReference.createInstance(PostPollResult.class); | ||
|
||
private final ObjectSerializer serializer; | ||
private final String endpoint; | ||
|
||
/** | ||
* Creates an instance of the operation resource polling strategy. | ||
* | ||
* @param pollingStrategyOptions options to configure this polling strategy. | ||
* @throws NullPointerException if {@code pollingStrategyOptions} is null. | ||
*/ | ||
public SyncOperationLocationPollingStrategy(PollingStrategyOptions pollingStrategyOptions) { | ||
super(OPERATION_LOCATION_HEADER, pollingStrategyOptions); | ||
this.endpoint = pollingStrategyOptions.getEndpoint(); | ||
this.serializer = pollingStrategyOptions.getSerializer() != null | ||
? pollingStrategyOptions.getSerializer() : JsonSerializerProviders.createInstance(true); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
@Override | ||
public PollResponse<T> onInitialResponse(Response<?> response, PollingContext<T> pollingContext, | ||
TypeReference<T> pollResponseType) { | ||
HttpHeader operationLocationHeader = response.getHeaders().get(OPERATION_LOCATION_HEADER); | ||
if (operationLocationHeader != null) { | ||
pollingContext.setData(OPERATION_LOCATION_HEADER.getCaseSensitiveName(), | ||
PollingUtils.getAbsolutePath(operationLocationHeader.getValue(), endpoint, LOGGER)); | ||
} | ||
final String httpMethod = response.getRequest().getHttpMethod().name(); | ||
pollingContext.setData(PollingConstants.HTTP_METHOD, httpMethod); | ||
|
||
if (response.getStatusCode() == 200 | ||
|| response.getStatusCode() == 201 | ||
|| response.getStatusCode() == 202 | ||
|| response.getStatusCode() == 204) { | ||
final Duration retryAfter = ImplUtils.getRetryAfterFromHeaders(response.getHeaders(), OffsetDateTime::now); | ||
if (HttpMethod.PUT.name().equalsIgnoreCase(httpMethod) | ||
|| HttpMethod.PATCH.name().equalsIgnoreCase(httpMethod)) { | ||
// PUT has initial response body as resultType | ||
// we expect Response<?> be either Response<BinaryData> or Response<U> | ||
// if it is not Response<BinaryData>, PollingUtils.serializeResponse would miss read-only properties | ||
BinaryData initialResponseBody = PollingUtils.serializeResponseSync(response.getValue(), serializer); | ||
pollingContext.setData( | ||
PollingConstants.INITIAL_RESOURCE_RESPONSE_BODY, initialResponseBody.toString()); | ||
return new PollResponse<>(LongRunningOperationStatus.IN_PROGRESS, null, retryAfter); | ||
} else { | ||
// same as OperationResourcePollingStrategy | ||
return new PollResponse<>(LongRunningOperationStatus.IN_PROGRESS, | ||
PollingUtils.convertResponseSync(response.getValue(), serializer, pollResponseType), retryAfter); | ||
} | ||
} | ||
|
||
throw LOGGER.logExceptionAsError(new AzureException(String.format( | ||
"Operation failed or cancelled with status code %d, '%s' header: %s, and response body: %s", | ||
response.getStatusCode(), OPERATION_LOCATION_HEADER, operationLocationHeader, | ||
PollingUtils.serializeResponseSync(response.getValue(), serializer)))); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public U getResult(PollingContext<T> pollingContext, TypeReference<U> resultType) { | ||
if (pollingContext.getLatestResponse().getStatus() == LongRunningOperationStatus.FAILED) { | ||
throw LOGGER.logExceptionAsError(new AzureException("Long running operation failed.")); | ||
} else if (pollingContext.getLatestResponse().getStatus() == LongRunningOperationStatus.USER_CANCELLED) { | ||
throw LOGGER.logExceptionAsError(new AzureException("Long running operation cancelled.")); | ||
} | ||
String httpMethod = pollingContext.getData(PollingConstants.HTTP_METHOD); | ||
if (HttpMethod.PUT.name().equalsIgnoreCase(httpMethod) | ||
|| HttpMethod.PATCH.name().equalsIgnoreCase(httpMethod)) { | ||
// take the initial response body from PollingContext, and de-serialize as final result | ||
BinaryData initialResponseBody = BinaryData.fromString(pollingContext.getData( | ||
PollingConstants.INITIAL_RESOURCE_RESPONSE_BODY)); | ||
return PollingUtils.deserializeResponseSync(initialResponseBody, serializer, resultType); | ||
} else if (HttpMethod.POST.name().equalsIgnoreCase(httpMethod)) { | ||
// take the last poll response body from PollingContext, | ||
// and de-serialize the "result" property as final result | ||
BinaryData latestResponseBody = | ||
BinaryData.fromString(pollingContext.getData(PollingConstants.POLL_RESPONSE_BODY)); | ||
PostPollResult pollResult = | ||
PollingUtils.deserializeResponseSync(latestResponseBody, serializer, POST_POLL_RESULT_TYPE_REFERENCE); | ||
if (pollResult != null && pollResult.getResult() != null) { | ||
return PollingUtils.deserializeResponseSync(pollResult.getResult(), serializer, resultType); | ||
} else { | ||
throw LOGGER.logExceptionAsError(new AzureException("Cannot get final result")); | ||
} | ||
} else { | ||
throw LOGGER.logExceptionAsError(new AzureException("Cannot get final result")); | ||
} | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
...src/main/java/com/azure/core/experimental/util/polling/implementation/PostPollResult.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
package com.azure.core.experimental.util.polling.implementation; | ||
|
||
import com.azure.core.util.BinaryData; | ||
|
||
public final class PostPollResult { | ||
private BinaryData result; | ||
|
||
public BinaryData getResult() { | ||
return result; | ||
} | ||
|
||
public void setResult(BinaryData result) { | ||
this.result = result; | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
...ore-experimental/src/main/java/com/azure/core/experimental/util/polling/package-info.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
/** | ||
* Package containing API for long running operations. | ||
*/ | ||
package com.azure.core.experimental.util.polling; |
Oops, something went wrong.