Skip to content

Commit

Permalink
Add AsyncPage implementation and docs. Add related tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mziccard committed May 2, 2016
1 parent 0cb8e3e commit 3bc602b
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 0 deletions.
28 changes: 28 additions & 0 deletions gcloud-java-core/src/main/java/com/google/cloud/AsyncPage.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,34 @@

import java.util.concurrent.Future;

/**
* Interface for asynchronously consuming Google Cloud paginated results.
*
* <p>Use {@code AsyncPage} to iterate through all values (also in next pages):
* <pre> {@code
* AsyncPage<T> page = ...; // get an AsyncPage<T> instance
* Iterator<T> iterator = page.iterateAll();
* while (iterator.hasNext()) {
* T value = iterator.next();
* // do something with value
* }}</pre>
*
* <p>Or handle pagination explicitly:
* <pre> {@code
* AsyncPage<T> page = ...; // get a AsyncPage<T> instance
* while (page != null) {
* for (T value : page.values()) {
* // do something with value
* }
* page = page.nextPage().get();
* }}</pre>
*
* @param <T> the value type that the page holds
*/
public interface AsyncPage<T> extends Page<T> {

/**
* Returns a {@link Future} object for the next page.
*/
Future<AsyncPage<T>> nextPageAsync();
}
82 changes: 82 additions & 0 deletions gcloud-java-core/src/main/java/com/google/cloud/AsyncPageImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* 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.cloud;

import com.google.common.base.Throwables;
import com.google.common.util.concurrent.Uninterruptibles;

import java.io.Serializable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

/**
* Base implementation for asynchronously consuming Google Cloud paginated results.
*
* @param <T> the value type that the page holds
*/
public class AsyncPageImpl<T> extends PageImpl<T> implements AsyncPage<T> {

private static final long serialVersionUID = -6009473188630364906L;

private NextPageFetcher<T> asyncPageFetcher;

/**
* Interface for asynchronously fetching the next page of results from the service.
*
* @param <T> the value type that the page holds
*/
public interface NextPageFetcher<T> extends Serializable {
Future<AsyncPage<T>> nextPage();
}

private static class SyncNextPageFetcher<T> implements PageImpl.NextPageFetcher<T> {

private static final long serialVersionUID = -4124568632363525351L;

private NextPageFetcher<T> asyncPageFetcher;

private SyncNextPageFetcher(NextPageFetcher<T> asyncPageFetcher) {
this.asyncPageFetcher = asyncPageFetcher;
}

@Override
public Page<T> nextPage() {
try {
return asyncPageFetcher != null
? Uninterruptibles.getUninterruptibly(asyncPageFetcher.nextPage()) : null;
} catch (ExecutionException ex) {
throw Throwables.propagate(ex.getCause());
}
}
}

/**
* Creates an {@code AsyncPageImpl} object.
*/
public AsyncPageImpl(NextPageFetcher<T> asyncPageFetcher, String cursor, Iterable<T> results) {
super(new SyncNextPageFetcher<T>(asyncPageFetcher), cursor, results);
this.asyncPageFetcher = asyncPageFetcher;
}

@Override
public Future<AsyncPage<T>> nextPageAsync() {
if (nextPageCursor() == null || asyncPageFetcher == null) {
return null;
}
return asyncPageFetcher.nextPage();
}
}
128 changes: 128 additions & 0 deletions gcloud-java-core/src/test/java/com/google/cloud/AsyncPageImplTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* 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.cloud;

import static org.junit.Assert.assertEquals;

import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.Futures;

import org.junit.Test;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

public class AsyncPageImplTest {

private static final ImmutableList<String> VALUES1 = ImmutableList.of("1", "2");
private static final ImmutableList<String> VALUES2 = ImmutableList.of("3", "4");
private static final ImmutableList<String> VALUES3 = ImmutableList.of("5", "6");
private static final ImmutableList<String> ALL_VALUES = ImmutableList.<String>builder()
.addAll(VALUES1)
.addAll(VALUES2)
.addAll(VALUES3)
.build();
private static final ImmutableList<String> SOME_VALUES = ImmutableList.<String>builder()
.addAll(VALUES2)
.addAll(VALUES3)
.build();

@Test
public void testPage() {
final AsyncPageImpl<String> nextResult = new AsyncPageImpl<>(null, "c", VALUES2);
AsyncPageImpl.NextPageFetcher<String> fetcher = new AsyncPageImpl.NextPageFetcher<String>() {
private static final long serialVersionUID = 4703765400378593176L;

@Override
public Future<AsyncPage<String>> nextPage() {
return Futures.<AsyncPage<String>>immediateFuture(nextResult);
}
};
AsyncPageImpl<String> result = new AsyncPageImpl<>(fetcher, "c", VALUES1);
assertEquals(nextResult, result.nextPage());
assertEquals("c", result.nextPageCursor());
assertEquals(VALUES1, result.values());
}

@Test
public void testPageAsync() throws ExecutionException, InterruptedException {
final AsyncPageImpl<String> nextResult = new AsyncPageImpl<>(null, "c", VALUES2);
AsyncPageImpl.NextPageFetcher<String> fetcher = new AsyncPageImpl.NextPageFetcher<String>() {
private static final long serialVersionUID = 4703765400378593176L;

@Override
public Future<AsyncPage<String>> nextPage() {
return Futures.<AsyncPage<String>>immediateFuture(nextResult);
}
};
AsyncPageImpl<String> result = new AsyncPageImpl<>(fetcher, "c", VALUES1);
assertEquals(nextResult, result.nextPageAsync().get());
assertEquals("c", result.nextPageCursor());
assertEquals(VALUES1, result.values());
}

@Test
public void testIterateAll() {
final AsyncPageImpl<String> nextResult2 = new AsyncPageImpl<>(null, "c3", VALUES3);
AsyncPageImpl.NextPageFetcher<String> fetcher2 = new AsyncPageImpl.NextPageFetcher<String>() {
private static final long serialVersionUID = -9203621430631884026L;

@Override
public Future<AsyncPage<String>> nextPage() {
return Futures.<AsyncPage<String>>immediateFuture(nextResult2);
}
};
final AsyncPageImpl<String> nextResult1 = new AsyncPageImpl<>(fetcher2, "c2", VALUES2);
AsyncPageImpl.NextPageFetcher<String> fetcher1 = new AsyncPageImpl.NextPageFetcher<String>() {
private static final long serialVersionUID = -9203621430631884026L;

@Override
public Future<AsyncPage<String>> nextPage() {
return Futures.<AsyncPage<String>>immediateFuture(nextResult1);
}
};
AsyncPageImpl<String> result = new AsyncPageImpl<>(fetcher1, "c1", VALUES1);
assertEquals(ALL_VALUES, ImmutableList.copyOf(result.iterateAll()));
}

@Test
public void testAsyncPageAndIterateAll() throws ExecutionException, InterruptedException {
final AsyncPageImpl<String> nextResult2 = new AsyncPageImpl<>(null, "c3", VALUES3);
AsyncPageImpl.NextPageFetcher<String> fetcher2 = new AsyncPageImpl.NextPageFetcher<String>() {
private static final long serialVersionUID = -9203621430631884026L;

@Override
public Future<AsyncPage<String>> nextPage() {
return Futures.<AsyncPage<String>>immediateFuture(nextResult2);
}
};
final AsyncPageImpl<String> nextResult1 = new AsyncPageImpl<>(fetcher2, "c2", VALUES2);
AsyncPageImpl.NextPageFetcher<String> fetcher1 = new AsyncPageImpl.NextPageFetcher<String>() {
private static final long serialVersionUID = -9203621430631884026L;

@Override
public Future<AsyncPage<String>> nextPage() {
return Futures.<AsyncPage<String>>immediateFuture(nextResult1);
}
};
AsyncPageImpl<String> result = new AsyncPageImpl<>(fetcher1, "c1", VALUES1);
assertEquals(nextResult1, result.nextPageAsync().get());
assertEquals("c1", result.nextPageCursor());
assertEquals(VALUES1, result.values());
assertEquals(SOME_VALUES, ImmutableList.copyOf(result.nextPageAsync().get().iterateAll()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public class PageImplTest {
public void testPage() {
final PageImpl<String> nextResult = new PageImpl<>(null, "c", NEXT_VALUES);
PageImpl.NextPageFetcher<String> fetcher = new PageImpl.NextPageFetcher<String>() {
private static final long serialVersionUID = -1714571149183431798L;

@Override
public PageImpl<String> nextPage() {
return nextResult;
Expand Down

0 comments on commit 3bc602b

Please sign in to comment.