All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.amazonaws.services.dynamodbv2.datamodeling.PaginatedList Maven / Gradle / Ivy

Go to download

The Amazon Web Services SDK for Java provides Java APIs for building software on AWS' cost-effective, scalable, and reliable infrastructure products. The AWS Java SDK allows developers to code against APIs for all of Amazon's infrastructure web services (Amazon S3, Amazon EC2, Amazon SQS, Amazon Relational Database Service, Amazon AutoScaling, etc).

The newest version!
/*
 * Copyright 2011-2014 Amazon Technologies, Inc.
 *
 * 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://aws.amazon.com/apache2.0
 *
 * This file 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.amazonaws.services.dynamodbv2.datamodeling;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig.PaginationLoadingStrategy;

/**
 * Unmodifiable list supporting paginated result sets from Amazon DynamoDB.
 * 

* Pages of results are fetched lazily from DynamoDB as they are needed. Some * methods, such as {@link PaginatedList#size()} and * {@link PaginatedList#toArray()}, require fetching the entire result set * eagerly. See the javadoc of individual methods for details on which are lazy. * * @param * The domain object type stored in this list. */ public abstract class PaginatedList implements List { private static final String UNMODIFIABLE_MESSAGE = "This is an unmodifiable list"; private static final String ITERATION_ONLY_UNSUPPORTED_OPERATION_MESSAGE = " is not supported when using ITERATION_ONLY configuration."; /** * Reference to the DynamoDB mapper for marshalling DynamoDB attributes back * into objects */ protected final DynamoDBMapper mapper; /** * The class annotated with DynamoDB tags declaring how to load/store * objects into DynamoDB */ protected final Class clazz; /** The client for working with DynamoDB */ protected final AmazonDynamoDB dynamo; /** Tracks if all results have been loaded yet or not */ protected boolean allResultsLoaded = false; /** * All currently loaded results for this list. * * In ITERATION_ONLY mode, this list will at most keep one page of the * loaded results, and all previous results will be cleared from the memory. */ protected final List allResults; /** Lazily loaded next results waiting to be added into allResults */ protected final List nextResults = new LinkedList(); /** The pagination loading strategy for this paginated list **/ private final PaginationLoadingStrategy paginationLoadingStrategy; /** * Keeps track on whether an iterator of the list has been retrieved. * Only updated and checked when the list is in ITERATION_ONLY mode. */ private boolean iterationStarted = false; /** * Constructs a PaginatedList instance using the default PaginationLoadingStrategy */ public PaginatedList(DynamoDBMapper mapper, Class clazz, AmazonDynamoDB dynamo) { this(mapper, clazz, dynamo, null); } /** * Constructs a PaginatedList instance. * * @param mapper * The mapper for marshalling DynamoDB attributes into objects. * @param clazz * The class of the annotated model. * @param dynamo * The DynamoDB client for making low-level request calls. * @param paginationLoadingStrategy * The strategy used for loading paginated results. Caller has to * explicitly set this parameter, since the DynamoDBMapperConfig * set in the mapper is not accessible here. If null value is * provided, LAZY_LOADING will be set by default. */ public PaginatedList(DynamoDBMapper mapper, Class clazz, AmazonDynamoDB dynamo, PaginationLoadingStrategy paginationLoadingStrategy) { this.mapper = mapper; this.clazz = clazz; this.dynamo = dynamo; this.paginationLoadingStrategy = paginationLoadingStrategy == null ? PaginationLoadingStrategy.LAZY_LOADING : paginationLoadingStrategy; this.allResults = new ArrayList(); // Ideally, we should eagerly load all results here as soon as EAGER_LOADING is configured. // But the implementation of loadAllResults() relies on a fully initialized sub-class object. // So we have to do this in each sub-class constructor. } /** * Eagerly loads all results for this list. *

* Not supported in ITERATION_ONLY mode. *

*/ public synchronized void loadAllResults() { checkUnsupportedOperationForIterationOnlyMode("loadAllResults()"); if ( allResultsLoaded ) return; while ( nextResultsAvailable() ) { // Keep all loaded results moveNextResults(false); } allResultsLoaded = true; } /** * Returns whether there are more results available not yet included in the * allResults member field. These could already have been fetched and are * sitting in the nextResults buffer, or they could be fetched from the * service opportunistically at the time this method is called. A return * value of true guarantees that nextResults is non-empty. */ private boolean nextResultsAvailable() { return !nextResults.isEmpty() || loadNextResults(); } /** * Attempts to load the next batch of results, if there are any, into the * nextResults buffer. Returns whether there were any results to load. A * return value of true guarantees that nextResults had items added to it. */ private synchronized boolean loadNextResults() { if ( atEndOfResults() ) return false; do { nextResults.addAll(fetchNextPage()); } while ( !atEndOfResults() && nextResults.isEmpty() ); return !nextResults.isEmpty(); } /** * Moves the contents of the nextResults buffer into allResults and resets * the buffer. * * @param clearPreviousResults * Whether it should clear previous results in allResults field. */ private void moveNextResults(boolean clearPreviousResults) { if (clearPreviousResults) { allResults.clear(); } allResults.addAll(nextResults); nextResults.clear(); } /** * Fetches the next page of results (which may be empty) and returns any * items found. */ protected abstract List fetchNextPage(); /** * Returns whether we have reached the end of the result set. */ protected abstract boolean atEndOfResults(); /** * Returns an iterator over this list that lazily initializes results as * necessary. *

* If it configured with ITERARTION_ONLY mode, then the iterator * could be only retrieved once, and any previously loaded results will be * cleared in the memory during the iteration. *

*/ @Override public Iterator iterator() { return new PaginatedListIterator(paginationLoadingStrategy == PaginationLoadingStrategy.ITERATION_ONLY); } private class PaginatedListIterator implements Iterator { /** * Whether this iterator is constructed by a PaginatedList in * ITERATION_ONLY mode. */ private final boolean iterationOnly; /** * A hard copy of the allResults list to prevent * ConcurrentModificationExceptions. * Only needed when the list is not in ITERNATION_ONLY mode. */ private final List allResultsCopy; private Iterator innerIterator; private int pos = 0; public PaginatedListIterator(boolean iterationOnly) { this.iterationOnly = iterationOnly; if (iterationOnly) { synchronized (PaginatedList.this) { if (iterationStarted) { throw new UnsupportedOperationException("The list could only be iterated once in ITERATION_ONLY mode."); } iterationStarted = true; } allResultsCopy = null; // not needed for ITERATION_ONLY mode innerIterator = allResults.iterator(); } else { /* * We make a copy of the allResults list to iterate over in order to * avoid ConcurrentModificationExceptions caused by other methods * loading more results into the list while someone iterates over it. * This is a more severe problem than it might seem, because even * innocuous-seeming operations such as contains() can modify the * underlying result set. */ allResultsCopy = new ArrayList(); allResultsCopy.addAll(allResults); innerIterator = allResultsCopy.iterator(); } } @Override public boolean hasNext() { return innerIterator.hasNext() || nextResultsAvailable(); } @Override public T next() { if ( !innerIterator.hasNext() ) { /* * We need to immediately fetch more results from the service, * if * -- it's in ITERATION_ONLY mode (which means innerIterator * is always pointing at the "real" list of loaded results) * OR it's not in ITERATION_ONLY and our private copy of the * result list is already up to date with the full one. */ if ( iterationOnly || allResults.size() == allResultsCopy.size() ) { if ( !nextResultsAvailable() ) { throw new NoSuchElementException(); } /* Clear previous results if it's in ITERATION_ONLY mode */ boolean clearPreviousResults = iterationOnly; moveNextResults(clearPreviousResults); } if ( iterationOnly ) { /* * allResults has been replaced with the latest page of results. */ innerIterator = allResults.iterator(); } else { /* * Update our private results copy, and then update the inner iterator */ if ( allResults.size() > allResultsCopy.size() ) allResultsCopy.addAll(allResults.subList(allResultsCopy.size(), allResults.size())); innerIterator = allResultsCopy.listIterator(pos); } } pos++; return innerIterator.next(); } @Override public void remove() { throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); } } /** * Returns whether the collection is empty. At most one (non-empty) page of * results is loaded to make the check. *

* Not supported in ITERATION_ONLY mode. *

*/ @Override public boolean isEmpty() { checkUnsupportedOperationForIterationOnlyMode("isEmpty()"); return !iterator().hasNext(); } /** * Returns the Nth element of the list. Results are loaded until N elements * are present, if necessary. *

* Not supported in ITERATION_ONLY mode. *

*/ @Override public T get(int n) { checkUnsupportedOperationForIterationOnlyMode("get(int n)"); while ( allResults.size() <= n && nextResultsAvailable() ) { moveNextResults(false); } return allResults.get(n); } /** * Returns whether the collection contains the given element. Results are * loaded and checked incrementally until a match is found or the end of the * result set is reached. *

* Not supported in ITERATION_ONLY mode. *

*/ @Override public boolean contains(Object arg0) { checkUnsupportedOperationForIterationOnlyMode("contains(Object arg0)"); if ( allResults.contains(arg0) ) return true; while ( nextResultsAvailable() ) { boolean found = nextResults.contains(arg0); moveNextResults(false); if ( found ) return true; } return false; } /** * Returns a sub-list in the range specified, loading more results as * necessary. *

* Not supported in ITERATION_ONLY mode. *

*/ @Override public List subList(int arg0, int arg1) { checkUnsupportedOperationForIterationOnlyMode("subList(int arg0, int arg1)"); while ( allResults.size() < arg1 && nextResultsAvailable() ) { moveNextResults(false); } return Collections.unmodifiableList(allResults.subList(arg0, arg1)); } /** * Returns the first index of the object given in the list. Additional * results are loaded incrementally as necessary. *

* Not supported in ITERATION_ONLY mode. *

*/ @Override public int indexOf(Object arg0) { checkUnsupportedOperationForIterationOnlyMode("indexOf(Object org0)"); int indexOf = allResults.indexOf(arg0); if ( indexOf >= 0 ) return indexOf; while ( nextResultsAvailable() ) { indexOf = nextResults.indexOf(arg0); int size = allResults.size(); moveNextResults(false); if ( indexOf >= 0 ) return indexOf + size; } return -1; } // Operations requiring the entire result set @Override public int size() { loadAllResults(); return allResults.size(); } @Override public boolean containsAll(Collection arg0) { loadAllResults(); return allResults.containsAll(arg0); } @Override public int lastIndexOf(Object arg0) { loadAllResults(); return allResults.lastIndexOf(arg0); } @Override public Object[] toArray() { loadAllResults(); return allResults.toArray(); } @Override public X[] toArray(X[] a) { loadAllResults(); return allResults.toArray(a); } // Unsupported Operations @Override public ListIterator listIterator() { throw new UnsupportedOperationException("ListIterators are not supported for this list"); } @Override public ListIterator listIterator(int arg0) { throw new UnsupportedOperationException("ListIterators are not supported for this list"); } @Override public boolean remove(Object arg0) { throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); } @Override public T remove(int arg0) { throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); } @Override public boolean removeAll(Collection arg0) { throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); } @Override public boolean retainAll(Collection arg0) { throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); } @Override public T set(int arg0, T arg1) { throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); } @Override public boolean add(T arg0) { throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); } @Override public void add(int arg0, T arg1) { throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); } @Override public boolean addAll(Collection arg0) { throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); } @Override public boolean addAll(int arg0, Collection arg1) { throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); } @Override public void clear() { throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); } private void checkUnsupportedOperationForIterationOnlyMode(String methodSignature) { if (this.paginationLoadingStrategy == PaginationLoadingStrategy.ITERATION_ONLY) { throw new UnsupportedOperationException(methodSignature + ITERATION_ONLY_UNSUPPORTED_OPERATION_MESSAGE); } }; }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy