Java tutorial
/*- * #%L * SDK for CLX XMS * %% * Copyright (C) 2016 CLX Communications * %% * 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. * #L% */ package com.clxcommunications.xms; import java.util.Iterator; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.apache.http.concurrent.FutureCallback; import com.clxcommunications.xms.api.Page; /** * Used for API calls that emits their result over multiple pages. This class * includes convenience methods for interacting with such pages in various ways. * For example, it is possible to retrieve individual pages or to produce an * iterator that seamlessly will iterate over all elements of all pages. * * @param <T> * the element type */ public abstract class PagedFetcher<T> { /** * Synchronously fetches the page having the given page number. * * @param page * the page number (starting from zero) * @return the fetched page * @throws ConcurrentException * if another checked exception occurred during execution * @throws ApiException * if an error occurred while communicating with XMS */ @Nonnull Page<T> fetch(int page) throws InterruptedException, ApiException { try { return fetchAsync(page, null).get(); } catch (ExecutionException e) { throw Utils.unwrapExecutionException(e); } } /** * Asynchronously fetches the page having the given page number. * * @param page * page to fetch (starting from zero) * @param callback * request callback * @return a future providing the requested page */ @Nonnull abstract Future<Page<T>> fetchAsync(int page, @Nullable FutureCallback<Page<T>> callback); /** * Returns an iterable object that traverses all fetched elements across all * associated pages. This is done by iterating over fetched pages and, when * necessary, fetching new pages. * <p> * Since multiple fetches may be necessary to iterate over all batches it is * possible that concurrent changes on the server will cause the same batch * to be iterated over twice. * <p> * ALso, since the returned iterator will perform asynchronous network * traffic it is possible that the {@link Iterator#hasNext()} and * {@link Iterator#next()} methods throws {@link RuntimeException} having as * cause an {@link ExecutionException}. * * @return a non-null iterable * @throws RuntimeException * if the background page fetching failed */ @Nonnull public Iterable<T> elements() { return new Iterable<T>() { @Override public Iterator<T> iterator() { final Iterator<Page<T>> pageIt = pages().iterator(); return new Iterator<T>() { Iterator<T> pageElemIt = pageIt.next().iterator(); @Override public boolean hasNext() { if (!pageElemIt.hasNext()) { if (!pageIt.hasNext()) { return false; } else { pageElemIt = pageIt.next().iterator(); return pageElemIt.hasNext(); } } else { return true; } } @Override public T next() { if (!pageElemIt.hasNext()) { pageElemIt = pageIt.next().iterator(); } return pageElemIt.next(); } }; } }; } /** * Returns an iterable object that fetches and traverses all matching pages. * Each iteration will result in a network fetch. * <p> * This iterator will always yield at least one page, which might be empty. * <p> * Since the returned iterator will perform asynchronous network traffic it * is possible that the {@link Iterator#next()} method throws * {@link RuntimeException} having as cause an {@link ExecutionException}. * * @return a non-null iterable * @throws RuntimeApiException * if the background page fetching failed */ @Nonnull public Iterable<Page<T>> pages() { return new Iterable<Page<T>>() { @Override public Iterator<Page<T>> iterator() { return new Iterator<Page<T>>() { private Page<T> page = null; private int seenElements = 0; @Override public boolean hasNext() { if (page == null) { return true; } else { return seenElements < page.totalSize() && !page.isEmpty(); } } @Override public Page<T> next() { int pageToFetch = (page == null) ? 0 : page.page() + 1; try { page = fetchAsync(pageToFetch, null).get(); } catch (InterruptedException e) { // Interrupt the thread to let upstream code know. Thread.currentThread().interrupt(); } catch (ExecutionException e) { ApiException cause; try { cause = Utils.unwrapExecutionException(e); } catch (ApiException einner) { cause = einner; } throw new RuntimeApiException(cause); } seenElements += page.size(); return page; } }; } }; } }