package org.infinispan.client.hotrod.impl.iteration; import java.util.LinkedList; import java.util.Map.Entry; import java.util.NoSuchElementException; import java.util.Queue; import java.util.Set; import org.infinispan.client.hotrod.exceptions.HotRodClientException; import org.infinispan.client.hotrod.exceptions.RemoteIllegalLifecycleStateException; import org.infinispan.client.hotrod.exceptions.TransportException; import org.infinispan.client.hotrod.impl.operations.IterationEndResponse; import org.infinispan.client.hotrod.impl.operations.IterationNextOperation; import org.infinispan.client.hotrod.impl.operations.IterationNextResponse; import org.infinispan.client.hotrod.impl.operations.IterationStartOperation; import org.infinispan.client.hotrod.impl.operations.IterationStartResponse; import org.infinispan.client.hotrod.impl.operations.OperationsFactory; import org.infinispan.client.hotrod.impl.protocol.HotRodConstants; import org.infinispan.client.hotrod.impl.transport.Transport; import org.infinispan.client.hotrod.logging.Log; import org.infinispan.client.hotrod.logging.LogFactory; import org.infinispan.commons.marshall.Marshaller; import org.infinispan.commons.util.CloseableIterator; import net.jcip.annotations.NotThreadSafe; /** * @author gustavonalle * @since 8.0 */ @NotThreadSafe public class RemoteCloseableIterator<E> implements CloseableIterator<Entry<Object, E>> { private static final Log log = LogFactory.getLog(RemoteCloseableIterator.class); private final OperationsFactory operationsFactory; private final String filterConverterFactory; private final byte[][] filterParams; private final Set<Integer> segments; private final int batchSize; private final boolean metadata; private KeyTracker segmentKeyTracker; private Transport transport; private String iterationId; private boolean endOfIteration = false; private boolean closed; private Queue<Entry<Object, E>> nextElements = new LinkedList<>(); public RemoteCloseableIterator(OperationsFactory operationsFactory, String filterConverterFactory, byte[][] filterParams, Set<Integer> segments, int batchSize, boolean metadata) { this.filterConverterFactory = filterConverterFactory; this.filterParams = filterParams; this.segments = segments; this.batchSize = batchSize; this.operationsFactory = operationsFactory; this.metadata = metadata; } public RemoteCloseableIterator(OperationsFactory operationsFactory, int batchSize, Set<Integer> segments, boolean metadata) { this(operationsFactory, null, null, segments, batchSize, metadata); } @Override public void close() { if (!closed) { try { IterationEndResponse endResponse = operationsFactory.newIterationEndOperation(iterationId, transport).execute(); short status = endResponse.getStatus(); if (HotRodConstants.isSuccess(status)) { log.iterationClosed(iterationId); } if (HotRodConstants.isInvalidIteration(status)) { throw log.errorClosingIteration(iterationId); } } catch (HotRodClientException e) { log.ignoringErrorDuringIterationClose(iterationId, e); } finally { closed = true; } } } @Override public boolean hasNext() { if (!endOfIteration && nextElements.isEmpty()) { fetch(); } return !endOfIteration; } @Override public Entry<Object, E> next() { if (!hasNext()) throw new NoSuchElementException(); return nextElements.remove(); } private void fetch() { try { IterationNextOperation<E> iterationNextOperation = operationsFactory.newIterationNextOperation(iterationId, transport, segmentKeyTracker); while (nextElements.isEmpty() && !endOfIteration) { IterationNextResponse<E> iterationNextResponse = iterationNextOperation.execute(); if (!iterationNextResponse.hasMore()) { endOfIteration = true; break; } nextElements.addAll(iterationNextResponse.getEntries()); } } catch (TransportException | RemoteIllegalLifecycleStateException e) { log.warnf(e, "Error reaching the server during iteration"); startInternal(segmentKeyTracker.missedSegments()); fetch(); } } private IterationStartResponse startInternal(Set<Integer> segments) { if (log.isDebugEnabled()) { log.debugf("Starting iteration with segments %s", segments); } IterationStartOperation iterationStartOperation = operationsFactory.newIterationStartOperation(filterConverterFactory, filterParams, segments, batchSize, metadata); IterationStartResponse startResponse = iterationStartOperation.execute(); this.transport = startResponse.getTransport(); if (log.isDebugEnabled()) { log.iterationTransportObtained(transport, iterationId); } this.iterationId = startResponse.getIterationId(); if (log.isDebugEnabled()) { log.startedIteration(iterationId); } return startResponse; } public void start() { IterationStartResponse startResponse = startInternal(segments); Marshaller marshaller = startResponse.getTransport().getTransportFactory().getMarshaller(); this.segmentKeyTracker = KeyTrackerFactory.create( marshaller, startResponse.getSegmentConsistentHash(), startResponse.getTopologyId(), segments); } }