From 1855733602de958764072d561e0671615cfa825f Mon Sep 17 00:00:00 2001 From: Martin Derka Date: Thu, 17 Mar 2016 19:08:18 -0700 Subject: [PATCH] Added batch concept supporting list zones calls only. --- .../main/java/com/google/gcloud/dns/Dns.java | 15 ++ .../java/com/google/gcloud/dns/DnsBatch.java | 231 ++++++++++++++++++ .../com/google/gcloud/dns/DnsException.java | 5 + .../java/com/google/gcloud/dns/DnsImpl.java | 87 ++++++- .../java/com/google/gcloud/dns/ZoneInfo.java | 2 + .../google/gcloud/dns/spi/DefaultDnsRpc.java | 35 ++- .../com/google/gcloud/dns/spi/DnsRpc.java | 8 + .../com/google/gcloud/dns/DnsImplTest.java | 30 +++ .../com/google/gcloud/dns/it/ITDnsTest.java | 62 +++++ 9 files changed, 466 insertions(+), 9 deletions(-) create mode 100644 gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsBatch.java diff --git a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/Dns.java b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/Dns.java index 6ce6b4c19994..7d7138111ae1 100644 --- a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/Dns.java +++ b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/Dns.java @@ -533,4 +533,19 @@ ChangeRequest getChangeRequest(String zoneName, String changeRequestId, * @see Cloud DNS Chages: list */ Page listChangeRequests(String zoneName, ChangeRequestListOption... options); + + /** + * Submits a {@code batch} for processing to the Google Cloud DNS. The operations in the batch + * will be called using a single HTTP request. For each successfully executed operation, its + * {@link DnsBatch.Callback#success(Object, DnsBatch.Request)} callback method will be invoked. + * For each operation which returned an error, its {@link DnsBatch.Callback#error(DnsException, + * DnsBatch.Request)} callback method will be invoked. + */ + void submitBatch(DnsBatch batch); + + /** + * Initiates a new empty batch ready to be populated with service calls, which will use this + * {@code Dns} instance when submitted for processing to Google Cloud DNS. + */ + DnsBatch batch(); } diff --git a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsBatch.java b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsBatch.java new file mode 100644 index 000000000000..6c2adf3b867d --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsBatch.java @@ -0,0 +1,231 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.dns; + +import com.google.gcloud.Page; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * A batch of operations to be submitted to Google Cloud DNS using a single HTTP request. + */ +public class DnsBatch { + + private Map requests = new LinkedHashMap<>(); + private Dns dns; + + /** + * An interface for the callback which will be invoked when the operation has been executed. The + * parameter {@code } represents the type of the result of the operation and thus depends on + * the {@link DnsBatch.Request} that this call back belongs to and it should be as follows: + * + *
    + *
  • {@link Zone} for creating and getting a zone
  • + *
  • {@link Boolean} for deleting a zone
  • + *
  • {@link ChangeRequest} for creating and getting a change request
  • + *
  • {@link ProjectInfo} for getting a project
  • + *
  • {@code Page} for listing zones
  • + *
  • {@code Page} for listing {@link DnsRecord}s inside a zone
  • + *
  • {@code Page} for listing {@link ChangeRequest}s for a zone
  • + *
+ */ + public interface Callback { + /** + * A method which will be called if the {@link DnsBatch.Request} succeeds. See the {@link + * Callback} documentation for details on type {@code T}. + * + * @param output the result of the operation + * @param request the request which succeeded + */ + void success(T output, DnsBatch.Request request); + + /** + * A method which will be called if the {@link DnsBatch.Request} fails. + * + * @param ex the error + * @param request the request which failed + */ + void error(DnsException ex, DnsBatch.Request request); + } + + /** + * An operation to be submitted to Google Cloud DNS within this batch. Only an subset of the class + * attributes appropriate for the represented operation is initialized. Refer to the class method + * and attribute documentation for the specific fields. + */ + public static class Request { + + private final String zoneName; + private final String changeId; + private final ChangeRequest changeRequest; + private final ZoneInfo zoneInfo; + private final Operation operation; + private final AbstractOption[] options; + + private Request(RequestBuilder builder) { + this.zoneName = builder.zoneName; + this.changeId = builder.changeId; + this.changeRequest = builder.changeRequest; + this.zoneInfo = builder.zoneInfo; + this.operation = builder.operation; + this.options = builder.options; + } + + private static RequestBuilder builder(Operation operation, AbstractOption... options) { + return new RequestBuilder(operation, options); + } + + /** + * Returns the name of the zone to which the operation is applied. This field is initialized for + * zone create, get and delete operation, and listing DNS records and changes within a zone. + * Returns {@code null} in other cases. + */ + public String zoneName() { + return zoneName; + } + + /** + * Returns the id of the change request which is being retrieved. Getting a change request is + * the only operation when this attribute is initialized. The method returns {@code null} in the + * remaining cases. + */ + public String changeId() { + return changeId; + } + + /** + * Returns the change request which is being created. Creating a change request is the only + * operation when this attribute is initialized. The method returns {@code null} in the + * remaining cases. + */ + public ChangeRequest changeRequest() { + return changeRequest; + } + + /** + * Returns the zone which is being created. Creating a zone is the only operation when this + * attribute is initialized. The method returns {@code null} in the remaining cases. + */ + public ZoneInfo zoneInfo() { + return zoneInfo; + } + + /** + * Returns the type of the operation represented by this {@link DnsBatch.Request}. This field is + * always initialized. + */ + public Operation operation() { + return operation; + } + + /** + * Returns options provided to the operation. Returns an empty array if no options were + * provided. + */ + public AbstractOption[] options() { + return options == null ? new AbstractOption[0] : options; + } + } + + static class RequestBuilder { + private final AbstractOption[] options; + private String zoneName; + private String changeId; + private ChangeRequest changeRequest; + private ZoneInfo zoneInfo; + private final Operation operation; + + RequestBuilder(Operation operation, AbstractOption... options) { + this.operation = operation; + this.options = options; + } + + RequestBuilder zoneName(String zoneName) { + this.zoneName = zoneName; + return this; + } + + RequestBuilder changeId(String changeId) { + this.changeId = changeId; + return this; + } + + RequestBuilder changeRequest(ChangeRequest changeRequest) { + this.changeRequest = changeRequest; + return this; + } + + RequestBuilder zoneInfo(ZoneInfo zoneInfo) { + this.zoneInfo = zoneInfo; + return this; + } + + Request build() { + return new Request(this); + } + } + + /** + * Represents the type of the batch operation. + */ + public enum Operation { + CREATE_ZONE, + DELETE_ZONE, + GET_ZONE, + LIST_ZONES, + APPLY_CHANGE_REQUEST, + GET_CHANGE_REQUEST, + LIST_CHANGES_REQUESTS, + LIST_DNS_RECORDS + } + + DnsBatch(Dns dns) { + this.dns = dns; + } + + public Dns service() { + return dns; + } + + Map requests() { + return requests; + } + + /** + * Adds a {@code DnsBatch.Request} represeting the list zones operation to this batch. The request + * will not have initialized any fields except for the operation type and options (if provided). + * The {@code callback} will receive a page of {@link Zone}s upon success of the request. The + * {@code options} can be used to restrict the fields returned or provide page size limits in the + * same way as for {@link Dns#listZones(Dns.ZoneListOption...)}. + */ + public DnsBatch listZones(Callback> callback, Dns.ZoneListOption... options) { + Request request = Request.builder(Operation.LIST_ZONES, options).build(); + requests.put(request, callback); + return this; + } + + // todo(mderka) add the rest of the operations + + /** + * Submits this batch for processing using a single HTTP request. This will invoke all callbacks + * for the invidual {@link DnsBatch.Request}s contained in this batch. + */ + public void submit() { + dns.submitBatch(this); + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsException.java b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsException.java index 1ecb98a3fdc6..21df5038a647 100644 --- a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsException.java +++ b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsException.java @@ -16,6 +16,7 @@ package com.google.gcloud.dns; +import com.google.api.client.googleapis.json.GoogleJsonError; import com.google.common.collect.ImmutableSet; import com.google.gcloud.BaseServiceException; import com.google.gcloud.RetryHelper.RetryHelperException; @@ -43,6 +44,10 @@ public DnsException(IOException exception) { super(exception, true); } + public DnsException(GoogleJsonError error) { + super(error, true); + } + private DnsException(int code, String message) { super(code, message, null, true); } diff --git a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsImpl.java b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsImpl.java index a60cfd9151da..61888a89de25 100644 --- a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsImpl.java +++ b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsImpl.java @@ -21,8 +21,13 @@ import static com.google.gcloud.RetryHelper.runWithRetries; import static com.google.gcloud.dns.ChangeRequest.fromPb; +import com.google.api.client.googleapis.batch.BatchRequest; +import com.google.api.client.googleapis.batch.json.JsonBatchCallback; +import com.google.api.client.googleapis.json.GoogleJsonError; +import com.google.api.client.http.HttpHeaders; import com.google.api.services.dns.model.Change; import com.google.api.services.dns.model.ManagedZone; +import com.google.api.services.dns.model.ManagedZonesListResponse; import com.google.api.services.dns.model.ResourceRecordSet; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; @@ -35,6 +40,7 @@ import com.google.gcloud.RetryHelper; import com.google.gcloud.dns.spi.DnsRpc; +import java.io.IOException; import java.util.Map; import java.util.concurrent.Callable; @@ -309,11 +315,86 @@ public com.google.api.services.dns.model.Change call() { } } + @Override + public void submitBatch(DnsBatch toSubmit) { + try { + BatchRequest batchRequest = prepareBatch(toSubmit); + batchRequest.execute(); + } catch (IOException ex) { + throw new DnsException(ex); + } + } + + /** + * Since {@code BatchRequest} is a final class, it cannot be mocked with easy mock and the call of + * {@code execute()} cannot be tested. Thus, most of the functionality of {@link + * #submitBatch(DnsBatch)} is extracted to this method which does not make the call so it does not + * communicate with the service. + */ + BatchRequest prepareBatch(DnsBatch toSubmit) throws IOException { + BatchRequest batch = null; + for (Map.Entry entry : toSubmit.requests().entrySet()) { + DnsBatch.Request request = entry.getKey(); + DnsBatch.Callback callback = entry.getValue(); + switch (request.operation()) { + case LIST_ZONES: + JsonBatchCallback rpcCallback = listZonesCallback(callback, request); + batch = + dnsRpc.enqueueListZones(batch, request, rpcCallback, optionMap(request.options())); + break; + default: + // todo(mderka) implement the rest of the operations + throw new UnsupportedOperationException("Not implemented yet"); + } + } + return batch; + } + + @Override + public DnsBatch batch() { + return new DnsBatch(this); + } + + private JsonBatchCallback listZonesCallback(final DnsBatch.Callback callback, + final DnsBatch.Request request) { + return new JsonBatchCallback() { + @Override + public void onFailure(GoogleJsonError error, HttpHeaders httpHeaders) { + if (callback != null) { + callback.error(new DnsException(error), request); + } + } + + @Override + public void onSuccess(ManagedZonesListResponse zoneList, HttpHeaders httpHeaders) { + if (callback != null) { + DnsRpc.ListResult listResult = + DnsRpc.ListResult.of(zoneList.getNextPageToken(), zoneList.getManagedZones()); + String cursor = listResult.pageToken(); + Iterable zones = listResult.results() == null ? ImmutableList.of() + : Iterables.transform(listResult.results(), new Function() { + @Override + public Zone apply(ManagedZone managedZone) { + return new Zone(options().service(), + new ZoneInfo.BuilderImpl(ZoneInfo.fromPb(managedZone))); + } + } + ); + Page page = new PageImpl<>( + new ZonePageFetcher(options(), cursor, optionMap(request.options())), cursor, zones); + callback.success(page, request); + } + } + }; + } + private Map optionMap(AbstractOption... options) { Map temp = Maps.newEnumMap(DnsRpc.Option.class); - for (AbstractOption option : options) { - Object prev = temp.put(option.rpcOption(), option.value()); - checkArgument(prev == null, "Duplicate option %s", option); + if (options != null) { + for (AbstractOption option : options) { + Object prev = temp.put(option.rpcOption(), option.value()); + checkArgument(prev == null, "Duplicate option %s", option); + } } return ImmutableMap.copyOf(temp); } diff --git a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/ZoneInfo.java b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/ZoneInfo.java index 38a88b67777e..4d29a6aee4eb 100644 --- a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/ZoneInfo.java +++ b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/ZoneInfo.java @@ -18,6 +18,8 @@ import static com.google.common.base.Preconditions.checkNotNull; +import com.google.api.services.dns.model.ManagedZone; +import com.google.common.base.Function; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; diff --git a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/spi/DefaultDnsRpc.java b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/spi/DefaultDnsRpc.java index f8b8adb87ada..d7f0087732d6 100644 --- a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/spi/DefaultDnsRpc.java +++ b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/spi/DefaultDnsRpc.java @@ -10,6 +10,9 @@ import static com.google.gcloud.dns.spi.DnsRpc.Option.SORTING_ORDER; import static java.net.HttpURLConnection.HTTP_NOT_FOUND; +import com.google.api.client.googleapis.batch.BatchRequest; +import com.google.api.client.googleapis.batch.json.JsonBatchCallback; +import com.google.api.client.googleapis.json.GoogleJsonError; import com.google.api.client.http.HttpRequestInitializer; import com.google.api.client.http.HttpTransport; import com.google.api.client.json.jackson.JacksonFactory; @@ -21,6 +24,7 @@ import com.google.api.services.dns.model.Project; import com.google.api.services.dns.model.ResourceRecordSet; import com.google.api.services.dns.model.ResourceRecordSetsListResponse; +import com.google.gcloud.dns.DnsBatch; import com.google.gcloud.dns.DnsException; import com.google.gcloud.dns.DnsOptions; @@ -40,6 +44,10 @@ private static DnsException translate(IOException exception) { return new DnsException(exception); } + private static DnsException translate(GoogleJsonError exception) { + return new DnsException(exception); + } + /** * Constructs an instance of this rpc client with provided {@link DnsOptions}. */ @@ -53,6 +61,26 @@ public DefaultDnsRpc(DnsOptions options) { this.options = options; } + @Override + public BatchRequest enqueueListZones(BatchRequest batch, final DnsBatch.Request request, + JsonBatchCallback batchCallback, Map options) { + BatchRequest batchQueue = batch == null ? dns.batch() : batch; + try { + listZonesRequest(options).queue(batchQueue, batchCallback); + return batchQueue; + } catch (IOException ex) { + throw translate(ex); + } + } + + private Dns.ManagedZones.List listZonesRequest(Map options) throws IOException { + return dns.managedZones().list(this.options.projectId()) + .setFields(FIELDS.getString(options)) + .setMaxResults(PAGE_SIZE.getInt(options)) + .setDnsName(DNS_NAME.getString(options)) + .setPageToken(PAGE_TOKEN.getString(options)); + } + @Override public ManagedZone create(ManagedZone zone, Map options) throws DnsException { try { @@ -85,12 +113,7 @@ public ManagedZone getZone(String zoneName, Map options) throws DnsEx public ListResult listZones(Map options) throws DnsException { // fields, page token, page size try { - ManagedZonesListResponse zoneList = dns.managedZones().list(this.options.projectId()) - .setFields(FIELDS.getString(options)) - .setMaxResults(PAGE_SIZE.getInt(options)) - .setDnsName(DNS_NAME.getString(options)) - .setPageToken(PAGE_TOKEN.getString(options)) - .execute(); + ManagedZonesListResponse zoneList = listZonesRequest(options).execute(); return of(zoneList.getNextPageToken(), zoneList.getManagedZones()); } catch (IOException ex) { throw translate(ex); diff --git a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/spi/DnsRpc.java b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/spi/DnsRpc.java index bde93b99bfdd..cd482df39145 100644 --- a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/spi/DnsRpc.java +++ b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/spi/DnsRpc.java @@ -16,13 +16,17 @@ package com.google.gcloud.dns.spi; +import com.google.api.client.googleapis.batch.BatchRequest; +import com.google.api.client.googleapis.batch.json.JsonBatchCallback; import com.google.api.services.dns.model.Change; import com.google.api.services.dns.model.ManagedZone; import com.google.api.services.dns.model.Project; import com.google.api.services.dns.model.ResourceRecordSet; import com.google.common.collect.ImmutableList; +import com.google.gcloud.dns.DnsBatch; import com.google.gcloud.dns.DnsException; +import java.io.IOException; import java.util.Map; public interface DnsRpc { @@ -171,4 +175,8 @@ Change getChangeRequest(String zoneName, String changeRequestId, Map */ ListResult listChangeRequests(String zoneName, Map options) throws DnsException; + + BatchRequest enqueueListZones(BatchRequest batch, DnsBatch.Request request, + JsonBatchCallback callback, Map options) throws IOException; + } diff --git a/gcloud-java-dns/src/test/java/com/google/gcloud/dns/DnsImplTest.java b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/DnsImplTest.java index a97c9c408036..ddcf0ecfd056 100644 --- a/gcloud-java-dns/src/test/java/com/google/gcloud/dns/DnsImplTest.java +++ b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/DnsImplTest.java @@ -17,8 +17,12 @@ package com.google.gcloud.dns; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import com.google.api.client.googleapis.batch.BatchRequest; +import com.google.api.client.googleapis.batch.json.JsonBatchCallback; import com.google.api.services.dns.model.Change; import com.google.api.services.dns.model.ManagedZone; import com.google.api.services.dns.model.ResourceRecordSet; @@ -37,6 +41,7 @@ import org.junit.Before; import org.junit.Test; +import java.io.IOException; import java.util.Map; public class DnsImplTest { @@ -373,4 +378,29 @@ public void testListDnsRecordsWithOptions() { .rpcOption()); assertEquals(DNS_RECORD_LIST_OPTIONS[4].value(), type); } + + @Test + public void testBatchInitialization() { + DnsBatch batch = options.service().batch(); + assertNotNull(batch); + assertTrue(batch instanceof DnsBatch); + assertTrue(batch.requests().isEmpty()); + } + + @Test + public void testPrepareBatchListZones() throws IOException { + DnsBatch.Callback> callback = EasyMock.createStrictMock(DnsBatch.Callback.class); + DnsBatch batch = options.service().batch().listZones(callback); + DnsBatch.Request request = batch.requests().keySet().iterator().next(); + Capture jsonCallback = Capture.newInstance(); + Capture> capturedOptions = Capture.newInstance(); + Capture capturedBatch = Capture.newInstance(); + EasyMock.expect(dnsRpcMock.enqueueListZones(EasyMock.capture(capturedBatch), + EasyMock.eq(request), EasyMock.capture(jsonCallback), EasyMock.capture(capturedOptions))) + .andReturn(null); + EasyMock.replay(dnsRpcMock); + DnsImpl dnsImpl = (DnsImpl) options.service(); + dnsImpl.prepareBatch(batch); + assertNull(capturedBatch.getValue()); + } } diff --git a/gcloud-java-dns/src/test/java/com/google/gcloud/dns/it/ITDnsTest.java b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/it/ITDnsTest.java index bfea46dfe039..b687750e5750 100644 --- a/gcloud-java-dns/src/test/java/com/google/gcloud/dns/it/ITDnsTest.java +++ b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/it/ITDnsTest.java @@ -23,10 +23,12 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import com.google.api.client.util.Lists; import com.google.common.collect.ImmutableList; import com.google.gcloud.Page; import com.google.gcloud.dns.ChangeRequest; import com.google.gcloud.dns.Dns; +import com.google.gcloud.dns.DnsBatch; import com.google.gcloud.dns.DnsException; import com.google.gcloud.dns.DnsOptions; import com.google.gcloud.dns.DnsRecord; @@ -34,12 +36,14 @@ import com.google.gcloud.dns.Zone; import com.google.gcloud.dns.ZoneInfo; +import org.easymock.EasyMock; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.Timeout; +import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -952,4 +956,62 @@ public void testListDnsRecords() { clear(); } } + + @Test + public void testListZoneBatch() { + DNS.create(ZONE1); + DNS.create(ZONE_EMPTY_DESCRIPTION); + // the mock is used to check that the callbacks were not skipped + final RuntimeException callbackCalled = EasyMock.createStrictMock(RuntimeException.class); + EasyMock.expect(callbackCalled.initCause(null)).andReturn(callbackCalled); + EasyMock.expect(callbackCalled.initCause(null)).andReturn(callbackCalled); + EasyMock.expect(callbackCalled.initCause(null)).andReturn(callbackCalled); + EasyMock.replay(callbackCalled); + DnsBatch.Callback> callback = new DnsBatch.Callback>() { + @Override + public void success(Page output, DnsBatch.Request request) { + List zones = Lists.newArrayList(DNS.listZones().iterateAll()); + Iterator outputIterator = output.iterateAll(); + while (outputIterator.hasNext()) { + Zone current = outputIterator.next(); + assertTrue(zones.contains(current)); + } + callbackCalled.initCause(null); // to indicated a call + } + + @Override + public void error(DnsException ex, DnsBatch.Request request) { + throw ex; + } + }; + try { + DNS.batch().listZones(callback).submit(); + // with multiple calls including options + final Dns.ZoneListOption options = + Dns.ZoneListOption.fields(Dns.ZoneField.NAME, Dns.ZoneField.ZONE_ID); + DNS.batch().listZones(callback).listZones( + new DnsBatch.Callback>() { + @Override + public void success(Page output, DnsBatch.Request request) { + ArrayList zones = Lists.newArrayList(DNS.listZones(options).iterateAll()); + Iterator outputIterator = output.iterateAll(); + while (outputIterator.hasNext()) { + Zone current = outputIterator.next(); + assertTrue(zones.contains(current)); + } + callbackCalled.initCause(null); // to indicated a call + } + + @Override + public void error(DnsException ex, DnsBatch.Request request) { + throw ex; + } + }, options + ).submit(); + EasyMock.verify(callbackCalled); + } finally { + DNS.delete(ZONE1.name()); + DNS.delete(ZONE_EMPTY_DESCRIPTION.name()); + } + } }