Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ public void canCreateVirtualMachineSyncPoll() throws Exception {
while (!pollStatus.isComplete()) {
SdkContext.sleep(delayInMills);

PollResponse<Void> pollResponse = acceptedVirtualMachine.getSyncPoller().poll();
PollResponse<?> pollResponse = acceptedVirtualMachine.getSyncPoller().poll();
pollStatus = pollResponse.getStatus();
delayInMills = pollResponse.getRetryAfter() == null
? 10000
Expand All @@ -242,7 +242,7 @@ public void canCreateVirtualMachineSyncPoll() throws Exception {
while (!pollStatus.isComplete()) {
SdkContext.sleep(delayInMills);

PollResponse<Void> pollResponse = acceptedDelete.getSyncPoller().poll();
PollResponse<?> pollResponse = acceptedDelete.getSyncPoller().poll();
pollStatus = pollResponse.getStatus();
delayInMills = pollResponse.getRetryAfter() == null
? 10000
Expand Down
7 changes: 5 additions & 2 deletions sdk/management/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
The Azure Management Libraries for Java is a higher-level, object-oriented API for *managing* Azure resources,
that is optimized for ease of use, succinctness and consistency.

- [API reference documentation][docs]
- [Azure SDK for Java][java_sdk]
- **[Code snippets and samples][sample]**

## Getting started
Expand Down Expand Up @@ -95,6 +95,8 @@ The key concepts of Azure Management Libraries includes:
- Integration with Azure role-based access control.
- Asynchronous operations with [Reactor][reactor]. (Preview)
- Configurable client, e.g. configuring HTTP client, retries, logging, etc.
- [API design][design]
- [API design (preview)][design_preview]

### Service features

Expand Down Expand Up @@ -306,7 +308,7 @@ If you would like to become an active contributor to this project please follow
5. Create new Pull Request

<!-- LINKS -->
[docs]: http://azure.github.io/azure-sdk-for-java/
[java_sdk]: http://azure.github.io/azure-sdk-for-java/
[jdk]: https://docs.microsoft.com/java/azure/jdk/
[azure_subscription]: https://azure.microsoft.com/free/
[azure_identity]: ../identity/azure-identity
Expand All @@ -317,4 +319,5 @@ If you would like to become an active contributor to this project please follow
[authenticate]: docs/AUTH.md
[sample]: docs/SAMPLE.md
[design]: docs/DESIGN.md
[design_preview]: docs/DESIGN_PREVIEW.md
[reactor]: https://projectreactor.io/
54 changes: 54 additions & 0 deletions sdk/management/docs/DESIGN_PREVIEW.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Design for Azure Management Libraries for Java (Preview)

## Fluent interface

### Fine control over long-running operation

Resource provision takes time, a typical solution adopted by Azure services is the [long-running operation (LRO)][lro].

Fluent interface does the polling operations in background, and only returns the final result.

Azure Management Libraries supports fine control over the polling for certain important resources, via `Accepted` and `SyncPoller` class. Method verb is `beginCreate` and `beginDelete`.

`Accepted` class provides following functionalities:
- `ActivationResponse` via `getAcceptedResult` method provides the response of the first activation operation. Note that though it wraps a resource instance, some action on this resource instance will not work, since it is not provisioned yet.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's little confusing the method name and return type is using different term: Accepted and Activation. Is there better names? Same confusing on Class name. I can see Accepted from httpstatus code, will it be good such as beginCreateResult or beginLROResult etc?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ActivationResponse extends SimpleResponse which implements Response, hence named as Response.

Activation is mostly from here https://github.com/Azure/azure-sdk-for-java/blob/master/sdk/core/azure-core/src/main/java/com/azure/core/util/polling/PollerFlux.java#L58-L61

Maybe I can change the method to getActivationResponse.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to getActivationResponse

- `SyncPoller` via `getSyncPoller` method provides the control of the polling operations. `SyncPoller.poll` can be called at desired time.
- Resource instance via `getFinalResult` method, after completion of the polling operations. The method will throw `ManagementException` if polling failed and resource cannot be provisioned.

Here is sample code for Deployment.

```java
// begin provision
Accepted<Deployment> acceptedDeployment = azure.deployments()
.define(name)
...
.beginCreate();
Deployment provisioningDeployment = acceptedDeployment.getAcceptedResult().getValue();

LongRunningOperationStatus pollStatus = acceptedDeployment.getAcceptedResult().getStatus();
int delayInMills = acceptedDeployment.getAcceptedResult().getRetryAfter() == null
? 0
: (int) acceptedDeployment.getAcceptedResult().getRetryAfter().toMillis();
while (!pollStatus.isComplete()) {
Thread.sleep(delayInMills);

// poll
PollResponse<?> pollResponse = acceptedDeployment.getSyncPoller().poll();
pollStatus = pollResponse.getStatus();
delayInMills = pollResponse.getRetryAfter() == null
? DEFAULT_DELAY_IN_MILLIS
: (int) pollResponse.getRetryAfter().toMillis();
}
// pollStatus == LongRunningOperationStatus.SUCCESSFULLY_COMPLETED, if successful

// final result
Deployment deployment = acceptedDeployment.getFinalResult();
```

Supported Azure resources:
- `delete` for `ResourceGroup`
- `create` for `Deployment`
- `create` and `delete` for `GenericResource`
- `create` and `delete` for `VirtualMachine`

[lro]: https://docs.microsoft.com/en-us/azure/architecture/patterns/async-request-reply
Original file line number Diff line number Diff line change
Expand Up @@ -269,13 +269,11 @@ protected <T extends Indexable> T taskResult(String key) {
}
}

@SuppressWarnings("unchecked")
protected Function<InnerModelT, FluentModelT> innerToFluentMap(final FluentModelImplT fluentModelImplT) {
return new Function<InnerModelT, FluentModelT>() {
@Override
public FluentModelT apply(InnerModelT innerModel) {
fluentModelImplT.setInner(innerModel);
return (FluentModelT) fluentModelImplT;
}
return innerModel -> {
fluentModelImplT.setInner(innerModel);
return (FluentModelT) fluentModelImplT;
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ public void deleteById(String id) {
deleteByIdAsync(id).block();
}


@Override
public Mono<Void> deleteByIdAsync(String id) {
return deleteByResourceGroupAsync(ResourceUtils.groupFromResourceId(id), ResourceUtils.nameFromResourceId(id));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@

package com.azure.resourcemanager.resources.implementation;

import com.azure.core.http.rest.Response;
import com.azure.core.util.logging.ClientLogger;
import com.azure.resourcemanager.resources.ResourceManager;
import com.azure.resourcemanager.resources.fluentcore.model.Accepted;
import com.azure.resourcemanager.resources.fluentcore.model.implementation.AcceptedImpl;
import com.azure.resourcemanager.resources.models.GenericResource;
import com.azure.resourcemanager.resources.models.Plan;
import com.azure.resourcemanager.resources.fluentcore.arm.ResourceUtils;
Expand All @@ -12,8 +16,11 @@
import com.azure.resourcemanager.resources.fluent.inner.GenericResourceInner;
import com.azure.resourcemanager.resources.ResourceManagementClient;
import com.azure.resourcemanager.resources.fluent.ResourcesClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.ByteBuffer;

/**
* The implementation for GenericResource and its nested interfaces.
*/
Expand All @@ -28,6 +35,9 @@ final class GenericResourceImpl
GenericResource.Definition,
GenericResource.UpdateStages.WithApiVersion,
GenericResource.Update {

private final ClientLogger logger = new ClientLogger(GenericResourceImpl.class);

private String resourceProviderNamespace;
private String parentResourcePath;
private String resourceType;
Expand Down Expand Up @@ -140,33 +150,44 @@ public GenericResourceImpl withApiVersion(String apiVersion) {
return this;
}

// CreateUpdateTaskGroup.ResourceCreator implementation
@Override
public Mono<GenericResource> createResourceAsync() {
final GenericResourceImpl self = this;
Mono<String> observable = null;
if (apiVersion != null) {
observable = Mono.just(apiVersion);
public Accepted<GenericResource> beginCreate() {
String apiVersion = this.getApiVersionAsync().block();

String name = this.name();
if (!isInCreateMode()) {
name = ResourceUtils.nameFromResourceId(inner().id());
}

Response<Flux<ByteBuffer>> activationResponse = this.manager().inner().getResources()
.createOrUpdateWithResponseAsync(
resourceGroupName(),
resourceProviderNamespace,
parentResourcePath(),
resourceType,
name,
apiVersion,
inner()).block();
if (activationResponse == null) {
throw logger.logExceptionAsError(new NullPointerException());
} else {
final ResourceManagementClient serviceClient = this.manager().inner();
observable = this.manager().providers().getByNameAsync(resourceProviderNamespace)
.flatMap(provider -> {
String id;
if (!isInCreateMode()) {
id = inner().id();
} else {
id = ResourceUtils.constructResourceId(
serviceClient.getSubscriptionId(),
resourceGroupName(),
resourceProviderNamespace(),
resourceType(),
this.name(),
parentResourcePath());
}
self.apiVersion = ResourceUtils.defaultApiVersion(id, provider);
return Mono.just(self.apiVersion);
});
Accepted<GenericResource> accepted = new AcceptedImpl<GenericResourceInner, GenericResource>(
activationResponse,
this.manager().inner().getSerializerAdapter(),
this.manager().inner().getHttpPipeline(),
GenericResourceInner.class,
GenericResourceInner.class,
inner -> new GenericResourceImpl(inner.id(), inner, this.manager()));

setInner(accepted.getAcceptedResult().getValue().inner());
return accepted;
}
}

// CreateUpdateTaskGroup.ResourceCreator implementation
@Override
public Mono<GenericResource> createResourceAsync() {
Mono<String> observable = this.getApiVersionAsync();
final ResourcesClient resourceClient = this.manager().inner().getResources();
return observable
.flatMap(api -> {
Expand All @@ -183,7 +204,34 @@ public Mono<GenericResource> createResourceAsync() {
api,
inner())
.subscribeOn(SdkContext.getReactorScheduler())
.map(innerToFluentMap(self));
.map(innerToFluentMap(this));
});
}

private Mono<String> getApiVersionAsync() {
Mono<String> apiVersion;
if (this.apiVersion != null) {
apiVersion = Mono.just(this.apiVersion);
} else {
final ResourceManagementClient serviceClient = this.manager().inner();
apiVersion = this.manager().providers().getByNameAsync(resourceProviderNamespace)
.flatMap(provider -> {
String id;
if (!isInCreateMode()) {
id = inner().id();
} else {
id = ResourceUtils.constructResourceId(
serviceClient.getSubscriptionId(),
resourceGroupName(),
resourceProviderNamespace(),
resourceType(),
this.name(),
parentResourcePath());
}
this.apiVersion = ResourceUtils.defaultApiVersion(id, provider);
return Mono.just(this.apiVersion);
});
}
return apiVersion;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@

import com.azure.core.http.rest.PagedFlux;
import com.azure.core.http.rest.PagedIterable;
import com.azure.core.http.rest.Response;
import com.azure.core.util.logging.ClientLogger;
import com.azure.resourcemanager.resources.ResourceManager;
import com.azure.resourcemanager.resources.fluentcore.model.Accepted;
import com.azure.resourcemanager.resources.fluentcore.model.implementation.AcceptedImpl;
import com.azure.resourcemanager.resources.models.GenericResource;
import com.azure.resourcemanager.resources.models.GenericResources;
import com.azure.resourcemanager.resources.models.ResourceGroup;
Expand All @@ -17,9 +20,12 @@
import com.azure.resourcemanager.resources.fluentcore.utils.Utils;
import com.azure.resourcemanager.resources.fluent.inner.GenericResourceInner;
import com.azure.resourcemanager.resources.fluent.ResourcesClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.ByteBuffer;
import java.util.List;
import java.util.function.Function;

/**
* Implementation of the {@link GenericResources}.
Expand Down Expand Up @@ -179,7 +185,6 @@ public Mono<Void> deleteAsync(String resourceGroupName, String resourceProviderN
parentResourcePath, resourceType, resourceName, apiVersion);
}


@Override
protected GenericResourceImpl wrapModel(String id) {
return new GenericResourceImpl(id, new GenericResourceInner(), this.manager())
Expand Down Expand Up @@ -222,6 +227,24 @@ public Mono<Void> deleteByIdAsync(final String id) {
.flatMap(apiVersion -> inner.deleteByIdAsync(id, apiVersion));
}

@Override
public Accepted<Void> beginDeleteById(String id) {
String apiVersion = getApiVersionFromId(id).block();

Response<Flux<ByteBuffer>> activationResponse = this.inner()
.deleteByIdWithResponseAsync(id, apiVersion).block();
if (activationResponse == null) {
throw logger.logExceptionAsError(new NullPointerException());
} else {
return new AcceptedImpl<Void, Void>(activationResponse,
manager().inner().getSerializerAdapter(),
manager().inner().getHttpPipeline(),
Void.class,
Void.class,
Function.identity());
}
}

private Mono<String> getApiVersionFromId(final String id) {
return this.manager().providers().getByNameAsync(ResourceUtils.resourceProviderFromResourceId(id))
.map(provider -> ResourceUtils.defaultApiVersion(id, provider));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.azure.core.annotation.Fluent;
import com.azure.resourcemanager.resources.fluentcore.arm.models.GroupableResource;
import com.azure.resourcemanager.resources.fluentcore.arm.models.Resource;
import com.azure.resourcemanager.resources.fluentcore.model.Accepted;
import com.azure.resourcemanager.resources.fluentcore.model.Appliable;
import com.azure.resourcemanager.resources.fluentcore.model.Creatable;
import com.azure.resourcemanager.resources.fluentcore.model.Refreshable;
Expand Down Expand Up @@ -181,6 +182,13 @@ interface WithCreate extends
* @return the next stage of generic resource definition
*/
WithCreate withProperties(Object properties);

/**
* Begins creating the Azure resource.
*
* @return the accepted create operation
*/
Accepted<GenericResource> beginCreate();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.azure.resourcemanager.resources.fluentcore.collection.SupportsDeletingById;
import com.azure.resourcemanager.resources.fluentcore.collection.SupportsListing;
import com.azure.resourcemanager.resources.ResourceManager;
import com.azure.resourcemanager.resources.fluentcore.model.Accepted;
import reactor.core.publisher.Mono;

import java.util.List;
Expand Down Expand Up @@ -136,4 +137,12 @@ void delete(String resourceGroupName, String resourceProviderNamespace,
*/
Mono<Void> deleteAsync(String resourceGroupName, String resourceProviderNamespace,
String parentResourcePath, String resourceType, String resourceName, String apiVersion);

/**
* Begins deleting a resource from Azure, identifying it by its resource ID.
*
* @param id the resource ID of the resource to delete
* @return the accepted deleting operation
*/
Accepted<Void> beginDeleteById(String id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ public void canDeployVirtualNetworkSyncPoll() throws Exception {
while (!pollStatus.isComplete()) {
SdkContext.sleep(delayInMills);

PollResponse<Void> pollResponse = acceptedDeployment.getSyncPoller().poll();
PollResponse<?> pollResponse = acceptedDeployment.getSyncPoller().poll();
pollStatus = pollResponse.getStatus();
delayInMills = pollResponse.getRetryAfter() == null
? 10000
Expand Down Expand Up @@ -290,7 +290,7 @@ public void canDeployVirtualNetworkSyncPollWithFailure() throws Exception {
while (!pollStatus.isComplete()) {
SdkContext.sleep(delayInMills);

PollResponse<Void> pollResponse = acceptedDeployment.getSyncPoller().poll();
PollResponse<?> pollResponse = acceptedDeployment.getSyncPoller().poll();
pollStatus = pollResponse.getStatus();
delayInMills = pollResponse.getRetryAfter() == null
? 10000
Expand Down Expand Up @@ -322,7 +322,7 @@ public void canDeployVirtualNetworkSyncPollWithFailure() throws Exception {
while (!pollStatus.isComplete()) {
SdkContext.sleep(delayInMills);

PollResponse<Void> pollResponse = acceptedDeployment.getSyncPoller().poll();
PollResponse<?> pollResponse = acceptedDeployment.getSyncPoller().poll();
pollStatus = pollResponse.getStatus();
delayInMills = pollResponse.getRetryAfter() == null
? 10000
Expand Down
Loading