/* * Copyright 2013 the original author or authors. * * 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 org.socialsignin.spring.data.dynamodb.repository.support; import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.socialsignin.spring.data.dynamodb.core.DynamoDBOperations; import org.socialsignin.spring.data.dynamodb.repository.DynamoDBPagingAndSortingRepository; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.util.Assert; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression; import com.amazonaws.services.dynamodbv2.datamodeling.PaginatedScanList; /** * Default implementation of the * {@link org.springframework.data.repository.PagingAndSortingRepository} * interface. * * Due to DynamoDB limitations, sorting is not supported for find-all operations * * Due to DynamoDB limitations, paging for find-all queries is not possible * using an integer page number For paged requests, attempt to approximate * paging behavior by limiting the number of items which will be scanned, and by * returning a sublist of the result-set. * * NB: Number of results scanned for a given page request is proportional to the * page number requested! * * * @author Michael Lavelle * * @param <T> * the type of the entity to handle * @param <ID> * the type of the entity's identifier */ public class SimpleDynamoDBPagingAndSortingRepository<T, ID extends Serializable> extends SimpleDynamoDBCrudRepository<T, ID> implements DynamoDBPagingAndSortingRepository<T, ID> { public SimpleDynamoDBPagingAndSortingRepository(DynamoDBEntityInformation<T, ID> entityInformation, DynamoDBOperations dynamoDBOperations, EnableScanPermissions enableScanPermissions) { super(entityInformation, dynamoDBOperations, enableScanPermissions); } @Override public Iterable<T> findAll(Sort sort) { throw new UnsupportedOperationException("Sorting not supported for find all scan operations"); } @Override public Page<T> findAll(Pageable pageable) { if (pageable.getSort() != null) { throw new UnsupportedOperationException("Sorting not supported for find all scan operations"); } DynamoDBScanExpression scanExpression = new DynamoDBScanExpression(); // Scan to the end of the page after the requested page int scanTo = pageable.getOffset() + (2 * pageable.getPageSize()); scanExpression.setLimit(scanTo); PaginatedScanList<T> paginatedScanList = dynamoDBOperations.scan(domainType, scanExpression); Iterator<T> iterator = paginatedScanList.iterator(); int processedCount = 0; if (pageable.getOffset() > 0) { processedCount = scanThroughResults(iterator, pageable.getOffset()); if (processedCount < pageable.getOffset()) return new PageImpl<T>(new ArrayList<T>()); } // Scan ahead to retrieve the next page count List<T> results = readPageOfResults(iterator, pageable.getPageSize()); assertScanEnabled(enableScanPermissions.isFindAllPaginatedScanEnabled(), "findAll(Pageable pageable)"); assertScanCountEnabled(enableScanPermissions.isFindAllUnpaginatedScanCountEnabled(), "findAll(Pageable pageable)"); int totalCount = dynamoDBOperations.count(domainType, scanExpression); return new PageImpl<T>(results, pageable, totalCount); } private int scanThroughResults(Iterator<T> paginatedScanListIterator, int resultsToScan) { int processed = 0; while (paginatedScanListIterator.hasNext() && processed < resultsToScan) { paginatedScanListIterator.next(); processed++; } return processed; } private List<T> readPageOfResults(Iterator<T> paginatedScanListIterator, int pageSize) { int processed = 0; List<T> resultsPage = new ArrayList<T>(); while (paginatedScanListIterator.hasNext() && processed < pageSize) { resultsPage.add(paginatedScanListIterator.next()); processed++; } return resultsPage; } public void assertScanCountEnabled(boolean countScanEnabled, String methodName) { Assert.isTrue(countScanEnabled, "Scanning for the total counts for unpaginated " + methodName + " queries is not enabled. " + "To enable, re-implement the " + methodName + "() method in your repository interface and annotate with @EnableScanCount, or " + "enable total count scanning for all repository methods by annotating your repository interface with @EnableScanCount"); } }