Skip to content

Commit

Permalink
Merge pull request #862 from anuchandy/pagedvmimages
Browse files Browse the repository at this point in the history
Lazy load the virtual machine image list
  • Loading branch information
Martin Sawicki authored Jun 21, 2016
2 parents 303ecbb + 53c7e8c commit 47154cc
Show file tree
Hide file tree
Showing 14 changed files with 327 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@
*/
package com.microsoft.azure.management.compute;

import java.io.IOException;
import java.util.List;

import com.microsoft.azure.CloudException;
import com.microsoft.azure.management.resources.fluentcore.arm.Region;

/**
Expand Down Expand Up @@ -36,11 +32,7 @@ public interface Sku {
String name();

/**
* Lists the virtual machines in this SKU.
*
* @return the virtual machine images
* @throws CloudException thrown for an invalid response from the service
* @throws IOException thrown for IO exception
* @return virtual machine images in the sku
*/
List<VirtualMachineImage> listImages() throws CloudException, IOException;
VirtualMachineImagesInSku images();
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
package com.microsoft.azure.management.compute;

import java.io.IOException;
import java.util.List;

import com.microsoft.azure.CloudException;
import com.microsoft.azure.PagedList;
import com.microsoft.azure.management.resources.fluentcore.arm.Region;
import com.microsoft.azure.management.resources.fluentcore.collection.SupportsListingByRegion;

Expand All @@ -31,7 +30,7 @@ public interface VirtualMachineImages extends
* @throws CloudException exceptions thrown from the cloud
* @throws IOException exceptions thrown from serialization/deserialization
*/
List<VirtualMachineImage> listByRegion(String regionName) throws CloudException, IOException;
PagedList<VirtualMachineImage> listByRegion(String regionName) throws CloudException, IOException;

/**
* Lists all the virtual machine images available in a given region.
Expand All @@ -42,5 +41,5 @@ public interface VirtualMachineImages extends
* @throws CloudException exceptions thrown from the cloud
* @throws IOException exceptions thrown from serialization/deserialization
*/
List<VirtualMachineImage> listByRegion(Region region) throws CloudException, IOException;
PagedList<VirtualMachineImage> listByRegion(Region region) throws CloudException, IOException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.microsoft.azure.management.compute;
import com.microsoft.azure.management.resources.fluentcore.collection.SupportsListing;

/**
* Entry point to virtual machine sku images.
*/
public interface VirtualMachineImagesInSku extends SupportsListing<VirtualMachineImage> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package com.microsoft.azure.management.compute.implementation;

import com.microsoft.azure.CloudException;
import com.microsoft.azure.Page;
import com.microsoft.azure.PagedList;
import com.microsoft.rest.RestException;

import java.io.IOException;
import java.util.Iterator;
import java.util.List;

/**
* {@link ChildListFlattener} that can take a paged list of parents and flatten their child lists
* as a single lazy paged list.
*
* @param <ParentT> the type of parent paged list item
* @param <ChildT> the type of child paged list item
*/
final class ChildListFlattener<ParentT, ChildT> {
protected final String switchToCousin = "switchToCousin";
protected Iterator<ParentT> parentItr;
protected PagedList<ChildT> currentChildList;
protected PagedList<ChildT> cousinList;
private final ChildListLoader<ParentT, ChildT> childListLoader;

/**
* Interface that will be implemented by the consumer of {@link ChildListFlattener}.
* <p>
* implementation will be used by {@link ChildListFlattener#flatten()} to load child
* paged list of parents in the parent paged list.
*
* @param <T> the parent type
* @param <U> the type of items in the child list
*/
interface ChildListLoader<T, U> {
/**
* Get the child paged list associated with the given parent.
*
* @param parent the parent
* @return child paged list associated with the parent
* @throws CloudException exceptions thrown from the cloud
* @throws IOException exceptions thrown from serialization/deserialization
*/
PagedList<U> loadList(T parent) throws CloudException, IOException;
}

/**
* Creates ChildListFlattener.
*
* @param parentList a paged list of parents
* @param childListLoader {@link ChildListLoader} for fetching child paged list associated any parent
*/
ChildListFlattener(PagedList<ParentT> parentList, ChildListLoader<ParentT, ChildT> childListLoader) {
this.parentItr = parentList.iterator();
this.childListLoader = childListLoader;
}

/**
* flatten the child paged lists.
*
* @return the lazy flattened paged list from the child paged lists
* @throws CloudException exceptions thrown from the cloud
* @throws IOException exceptions thrown from serialization/deserialization
*/
public PagedList<ChildT> flatten() throws CloudException, IOException {
this.currentChildList = nextChildList();
if (this.currentChildList == null) {
return emptyPagedList();
}
// setCousin sets the next child paged list (i.e. the current child list's immediate cousin).
// we need to know in advance whether there is going to be a next child paged list (cousin).
// This is because the 'next link' of the current child paged list's last page will be null.
// when the PagedList sees next link as null it assumes there is no more pages hence it won't
// call nextPage and iteration stops, if cousin presents then 'childListPage' method replace
// this null with a marker indicating there is more pages.
setCousin();
return new PagedList<ChildT>(childListPage(currentChildList.currentPage())) {
@Override
public Page<ChildT> nextPage(String nextPageLink) throws RestException, IOException {
if (nextPageLink.equalsIgnoreCase(switchToCousin)) {
// Reached end of current child paged list, make next child list(cousin) as current
// paged list and return it's first page.
currentChildList = cousinList;
setCousin();
return childListPage(currentChildList.currentPage());
} else {
currentChildList.loadNextPage();
if (currentChildList.currentPage().getNextPageLink() == null) {
// This is the last page of the current child paged list set it's cousin
// so that next call to nextPage can start using it.
setCousin();
}
return childListPage(currentChildList.currentPage());
}
}
};
}

/**
* @return true if the current child paged list has a cousin
*/
private boolean hasCousin() {
return this.cousinList != null;
}

/**
* Locate and sets the cousin list (the next child paged list).
*
* @throws CloudException exceptions thrown from the cloud
* @throws IOException exceptions thrown from serialization/deserialization
*/
private void setCousin() throws CloudException, IOException {
cousinList = nextChildList();
}

/**
* Returns the next child paged list containing at least one item.
* <p>
* This method iterate the parent list from where it stopped last time and return
* a non-empty child paged list of a parent. If there is no parent with non-empty child list
* or if the parent list iteration is finished then this method returns null.
*
* @return a child paged list {@link PagedList}
* @throws CloudException exceptions thrown from the cloud
* @throws IOException exceptions thrown from serialization/deserialization
*/
private PagedList<ChildT> nextChildList() throws CloudException, IOException {
while (parentItr.hasNext()) {
PagedList<ChildT> nextChildList = childListLoader.loadList(parentItr.next());
if (nextChildList.iterator().hasNext()) {
return nextChildList;
}
}
return null;
}

/**
* Method returns a {@link Page} with the same items as in the given page, if the given
* page is last page of the current paged child list and if there is a cousin list then
* returned page's next-link will be set to a predefined value indicating presence of
* cousin list.
*
* @param page the page
* @return page with next-link updated if there is a cousin
*/
private Page<ChildT> childListPage(final Page<ChildT> page) {
return new Page<ChildT>() {
@Override
public String getNextPageLink() {
if (page.getNextPageLink() != null) {
// The current child paged list has more pages.
return page.getNextPageLink();
}

if (hasCousin()) {
// The current child paged list has no more pages so switch to it's cousin list
return switchToCousin;
}
// reached end of child paged list of last parent, iteration will be stopped.
return null;
}

@Override
public List<ChildT> getItems() {
return page.getItems();
}
};
}

/**
* @return an empty paged list
*/
private PagedList<ChildT> emptyPagedList() {
return new PagedList<ChildT>() {
@Override
public Page<ChildT> nextPage(String nextPageLink) throws RestException, IOException {
return null;
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package com.microsoft.azure.management.compute.implementation;

import com.microsoft.azure.CloudException;
import com.microsoft.azure.PagedList;
import com.microsoft.azure.management.compute.Publisher;
import com.microsoft.azure.management.compute.Publishers;
import com.microsoft.azure.management.compute.implementation.api.VirtualMachineImageResourceInner;
Expand All @@ -14,7 +15,6 @@
import com.microsoft.azure.management.resources.fluentcore.arm.collection.implementation.ReadableWrappersImpl;

import java.io.IOException;
import java.util.List;

/**
* The implementation for {@link Publishers}.
Expand All @@ -30,7 +30,7 @@ class PublishersImpl
}

@Override
public List<Publisher> listByRegion(Region region) throws CloudException, IOException {
public PagedList<Publisher> listByRegion(Region region) throws CloudException, IOException {
return listByRegion(region.toString());
}

Expand All @@ -40,7 +40,7 @@ protected PublisherImpl wrapModel(VirtualMachineImageResourceInner inner) {
}

@Override
public List<Publisher> listByRegion(String regionName) throws CloudException, IOException {
public PagedList<Publisher> listByRegion(String regionName) throws CloudException, IOException {
return wrapList(innerCollection.listPublishers(regionName).getBody());
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
package com.microsoft.azure.management.compute.implementation;

import com.microsoft.azure.CloudException;
import com.microsoft.azure.management.compute.Offer;
import com.microsoft.azure.management.compute.Publisher;
import com.microsoft.azure.management.compute.Sku;
import com.microsoft.azure.management.compute.VirtualMachineImage;
import com.microsoft.azure.management.compute.implementation.api.VirtualMachineImageResourceInner;
import com.microsoft.azure.management.compute.VirtualMachineImagesInSku;
import com.microsoft.azure.management.compute.implementation.api.VirtualMachineImagesInner;
import com.microsoft.azure.management.resources.fluentcore.arm.Region;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
* The implementation for {@link Sku}.
*/
Expand All @@ -21,11 +15,13 @@ class SkuImpl
private final VirtualMachineImagesInner client;
private final Offer offer;
private final String skuName;
private final VirtualMachineImagesInSku imagesInSku;

SkuImpl(Offer offer, String skuName, VirtualMachineImagesInner client) {
this.offer = offer;
this.skuName = skuName;
this.client = client;
this.imagesInSku = new VirtualMachineImagesInSkuImpl(this, client);
}

@Override
Expand All @@ -48,24 +44,7 @@ public String name() {
}

@Override
public List<VirtualMachineImage> listImages() throws CloudException, IOException {
List<VirtualMachineImage> images = new ArrayList<>();
for (VirtualMachineImageResourceInner inner
: client.list(
region().toString(),
publisher().name(),
offer.name(),
skuName).getBody()) {
String version = inner.name();
images.add(new VirtualMachineImageImpl(
region(),
publisher().name(),
offer.name(),
skuName,
version,
client.get(region().toString(), publisher().name(), offer.name(), skuName, version).getBody(),
client));
}
return images;
public VirtualMachineImagesInSku images() {
return this.imagesInSku;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import com.microsoft.azure.management.compute.implementation.api.OSDiskImage;
import com.microsoft.azure.management.compute.implementation.api.PurchasePlan;
import com.microsoft.azure.management.compute.implementation.api.VirtualMachineImageInner;
import com.microsoft.azure.management.compute.implementation.api.VirtualMachineImagesInner;
import com.microsoft.azure.management.resources.fluentcore.arm.Region;
import com.microsoft.azure.management.resources.fluentcore.model.implementation.IndexableWrapperImpl;

Expand All @@ -21,7 +20,7 @@ class VirtualMachineImageImpl
private final Region location;
private ImageReference imageReference;

VirtualMachineImageImpl(Region location, String publisher, String offer, String sku, String version, VirtualMachineImagesInner client) {
VirtualMachineImageImpl(Region location, String publisher, String offer, String sku, String version) {
super(null, null);
this.location = location;
this.imageReference = new ImageReference();
Expand All @@ -31,7 +30,7 @@ class VirtualMachineImageImpl
this.imageReference.withVersion(version);
}

VirtualMachineImageImpl(Region location, String publisher, String offer, String sku, String version, VirtualMachineImageInner innerModel, VirtualMachineImagesInner client) {
VirtualMachineImageImpl(Region location, String publisher, String offer, String sku, String version, VirtualMachineImageInner innerModel) {
super(innerModel.id(), innerModel);
this.location = location;
this.imageReference = new ImageReference();
Expand Down
Loading

0 comments on commit 47154cc

Please sign in to comment.