/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.usergrid.corepersistence.results; import java.util.Iterator; import java.util.NoSuchElementException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.usergrid.corepersistence.pipeline.read.ResultsPage; import org.apache.usergrid.persistence.Results; import com.google.common.base.Optional; import rx.Observable; /** * Our proxy to allow us to subscribe to observable results, then return them as an iterator. A bridge for 2.0 -> 1.0 * code. This should not be used on any new code, and will eventually be deleted */ @Deprecated//Required for 1.0 compatibility public abstract class ObservableQueryExecutor<T> implements QueryExecutor { private static final Logger logger = LoggerFactory.getLogger( ObservableQueryExecutor.class ); private Results results; private Optional<String> cursor; private boolean complete; protected ObservableQueryExecutor(final Optional<String> startCursor){ this.cursor = startCursor; } /** * Transform the results * @param resultsPage * @return */ protected abstract Results createResults( final ResultsPage<T> resultsPage ); /** * Build new results page from the cursor * @param cursor * @return */ protected abstract Observable<ResultsPage<T>> buildNewResultsPage(final Optional<String> cursor); /** * Legacy to transform our results page to a new results * @param resultsPage * @return */ private Results createResultsInternal( final ResultsPage<T> resultsPage ) { final Results results = createResults( resultsPage ); //add the cursor if our limit is the same if ( resultsPage.hasMoreResults() ) { final Optional<String> cursor = resultsPage.getResponseCursor().encodeAsString(); if ( cursor.isPresent() ) { results.setCursor( cursor.get() ); } } return results; } @Override public Iterator<Results> iterator() { return this; } @Override public boolean hasNext() { if ( !complete && results == null) { if(logger.isTraceEnabled()){ logger.trace("Iterator not complete and there are results object is null, advancing"); } advance(); } return results != null; } @Override public Results next() { if ( !hasNext() ) { throw new NoSuchElementException( "No more results present" ); } final Results next = results; results = null; next.setQueryExecutor( this ); return next; } private void advance(){ //map to our old results objects, return a default empty if required final Observable<Results> observable = buildNewResultsPage( cursor ).map( resultsPage -> createResultsInternal( resultsPage ) ).defaultIfEmpty( new Results() ); if (logger.isTraceEnabled()) { logger.trace("Trying to load results page"); } //take the first from our observable final Results resultsPage = observable.take(1).toBlocking().first(); if (logger.isTraceEnabled()) { logger.trace("Results page loaded {}", resultsPage); } //set the results for the iterator this.results = resultsPage; //set the complete flag this.complete = !resultsPage.hasCursor(); //if not comlete, set our cursor for the next iteration if(!complete){ this.cursor = Optional.of( results.getCursor()); }else{ this.cursor = Optional.absent(); } } }