Merge branch 'TINKERPOP-2412' into 3.4-dev
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 2f5b903..a1d822d 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -30,6 +30,7 @@
* Improved initialization time of Java Driver if the default serializer is replaced.
* Deprecated `withGraph()` in favor of `withEmbedded()` on `AnonymousTraversalSource`.
* Fixed bug in Javascript `Translator` that wasn't handling child traversals well.
+* Implemented `AutoCloseable` on `MultiIterator`.
* Fixed an iterator leak in `HasContainer`.
* Avoid creating unnecessary detached objects in JVM
* Added `Traversal.getTraverserSetSupplier()` to allow providers to supply their own `TraverserSet` instances.
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/iterator/MultiIterator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/iterator/MultiIterator.java
index 20c0946..1f1d4e9 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/iterator/MultiIterator.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/iterator/MultiIterator.java
@@ -19,6 +19,7 @@
package org.apache.tinkerpop.gremlin.util.iterator;
import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException;
+import org.apache.tinkerpop.gremlin.structure.util.CloseableIterator;
import java.io.Serializable;
import java.util.ArrayList;
@@ -28,7 +29,7 @@
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
-public final class MultiIterator<T> implements Iterator<T>, Serializable {
+public final class MultiIterator<T> implements Iterator<T>, Serializable, AutoCloseable {
private final List<Iterator<T>> iterators = new ArrayList<>();
private int current = 0;
@@ -85,4 +86,14 @@
this.current = 0;
}
+ /**
+ * Close the underlying iterators if auto-closeable. Note that when Exception is thrown from any iterator
+ * in the for loop on closing, remaining iterators possibly left unclosed.
+ */
+ @Override
+ public void close() {
+ for (Iterator<T> iterator : this.iterators) {
+ CloseableIterator.closeIterator(iterator);
+ }
+ }
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/iterator/MultiIteratorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/iterator/MultiIteratorTest.java
index a52d124..4b97299 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/iterator/MultiIteratorTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/iterator/MultiIteratorTest.java
@@ -20,7 +20,6 @@
import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException;
import org.junit.Test;
-
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -29,6 +28,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
/**
* @author Stephen Mallette (http://stephen.genoprime.com)
@@ -112,4 +112,47 @@
assertFalse(itty.hasNext());
}
+
+ @Test
+ public void shouldCloseIterators() {
+
+ final MultiIterator<String> itty = new MultiIterator<>();
+ final DummyAutoCloseableIterator<String> inner1 = new DummyAutoCloseableIterator<>();
+ final DummyAutoCloseableIterator<String> inner2 = new DummyAutoCloseableIterator<>();
+ itty.addIterator(inner1);
+ itty.addIterator(inner2);
+
+ itty.close();
+
+ assertTrue(inner1.isClosed());
+ assertTrue(inner2.isClosed());
+ }
+
+ // Dummy iterator to verify that its close method is called in the test.
+ private static class DummyAutoCloseableIterator<T> implements Iterator<T>, AutoCloseable {
+ private boolean closed;
+
+ public DummyAutoCloseableIterator() {
+ closed = false;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return false;
+ }
+
+ @Override
+ public T next() {
+ return null;
+ }
+
+ @Override
+ public void close() throws Exception {
+ closed = true;
+ }
+
+ public boolean isClosed() {
+ return closed;
+ }
+ }
}