You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@asterixdb.apache.org by am...@apache.org on 2018/02/13 00:09:55 UTC

[6/7] asterixdb git commit: [ASTERIXDB-2204][STO] Fix implementations and usages of IIndexCursor

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-examples/hyracks-integration-tests/src/test/java/org/apache/hyracks/tests/unit/IIndexCursorTest.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-examples/hyracks-integration-tests/src/test/java/org/apache/hyracks/tests/unit/IIndexCursorTest.java b/hyracks-fullstack/hyracks/hyracks-examples/hyracks-integration-tests/src/test/java/org/apache/hyracks/tests/unit/IIndexCursorTest.java
deleted file mode 100644
index 4d0f287..0000000
--- a/hyracks-fullstack/hyracks/hyracks-examples/hyracks-integration-tests/src/test/java/org/apache/hyracks/tests/unit/IIndexCursorTest.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * 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.hyracks.tests.unit;
-
-import org.apache.hyracks.storage.common.ICursorInitialState;
-import org.apache.hyracks.storage.common.IIndexCursor;
-import org.apache.hyracks.storage.common.ISearchPredicate;
-import org.junit.Assert;
-import org.junit.Test;
-
-import java.util.List;
-
-/**
- * This is a test class that forms the basis for unit tests of different implementations of the IIndexCursor interface
- */
-public abstract class IIndexCursorTest {
-    @Test
-    public void testNormalLifeCycle() throws Exception {
-        IIndexCursor cursor = createCursor();
-        ICursorInitialState initialState = createCursorInitialState();
-        List<ISearchPredicate> predicates = createSearchPredicates();
-        for (ISearchPredicate predicate : predicates) {
-            cursor.open(initialState, predicate);
-            while (cursor.hasNext()) {
-                cursor.next();
-            }
-            cursor.close();
-        }
-        cursor.destroy();
-    }
-
-    @Test
-    public void testCreateDestroySucceed() throws Exception {
-        IIndexCursor cursor = createCursor();
-        cursor.destroy();
-    }
-
-    @Test
-    public void testDoubleOpenFails() throws Exception {
-        IIndexCursor cursor = createCursor();
-        ICursorInitialState initialState = createCursorInitialState();
-        List<ISearchPredicate> predicates = createSearchPredicates();
-        cursor.open(initialState, predicates.get(0));
-        boolean expectedExceptionThrown = false;
-        try {
-            cursor.open(initialState, predicates.get(0));
-        } catch (Exception e) {
-            expectedExceptionThrown = true;
-        }
-        cursor.close();
-        cursor.destroy();
-        Assert.assertTrue(expectedExceptionThrown);
-    }
-
-    @Test
-    public void testCloseWithoutOpenFails() throws Exception {
-        IIndexCursor cursor = createCursor();
-        boolean expectedExceptionThrown = false;
-        try {
-            cursor.close();
-        } catch (Exception e) {
-            expectedExceptionThrown = true;
-        }
-        cursor.destroy();
-        Assert.assertTrue(expectedExceptionThrown);
-    }
-
-    @Test
-    public void testDoubleCloseFails() throws Exception {
-        IIndexCursor cursor = createCursor();
-        ICursorInitialState initialState = createCursorInitialState();
-        List<ISearchPredicate> predicates = createSearchPredicates();
-        cursor.open(initialState, predicates.get(0));
-        cursor.close();
-        boolean expectedExceptionThrown = false;
-        try {
-            cursor.close();
-        } catch (Exception e) {
-            expectedExceptionThrown = true;
-        }
-        cursor.destroy();
-        Assert.assertTrue(expectedExceptionThrown);
-    }
-
-    @Test
-    public void testHasNextBeforeOpenFails() throws Exception {
-        IIndexCursor cursor = createCursor();
-        boolean expectedExceptionThrown = false;
-        try {
-            cursor.hasNext();
-        } catch (Exception e) {
-            expectedExceptionThrown = true;
-        }
-        cursor.destroy();
-        Assert.assertTrue(expectedExceptionThrown);
-    }
-
-    @Test
-    public void testHasNextAfterCloseFails() throws Exception {
-        IIndexCursor cursor = createCursor();
-        ICursorInitialState initialState = createCursorInitialState();
-        List<ISearchPredicate> predicates = createSearchPredicates();
-        cursor.open(initialState, predicates.get(0));
-        cursor.close();
-        boolean expectedExceptionThrown = false;
-        try {
-            cursor.hasNext();
-        } catch (Exception e) {
-            expectedExceptionThrown = true;
-        }
-        cursor.destroy();
-        Assert.assertTrue(expectedExceptionThrown);
-    }
-
-    @Test
-    public void testNextBeforeOpenFails() throws Exception {
-        IIndexCursor cursor = createCursor();
-        boolean expectedExceptionThrown = false;
-        try {
-            cursor.next();
-        } catch (Exception e) {
-            expectedExceptionThrown = true;
-        }
-        cursor.destroy();
-        Assert.assertTrue(expectedExceptionThrown);
-    }
-
-    @Test
-    public void testNextAfterCloseFails() throws Exception {
-        IIndexCursor cursor = createCursor();
-        ICursorInitialState initialState = createCursorInitialState();
-        List<ISearchPredicate> predicates = createSearchPredicates();
-        cursor.open(initialState, predicates.get(0));
-        cursor.close();
-        boolean expectedExceptionThrown = false;
-        try {
-            cursor.next();
-        } catch (Exception e) {
-            expectedExceptionThrown = true;
-        }
-        cursor.destroy();
-        Assert.assertTrue(expectedExceptionThrown);
-    }
-
-    @Test
-    public void testDestroyWhileOpenFails() throws Exception {
-        IIndexCursor cursor = createCursor();
-        ICursorInitialState initialState = createCursorInitialState();
-        List<ISearchPredicate> predicates = createSearchPredicates();
-        cursor.open(initialState, predicates.get(0));
-        boolean expectedExceptionThrown = false;
-        try {
-            cursor.destroy();
-        } catch (Exception e) {
-            expectedExceptionThrown = true;
-        }
-        cursor.close();
-        cursor.destroy();
-        Assert.assertTrue(expectedExceptionThrown);
-    }
-
-    @Test
-    public void testOpenAfterDestroyFails() throws Exception {
-        IIndexCursor cursor = createCursor();
-        ICursorInitialState initialState = createCursorInitialState();
-        List<ISearchPredicate> predicates = createSearchPredicates();
-        cursor.open(initialState, predicates.get(0));
-        cursor.close();
-        cursor.destroy();
-        boolean expectedExceptionThrown = false;
-        try {
-            cursor.open(initialState, predicates.get(0));
-        } catch (Exception e) {
-            expectedExceptionThrown = true;
-        }
-        Assert.assertTrue(expectedExceptionThrown);
-    }
-
-    @Test
-    public void testCloseAfterDestroyFails() throws Exception {
-        IIndexCursor cursor = createCursor();
-        ICursorInitialState initialState = createCursorInitialState();
-        List<ISearchPredicate> predicates = createSearchPredicates();
-        cursor.open(initialState, predicates.get(0));
-        cursor.close();
-        cursor.destroy();
-        boolean expectedExceptionThrown = false;
-        try {
-            cursor.close();
-        } catch (Exception e) {
-            expectedExceptionThrown = true;
-        }
-        Assert.assertTrue(expectedExceptionThrown);
-    }
-
-    @Test
-    public void testNextAfterDestroyFails() throws Exception {
-        IIndexCursor cursor = createCursor();
-        ICursorInitialState initialState = createCursorInitialState();
-        List<ISearchPredicate> predicates = createSearchPredicates();
-        cursor.open(initialState, predicates.get(0));
-        cursor.close();
-        cursor.destroy();
-        boolean expectedExceptionThrown = false;
-        try {
-            cursor.next();
-        } catch (Exception e) {
-            expectedExceptionThrown = true;
-        }
-        Assert.assertTrue(expectedExceptionThrown);
-    }
-
-    @Test
-    public void testHasNextAfterDestroyFails() throws Exception {
-        IIndexCursor cursor = createCursor();
-        ICursorInitialState initialState = createCursorInitialState();
-        List<ISearchPredicate> predicates = createSearchPredicates();
-        cursor.open(initialState, predicates.get(0));
-        cursor.close();
-        cursor.destroy();
-        boolean expectedExceptionThrown = false;
-        try {
-            cursor.hasNext();
-        } catch (Exception e) {
-            expectedExceptionThrown = true;
-        }
-        Assert.assertTrue(expectedExceptionThrown);
-    }
-
-    @Test
-    public void testGetTupleReturnsNullAfterDestroy() throws Exception {
-        IIndexCursor cursor = createCursor();
-        ICursorInitialState initialState = createCursorInitialState();
-        List<ISearchPredicate> predicates = createSearchPredicates();
-        cursor.open(initialState, predicates.get(0));
-        cursor.close();
-        cursor.destroy();
-        Assert.assertNull(cursor.getTuple());
-    }
-
-    protected abstract List<ISearchPredicate> createSearchPredicates();
-
-    protected abstract ICursorInitialState createCursorInitialState();
-
-    protected abstract IIndexCursor createCursor();
-}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/dataflow/BTreeUpdateSearchOperatorNodePushable.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/dataflow/BTreeUpdateSearchOperatorNodePushable.java b/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/dataflow/BTreeUpdateSearchOperatorNodePushable.java
index 3c045cb..a296672 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/dataflow/BTreeUpdateSearchOperatorNodePushable.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/dataflow/BTreeUpdateSearchOperatorNodePushable.java
@@ -29,10 +29,10 @@ import org.apache.hyracks.storage.am.btree.api.IBTreeLeafFrame;
 import org.apache.hyracks.storage.am.btree.impls.BTreeRangeSearchCursor;
 import org.apache.hyracks.storage.am.common.api.ISearchOperationCallbackFactory;
 import org.apache.hyracks.storage.am.common.api.ITreeIndex;
-import org.apache.hyracks.storage.am.common.api.ITreeIndexCursor;
 import org.apache.hyracks.storage.am.common.api.ITreeIndexFrame;
 import org.apache.hyracks.storage.am.common.api.ITupleUpdater;
 import org.apache.hyracks.storage.am.common.dataflow.IIndexDataflowHelperFactory;
+import org.apache.hyracks.storage.common.IIndexCursor;
 
 public class BTreeUpdateSearchOperatorNodePushable extends BTreeSearchOperatorNodePushable {
     private final ITupleUpdater tupleUpdater;
@@ -49,7 +49,7 @@ public class BTreeUpdateSearchOperatorNodePushable extends BTreeSearchOperatorNo
     }
 
     @Override
-    protected ITreeIndexCursor createCursor() {
+    protected IIndexCursor createCursor() {
         ITreeIndex treeIndex = (ITreeIndex) index;
         ITreeIndexFrame cursorFrame = treeIndex.getLeafFrameFactory().createFrame();
         return new BTreeRangeSearchCursor((IBTreeLeafFrame) cursorFrame, true);

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/BTree.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/BTree.java b/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/BTree.java
index 6e2d694..077a006 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/BTree.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/BTree.java
@@ -841,6 +841,7 @@ public class BTree extends AbstractTreeIndex {
     public class BTreeAccessor implements ITreeIndexAccessor {
         protected BTree btree;
         protected BTreeOpContext ctx;
+        private boolean destroyed = false;
 
         public BTreeAccessor(BTree btree, IModificationOperationCallback modificationCalback,
                 ISearchOperationCallback searchCallback) {
@@ -995,6 +996,15 @@ public class BTree extends AbstractTreeIndex {
                 throw HyracksDataException.create(ErrorCode.OPERATION_EXCEEDED_MAX_RESTARTS, MAX_RESTARTS);
             }
         }
+
+        @Override
+        public void destroy() throws HyracksDataException {
+            if (destroyed) {
+                return;
+            }
+            destroyed = true;
+            ctx.destroy();
+        }
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/BTreeCountingSearchCursor.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/BTreeCountingSearchCursor.java b/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/BTreeCountingSearchCursor.java
index 10f79a2..f7e0ce0 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/BTreeCountingSearchCursor.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/BTreeCountingSearchCursor.java
@@ -29,6 +29,7 @@ import org.apache.hyracks.storage.am.common.api.ITreeIndexCursor;
 import org.apache.hyracks.storage.am.common.api.ITreeIndexTupleReference;
 import org.apache.hyracks.storage.am.common.ophelpers.FindTupleMode;
 import org.apache.hyracks.storage.am.common.ophelpers.FindTupleNoExactMatchPolicy;
+import org.apache.hyracks.storage.common.EnforcedIndexCursor;
 import org.apache.hyracks.storage.common.ICursorInitialState;
 import org.apache.hyracks.storage.common.ISearchPredicate;
 import org.apache.hyracks.storage.common.MultiComparator;
@@ -36,7 +37,7 @@ import org.apache.hyracks.storage.common.buffercache.IBufferCache;
 import org.apache.hyracks.storage.common.buffercache.ICachedPage;
 import org.apache.hyracks.storage.common.file.BufferedFileHandle;
 
-public class BTreeCountingSearchCursor implements ITreeIndexCursor {
+public class BTreeCountingSearchCursor extends EnforcedIndexCursor implements ITreeIndexCursor {
 
     private int fileId = -1;
     private ICachedPage page = null;
@@ -75,11 +76,7 @@ public class BTreeCountingSearchCursor implements ITreeIndexCursor {
     }
 
     @Override
-    public void open(ICursorInitialState initialState, ISearchPredicate searchPred) throws HyracksDataException {
-        // in case open is called multiple times without closing
-        if (page != null) {
-            releasePage();
-        }
+    public void doOpen(ICursorInitialState initialState, ISearchPredicate searchPred) throws HyracksDataException {
 
         page = ((BTreeCursorInitialState) initialState).getPage();
         isPageDirty = false;
@@ -169,7 +166,7 @@ public class BTreeCountingSearchCursor implements ITreeIndexCursor {
     }
 
     @Override
-    public boolean hasNext() throws HyracksDataException {
+    public boolean doHasNext() throws HyracksDataException {
         // get the count for the current page
         // follow the sibling pointer until last page
         // if no more tuples on a page, then done
@@ -199,7 +196,7 @@ public class BTreeCountingSearchCursor implements ITreeIndexCursor {
     }
 
     @Override
-    public void next() throws HyracksDataException {
+    public void doNext() throws HyracksDataException {
         // Do nothing. Count is performed just once!
         IntegerPointable.setInteger(countBuf, 0, count);
         tupleBuilder.addField(countBuf, 0, 4);
@@ -207,7 +204,7 @@ public class BTreeCountingSearchCursor implements ITreeIndexCursor {
     }
 
     @Override
-    public void destroy() throws HyracksDataException {
+    public void doDestroy() throws HyracksDataException {
         if (page != null) {
             releasePage();
         }
@@ -220,16 +217,12 @@ public class BTreeCountingSearchCursor implements ITreeIndexCursor {
     }
 
     @Override
-    public void close() {
-        try {
-            destroy();
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
+    public void doClose() throws HyracksDataException {
+        doDestroy();
     }
 
     @Override
-    public ITupleReference getTuple() {
+    public ITupleReference doGetTuple() {
         return countTuple;
     }
 

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/BTreeOpContext.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/BTreeOpContext.java b/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/BTreeOpContext.java
index a46ae9a..60fa145 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/BTreeOpContext.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/BTreeOpContext.java
@@ -24,6 +24,7 @@ import java.util.Deque;
 
 import org.apache.hyracks.api.dataflow.value.IBinaryComparatorFactory;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.api.util.DestroyUtils;
 import org.apache.hyracks.dataflow.common.comm.io.ArrayTupleBuilder;
 import org.apache.hyracks.dataflow.common.comm.io.ArrayTupleReference;
 import org.apache.hyracks.dataflow.common.data.accessors.ITupleReference;
@@ -73,6 +74,7 @@ public class BTreeOpContext implements IIndexOperationContext, IExtraPageBlockHe
     private ISearchOperationCallback searchCallback;
     private ITupleAcceptor acceptor;
     private int smoCount;
+    private boolean destroyed = false;
 
     // Debug
     private final Deque<PageValidationInfo> validationInfos;
@@ -383,4 +385,16 @@ public class BTreeOpContext implements IIndexOperationContext, IExtraPageBlockHe
     public void resetNonIndexFieldsTuple(ITupleReference newValue) {
         tupleWithNonIndexFields.reset(newValue);
     }
+
+    @Override
+    public void destroy() throws HyracksDataException {
+        if (destroyed) {
+            return;
+        }
+        destroyed = true;
+        Throwable failure = DestroyUtils.destroy(null, accessor, cursor);
+        if (failure != null) {
+            throw HyracksDataException.create(failure);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/BTreeRangeSearchCursor.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/BTreeRangeSearchCursor.java b/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/BTreeRangeSearchCursor.java
index e903180..bff1bcb 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/BTreeRangeSearchCursor.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/BTreeRangeSearchCursor.java
@@ -29,6 +29,7 @@ import org.apache.hyracks.storage.am.common.api.ITreeIndexCursor;
 import org.apache.hyracks.storage.am.common.api.ITreeIndexTupleReference;
 import org.apache.hyracks.storage.am.common.ophelpers.FindTupleMode;
 import org.apache.hyracks.storage.am.common.ophelpers.FindTupleNoExactMatchPolicy;
+import org.apache.hyracks.storage.common.EnforcedIndexCursor;
 import org.apache.hyracks.storage.common.ICursorInitialState;
 import org.apache.hyracks.storage.common.IIndexAccessor;
 import org.apache.hyracks.storage.common.ISearchOperationCallback;
@@ -38,7 +39,7 @@ import org.apache.hyracks.storage.common.buffercache.IBufferCache;
 import org.apache.hyracks.storage.common.buffercache.ICachedPage;
 import org.apache.hyracks.storage.common.file.BufferedFileHandle;
 
-public class BTreeRangeSearchCursor implements ITreeIndexCursor {
+public class BTreeRangeSearchCursor extends EnforcedIndexCursor implements ITreeIndexCursor {
 
     protected final IBTreeLeafFrame frame;
     protected final ITreeIndexTupleReference frameTuple;
@@ -81,19 +82,12 @@ public class BTreeRangeSearchCursor implements ITreeIndexCursor {
     }
 
     @Override
-    public void destroy() throws HyracksDataException {
-        if (page != null) {
-            releasePage();
-        }
-
-        tupleIndex = 0;
-        page = null;
-        isPageDirty = false;
-        pred = null;
+    public void doDestroy() throws HyracksDataException {
+        // No Op all resources are released in the close call
     }
 
     @Override
-    public ITupleReference getTuple() {
+    public ITupleReference doGetTuple() {
         return frameTuple;
     }
 
@@ -118,7 +112,7 @@ public class BTreeRangeSearchCursor implements ITreeIndexCursor {
     }
 
     @Override
-    public boolean hasNext() throws HyracksDataException {
+    public boolean doHasNext() throws HyracksDataException {
         int nextLeafPage;
         if (tupleIndex >= frame.getTupleCount()) {
             nextLeafPage = frame.getNextLeaf();
@@ -159,8 +153,10 @@ public class BTreeRangeSearchCursor implements ITreeIndexCursor {
 
                 // retraverse the index looking for the reconciled key
                 reusablePredicate.setLowKey(reconciliationTuple, true);
+                // before re-using the cursor, we must close it
+                close();
+                // this search call will re-open the cursor
                 accessor.search(this, reusablePredicate);
-
                 if (stopTupleIndex < 0 || tupleIndex > stopTupleIndex) {
                     return false;
                 }
@@ -177,7 +173,7 @@ public class BTreeRangeSearchCursor implements ITreeIndexCursor {
     }
 
     @Override
-    public void next() throws HyracksDataException {
+    public void doNext() throws HyracksDataException {
         tupleIndex++;
     }
 
@@ -216,7 +212,7 @@ public class BTreeRangeSearchCursor implements ITreeIndexCursor {
     }
 
     @Override
-    public void open(ICursorInitialState initialState, ISearchPredicate searchPred) throws HyracksDataException {
+    public void doOpen(ICursorInitialState initialState, ISearchPredicate searchPred) throws HyracksDataException {
         // in case open is called multiple times without closing
         if (page != null) {
             resetBeforeOpen();
@@ -262,8 +258,15 @@ public class BTreeRangeSearchCursor implements ITreeIndexCursor {
     }
 
     @Override
-    public void close() throws HyracksDataException {
-        destroy();
+    public void doClose() throws HyracksDataException {
+        if (page != null) {
+            releasePage();
+        }
+
+        tupleIndex = 0;
+        page = null;
+        isPageDirty = false;
+        pred = null;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/DiskBTree.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/DiskBTree.java b/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/DiskBTree.java
index 32fa9df..eee43b5 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/DiskBTree.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/DiskBTree.java
@@ -275,7 +275,10 @@ public class DiskBTree extends BTree {
 
         @Override
         protected void releasePage() throws HyracksDataException {
-            bufferCache.unpin(page);
+            if (page != null) {
+                bufferCache.unpin(page);
+                page = null;
+            }
         }
 
         @Override

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/DiskBTreePointSearchCursor.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/DiskBTreePointSearchCursor.java b/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/DiskBTreePointSearchCursor.java
index 5839d0e..7814e60 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/DiskBTreePointSearchCursor.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/DiskBTreePointSearchCursor.java
@@ -35,17 +35,17 @@ public class DiskBTreePointSearchCursor extends DiskBTreeRangeSearchCursor {
     }
 
     @Override
-    public boolean hasNext() throws HyracksDataException {
+    public boolean doHasNext() throws HyracksDataException {
         return tupleIndex >= 0 && !nextHasBeenCalled;
     }
 
     @Override
-    public void next() throws HyracksDataException {
+    public void doNext() throws HyracksDataException {
         nextHasBeenCalled = true;
     }
 
     @Override
-    public void open(ICursorInitialState initialState, ISearchPredicate searchPred) throws HyracksDataException {
+    public void doOpen(ICursorInitialState initialState, ISearchPredicate searchPred) throws HyracksDataException {
         // in case open is called multiple times without closing
         if (page != null) {
             resetBeforeOpen();

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/DiskBTreeRangeSearchCursor.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/DiskBTreeRangeSearchCursor.java b/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/DiskBTreeRangeSearchCursor.java
index 7d4ee0d..0e82088 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/DiskBTreeRangeSearchCursor.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/main/java/org/apache/hyracks/storage/am/btree/impls/DiskBTreeRangeSearchCursor.java
@@ -37,7 +37,7 @@ public class DiskBTreeRangeSearchCursor extends BTreeRangeSearchCursor {
     }
 
     @Override
-    public boolean hasNext() throws HyracksDataException {
+    public boolean doHasNext() throws HyracksDataException {
         int nextLeafPage;
         if (tupleIndex >= frame.getTupleCount()) {
             nextLeafPage = frame.getNextLeaf();
@@ -99,8 +99,8 @@ public class DiskBTreeRangeSearchCursor extends BTreeRangeSearchCursor {
     }
 
     @Override
-    public void close() throws HyracksDataException {
-        super.close();
+    public void doClose() throws HyracksDataException {
+        super.doClose();
         searchPages.clear();
     }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/test/java/org/apache/hyracks/storage/am/btree/test/FramewriterTest.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/test/java/org/apache/hyracks/storage/am/btree/test/FramewriterTest.java b/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/test/java/org/apache/hyracks/storage/am/btree/test/FramewriterTest.java
index 4ff0656..12dc310 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/test/java/org/apache/hyracks/storage/am/btree/test/FramewriterTest.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-btree/src/test/java/org/apache/hyracks/storage/am/btree/test/FramewriterTest.java
@@ -67,10 +67,12 @@ import org.mockito.Mockito;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
 import org.powermock.core.classloader.annotations.PrepareForTest;
 import org.powermock.modules.junit4.PowerMockRunner;
 
 @RunWith(PowerMockRunner.class)
+@PowerMockIgnore("javax.management.*")
 @PrepareForTest({ BTreeUtils.class, FrameTupleAccessor.class, ArrayTupleBuilder.class,
         IndexSearchOperatorNodePushable.class, FrameUtils.class, FrameTupleAppender.class })
 public class FramewriterTest {

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-common/pom.xml
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-common/pom.xml b/hyracks-fullstack/hyracks/hyracks-storage-am-common/pom.xml
index 94fe2c2..5f05ce9 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-common/pom.xml
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-common/pom.xml
@@ -16,18 +16,16 @@
  ! specific language governing permissions and limitations
  ! under the License.
  !-->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <artifactId>hyracks-storage-am-common</artifactId>
   <name>hyracks-storage-am-common</name>
-
   <parent>
     <groupId>org.apache.hyracks</groupId>
     <artifactId>hyracks</artifactId>
     <version>0.3.4-SNAPSHOT</version>
   </parent>
-
   <licenses>
     <license>
       <name>Apache License, Version 2.0</name>
@@ -36,11 +34,24 @@
       <comments>A business-friendly OSS license</comments>
     </license>
   </licenses>
-
   <properties>
     <root.dir>${basedir}/../..</root.dir>
   </properties>
-
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
   <dependencies>
     <dependency>
       <groupId>org.apache.hyracks</groupId>
@@ -94,5 +105,10 @@
       <groupId>org.apache.logging.log4j</groupId>
       <artifactId>log4j-api</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-all</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
-</project>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/api/IIndexOperationContext.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/api/IIndexOperationContext.java b/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/api/IIndexOperationContext.java
index a8bdf32..01c98f1 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/api/IIndexOperationContext.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/api/IIndexOperationContext.java
@@ -18,13 +18,26 @@
  */
 package org.apache.hyracks.storage.am.common.api;
 
+import org.apache.hyracks.api.dataflow.IDestroyable;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.storage.am.common.ophelpers.IndexOperation;
 
-public interface IIndexOperationContext {
+public interface IIndexOperationContext extends IDestroyable {
+    /**
+     * Set the next intended operation for this context
+     *
+     * @param newOp
+     * @throws HyracksDataException
+     */
     void setOperation(IndexOperation newOp) throws HyracksDataException;
 
+    /**
+     * @return the intended operation
+     */
     IndexOperation getOperation();
 
+    /**
+     * Release resources associated with the context and prepare it for the next use
+     */
     void reset();
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/api/ITreeIndexAccessor.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/api/ITreeIndexAccessor.java b/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/api/ITreeIndexAccessor.java
index 90963bf..0d7fd5f 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/api/ITreeIndexAccessor.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/api/ITreeIndexAccessor.java
@@ -39,6 +39,8 @@ public interface ITreeIndexAccessor extends IIndexAccessor {
     /**
      * Open the given cursor for a disk-order scan, positioning the cursor to
      * the first leaf tuple.
+     * If this method returns successfully, the cursor is open.
+     * Otherwise, it was not open
      *
      * @param icursor
      *            Cursor to be opened for disk-order scanning.

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/dataflow/IndexDropOperatorNodePushable.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/dataflow/IndexDropOperatorNodePushable.java b/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/dataflow/IndexDropOperatorNodePushable.java
index aae830d..8490c6a 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/dataflow/IndexDropOperatorNodePushable.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/dataflow/IndexDropOperatorNodePushable.java
@@ -21,7 +21,6 @@ package org.apache.hyracks.storage.am.common.dataflow;
 
 import static org.apache.hyracks.api.exceptions.ErrorCode.CANNOT_DROP_IN_USE_INDEX;
 import static org.apache.hyracks.api.exceptions.ErrorCode.INDEX_DOES_NOT_EXIST;
-import static org.apache.hyracks.storage.am.common.dataflow.IndexDropOperatorDescriptor.DropOption;
 import static org.apache.hyracks.storage.am.common.dataflow.IndexDropOperatorDescriptor.DropOption.IF_EXISTS;
 import static org.apache.hyracks.storage.am.common.dataflow.IndexDropOperatorDescriptor.DropOption.WAIT_ON_IN_USE;
 
@@ -34,6 +33,7 @@ import org.apache.hyracks.api.dataflow.value.RecordDescriptor;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.dataflow.std.base.AbstractOperatorNodePushable;
 import org.apache.hyracks.storage.am.common.api.IIndexDataflowHelper;
+import org.apache.hyracks.storage.am.common.dataflow.IndexDropOperatorDescriptor.DropOption;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
@@ -109,7 +109,6 @@ public class IndexDropOperatorNodePushable extends AbstractOperatorNodePushable
                 maxWaitTimeMillis -= DROP_ATTEMPT_WAIT_TIME_MILLIS;
                 return true;
             } catch (InterruptedException e1) {
-                Thread.currentThread().interrupt();
                 throw HyracksDataException.create(e1);
             }
         }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/dataflow/IndexSearchOperatorNodePushable.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/dataflow/IndexSearchOperatorNodePushable.java b/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/dataflow/IndexSearchOperatorNodePushable.java
index d55962a..41fdc41 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/dataflow/IndexSearchOperatorNodePushable.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/dataflow/IndexSearchOperatorNodePushable.java
@@ -29,6 +29,8 @@ import org.apache.hyracks.api.dataflow.value.IMissingWriterFactory;
 import org.apache.hyracks.api.dataflow.value.RecordDescriptor;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.api.job.profiling.IOperatorStats;
+import org.apache.hyracks.api.util.DestroyUtils;
+import org.apache.hyracks.api.util.ExceptionUtils;
 import org.apache.hyracks.control.common.job.profiling.OperatorStats;
 import org.apache.hyracks.dataflow.common.comm.io.ArrayTupleBuilder;
 import org.apache.hyracks.dataflow.common.comm.io.FrameTupleAccessor;
@@ -43,6 +45,7 @@ import org.apache.hyracks.storage.am.common.api.ISearchOperationCallbackFactory;
 import org.apache.hyracks.storage.am.common.impls.IndexAccessParameters;
 import org.apache.hyracks.storage.am.common.impls.NoOpOperationCallback;
 import org.apache.hyracks.storage.am.common.tuples.PermutingFrameTupleReference;
+import org.apache.hyracks.storage.am.common.util.ResourceReleaseUtils;
 import org.apache.hyracks.storage.common.IIndex;
 import org.apache.hyracks.storage.common.IIndexAccessParameters;
 import org.apache.hyracks.storage.common.IIndexAccessor;
@@ -218,7 +221,15 @@ public abstract class IndexSearchOperatorNodePushable extends AbstractUnaryInput
 
     @Override
     public void close() throws HyracksDataException {
-        HyracksDataException closeException = null;
+        Throwable failure = releaseResources();
+        failure = ResourceReleaseUtils.close(writer, failure);
+        if (failure != null) {
+            throw HyracksDataException.create(failure);
+        }
+    }
+
+    private Throwable releaseResources() {
+        Throwable failure = null;
         if (index != null) {
             // if index == null, then the index open was not successful
             if (!failed) {
@@ -226,44 +237,24 @@ public abstract class IndexSearchOperatorNodePushable extends AbstractUnaryInput
                     if (appender.getTupleCount() > 0) {
                         appender.write(writer, true);
                     }
-                } catch (Throwable th) {
-                    writer.fail();
-                    closeException = HyracksDataException.create(th);
-                }
-            }
-
-            try {
-                cursor.destroy();
-            } catch (Throwable th) {
-                if (closeException == null) {
-                    closeException = HyracksDataException.create(th);
-                } else {
-                    closeException.addSuppressed(th);
+                } catch (Throwable th) { // NOSONAR Must ensure writer.fail is called.
+                    // subsequently, the failure will be thrown
+                    failure = th;
                 }
-            }
-            try {
-                indexHelper.close();
-            } catch (Throwable th) {
-                if (closeException == null) {
-                    closeException = new HyracksDataException(th);
-                } else {
-                    closeException.addSuppressed(th);
+                if (failure != null) {
+                    try {
+                        writer.fail();
+                    } catch (Throwable th) {// NOSONAR Must cursor.close is called.
+                        // subsequently, the failure will be thrown
+                        failure = ExceptionUtils.suppress(failure, th);
+                    }
                 }
             }
+            failure = ResourceReleaseUtils.close(cursor, failure);
+            failure = DestroyUtils.destroy(failure, cursor, indexAccessor);
+            failure = ResourceReleaseUtils.close(indexHelper, failure);
         }
-        try {
-            // will definitely be called regardless of exceptions
-            writer.close();
-        } catch (Throwable th) {
-            if (closeException == null) {
-                closeException = new HyracksDataException(th);
-            } else {
-                closeException.addSuppressed(th);
-            }
-        }
-        if (closeException != null) {
-            throw closeException;
-        }
+        return failure;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/dataflow/TreeIndexDiskOrderScanOperatorNodePushable.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/dataflow/TreeIndexDiskOrderScanOperatorNodePushable.java b/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/dataflow/TreeIndexDiskOrderScanOperatorNodePushable.java
index 7626815..90b50c6 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/dataflow/TreeIndexDiskOrderScanOperatorNodePushable.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/dataflow/TreeIndexDiskOrderScanOperatorNodePushable.java
@@ -19,10 +19,12 @@
 package org.apache.hyracks.storage.am.common.dataflow;
 
 import java.io.DataOutput;
+import java.io.IOException;
 
 import org.apache.hyracks.api.comm.VSizeFrame;
 import org.apache.hyracks.api.context.IHyracksTaskContext;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.api.util.ExceptionUtils;
 import org.apache.hyracks.dataflow.common.comm.io.ArrayTupleBuilder;
 import org.apache.hyracks.dataflow.common.comm.io.FrameTupleAppender;
 import org.apache.hyracks.dataflow.common.comm.util.FrameUtils;
@@ -37,6 +39,7 @@ import org.apache.hyracks.storage.am.common.api.ITreeIndexFrame;
 import org.apache.hyracks.storage.am.common.impls.IndexAccessParameters;
 import org.apache.hyracks.storage.am.common.impls.NoOpOperationCallback;
 import org.apache.hyracks.storage.am.common.impls.TreeIndexDiskOrderScanCursor;
+import org.apache.hyracks.storage.am.common.util.ResourceReleaseUtils;
 import org.apache.hyracks.storage.common.IIndexAccessParameters;
 import org.apache.hyracks.storage.common.ISearchOperationCallback;
 import org.apache.hyracks.storage.common.LocalResource;
@@ -56,55 +59,69 @@ public class TreeIndexDiskOrderScanOperatorNodePushable extends AbstractUnaryOut
 
     @Override
     public void initialize() throws HyracksDataException {
+        Throwable failure = null;
         treeIndexHelper.open();
-        ITreeIndex treeIndex = (ITreeIndex) treeIndexHelper.getIndexInstance();
         try {
-            ITreeIndexFrame cursorFrame = treeIndex.getLeafFrameFactory().createFrame();
-            ITreeIndexCursor cursor = new TreeIndexDiskOrderScanCursor(cursorFrame);
-            LocalResource resource = treeIndexHelper.getResource();
-            ISearchOperationCallback searchCallback =
-                    searchCallbackFactory.createSearchOperationCallback(resource.getId(), ctx, null);
-            IIndexAccessParameters iap = new IndexAccessParameters(NoOpOperationCallback.INSTANCE, searchCallback);
-            ITreeIndexAccessor indexAccessor = (ITreeIndexAccessor) treeIndex.createAccessor(iap);
+            writer.open();
+            FrameTupleAppender appender = new FrameTupleAppender(new VSizeFrame(ctx));
+            scan(appender);
+            appender.write(writer, true);
+        } catch (Throwable th) { // NOSONAR: Must call writer.fail
+            failure = th;
             try {
-                writer.open();
-                indexAccessor.diskOrderScan(cursor);
-                int fieldCount = treeIndex.getFieldCount();
-                FrameTupleAppender appender = new FrameTupleAppender(new VSizeFrame(ctx));
-                ArrayTupleBuilder tb = new ArrayTupleBuilder(fieldCount);
-                DataOutput dos = tb.getDataOutput();
+                writer.fail();
+            } catch (Throwable failFailure) {// NOSONAR: Must maintain all stacks
+                failure = ExceptionUtils.suppress(failure, failFailure);
+            }
+        } finally {
+            failure = ResourceReleaseUtils.close(writer, failure);
+        }
+        if (failure != null) {
+            throw HyracksDataException.create(failure);
+        }
+    }
+
+    private void scan(FrameTupleAppender appender) throws IOException {
+        ITreeIndex treeIndex = (ITreeIndex) treeIndexHelper.getIndexInstance();
+        LocalResource resource = treeIndexHelper.getResource();
+        ISearchOperationCallback searchCallback =
+                searchCallbackFactory.createSearchOperationCallback(resource.getId(), ctx, null);
+        IIndexAccessParameters iap = new IndexAccessParameters(NoOpOperationCallback.INSTANCE, searchCallback);
+        ITreeIndexAccessor indexAccessor = (ITreeIndexAccessor) treeIndex.createAccessor(iap);
+        try {
+            doScan(treeIndex, indexAccessor, appender);
+        } finally {
+            indexAccessor.destroy();
+        }
+    }
 
+    private void doScan(ITreeIndex treeIndex, ITreeIndexAccessor indexAccessor, FrameTupleAppender appender)
+            throws IOException {
+        int fieldCount = treeIndex.getFieldCount();
+        ArrayTupleBuilder tb = new ArrayTupleBuilder(fieldCount);
+        DataOutput dos = tb.getDataOutput();
+        ITreeIndexFrame cursorFrame = treeIndex.getLeafFrameFactory().createFrame();
+        ITreeIndexCursor cursor = new TreeIndexDiskOrderScanCursor(cursorFrame);
+        try {
+            indexAccessor.diskOrderScan(cursor);
+            try {
                 while (cursor.hasNext()) {
                     tb.reset();
                     cursor.next();
-
                     ITupleReference frameTuple = cursor.getTuple();
                     for (int i = 0; i < frameTuple.getFieldCount(); i++) {
                         dos.write(frameTuple.getFieldData(i), frameTuple.getFieldStart(i),
                                 frameTuple.getFieldLength(i));
                         tb.addFieldEndOffset();
                     }
-
                     FrameUtils.appendToWriter(writer, appender, tb.getFieldEndOffsets(), tb.getByteArray(), 0,
                             tb.getSize());
-
                 }
-                appender.write(writer, true);
-            } catch (Throwable th) {
-                writer.fail();
-                throw new HyracksDataException(th);
             } finally {
-                try {
-                    cursor.destroy();
-                } catch (Exception cursorCloseException) {
-                    throw new IllegalStateException(cursorCloseException);
-                } finally {
-                    writer.close();
-                }
+                cursor.close();
             }
-        } catch (Throwable th) {
-            treeIndexHelper.close();
-            throw new HyracksDataException(th);
+        } finally {
+            cursor.destroy();
         }
     }
 

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/impls/TreeIndexDiskOrderScanCursor.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/impls/TreeIndexDiskOrderScanCursor.java b/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/impls/TreeIndexDiskOrderScanCursor.java
index b2c26db..36fba76 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/impls/TreeIndexDiskOrderScanCursor.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/impls/TreeIndexDiskOrderScanCursor.java
@@ -20,17 +20,17 @@
 package org.apache.hyracks.storage.am.common.impls;
 
 import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.dataflow.common.data.accessors.ITupleReference;
 import org.apache.hyracks.storage.am.common.api.ITreeIndexCursor;
 import org.apache.hyracks.storage.am.common.api.ITreeIndexFrame;
 import org.apache.hyracks.storage.am.common.api.ITreeIndexTupleReference;
+import org.apache.hyracks.storage.common.EnforcedIndexCursor;
 import org.apache.hyracks.storage.common.ICursorInitialState;
 import org.apache.hyracks.storage.common.ISearchPredicate;
 import org.apache.hyracks.storage.common.buffercache.IBufferCache;
 import org.apache.hyracks.storage.common.buffercache.ICachedPage;
 import org.apache.hyracks.storage.common.file.BufferedFileHandle;
 
-public class TreeIndexDiskOrderScanCursor implements ITreeIndexCursor {
+public class TreeIndexDiskOrderScanCursor extends EnforcedIndexCursor implements ITreeIndexCursor {
 
     protected int tupleIndex = 0;
     protected int fileId = -1;
@@ -48,13 +48,15 @@ public class TreeIndexDiskOrderScanCursor implements ITreeIndexCursor {
     }
 
     @Override
-    public void destroy() throws HyracksDataException {
+    public void doDestroy() throws HyracksDataException {
+        tupleIndex = 0;
+        currentPageId = -1;
+        maxPageId = -1;
         releasePage();
-        page = null;
     }
 
     @Override
-    public ITreeIndexTupleReference getTuple() {
+    public ITreeIndexTupleReference doGetTuple() {
         return frameTuple;
     }
 
@@ -65,7 +67,6 @@ public class TreeIndexDiskOrderScanCursor implements ITreeIndexCursor {
             }
 
             releasePage();
-
             ICachedPage nextPage = acquireNextPage();
             page = nextPage;
             frame.setPage(page);
@@ -80,7 +81,7 @@ public class TreeIndexDiskOrderScanCursor implements ITreeIndexCursor {
     }
 
     @Override
-    public boolean hasNext() throws HyracksDataException {
+    public boolean doHasNext() throws HyracksDataException {
         if (currentPageId > maxPageId) {
             return false;
         }
@@ -98,16 +99,12 @@ public class TreeIndexDiskOrderScanCursor implements ITreeIndexCursor {
     }
 
     @Override
-    public void next() throws HyracksDataException {
+    public void doNext() throws HyracksDataException {
         tupleIndex++;
     }
 
     @Override
-    public void open(ICursorInitialState initialState, ISearchPredicate searchPred) throws HyracksDataException {
-        // in case open is called multiple times without closing
-        if (page != null) {
-            releasePage();
-        }
+    public void doOpen(ICursorInitialState initialState, ISearchPredicate searchPred) throws HyracksDataException {
         page = initialState.getPage();
         tupleIndex = 0;
         frame.setPage(page);
@@ -115,11 +112,11 @@ public class TreeIndexDiskOrderScanCursor implements ITreeIndexCursor {
     }
 
     @Override
-    public void close() {
+    public void doClose() throws HyracksDataException {
         tupleIndex = 0;
         currentPageId = -1;
         maxPageId = -1;
-        page = null;
+        releasePage();
     }
 
     @Override
@@ -146,8 +143,11 @@ public class TreeIndexDiskOrderScanCursor implements ITreeIndexCursor {
     }
 
     protected void releasePage() throws HyracksDataException {
-        page.releaseReadLatch();
-        bufferCache.unpin(page);
+        if (page != null) {
+            page.releaseReadLatch();
+            bufferCache.unpin(page);
+            page = null;
+        }
     }
 
     protected ICachedPage acquireNextPage() throws HyracksDataException {

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/util/ResourceReleaseUtils.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/util/ResourceReleaseUtils.java b/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/util/ResourceReleaseUtils.java
new file mode 100644
index 0000000..50b6e59
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/main/java/org/apache/hyracks/storage/am/common/util/ResourceReleaseUtils.java
@@ -0,0 +1,158 @@
+/*
+ * 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.hyracks.storage.am.common.util;
+
+import java.util.List;
+
+import org.apache.hyracks.api.comm.IFrameWriter;
+import org.apache.hyracks.api.util.ExceptionUtils;
+import org.apache.hyracks.storage.am.common.api.IIndexDataflowHelper;
+import org.apache.hyracks.storage.common.IIndexCursor;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+public class ResourceReleaseUtils {
+
+    private static final Logger LOGGER = LogManager.getLogger();
+
+    private ResourceReleaseUtils() {
+    }
+
+    /**
+     * Close the cursor and suppress any Throwable thrown by the close call.
+     * This method must NEVER throw any Throwable
+     *
+     * @param cursor
+     *            the cursor to close
+     * @param root
+     *            the first exception encountered during release of resources
+     * @return the root Throwable if not null or a new Throwable if any was thrown, otherwise, it returns null
+     */
+    public static Throwable close(IIndexCursor cursor, Throwable root) {
+        if (cursor != null) {
+            try {
+                cursor.close();
+            } catch (Throwable th) { // NOSONAR Will be re-thrown
+                try {
+                    LOGGER.log(Level.WARN, "Failure closing a closeable resource", th);
+                } catch (Throwable loggingFailure) {
+                    // Do nothing
+                }
+                root = ExceptionUtils.suppress(root, th);
+            }
+        }
+        return root;
+    }
+
+    /**
+     * Close the AutoCloseable and suppress any Throwable thrown by the close call.
+     * This method must NEVER throw any Throwable
+     *
+     * @param closable
+     *            the resource to close
+     * @param root
+     *            the first exception encountered during release of resources
+     * @return the root Throwable if not null or a new Throwable if any was thrown, otherwise, it returns null
+     */
+    public static Throwable close(AutoCloseable closable, Throwable root) {
+        if (closable != null) {
+            try {
+                closable.close();
+            } catch (Throwable th) { // NOSONAR Will be re-thrown
+                try {
+                    LOGGER.log(Level.WARN, "Failure closing a closeable resource", th);
+                } catch (Throwable loggingFailure) {
+                    // Do nothing
+                }
+                root = ExceptionUtils.suppress(root, th);
+            }
+        }
+        return root;
+    }
+
+    /**
+     * Close the IIndexDataflowHelper and suppress any Throwable thrown by the close call.
+     * This method must NEVER throw any Throwable
+     *
+     * @param indexHelper
+     *            the indexHelper to close
+     * @param root
+     *            the first exception encountered during release of resources
+     * @return the root Throwable if not null or a new Throwable if any was thrown, otherwise, it returns null
+     */
+    public static Throwable close(IIndexDataflowHelper indexHelper, Throwable root) {
+        if (indexHelper != null) {
+            try {
+                indexHelper.close();
+            } catch (Throwable th) { // NOSONAR Will be re-thrown
+                try {
+                    LOGGER.log(Level.WARN, "Failure closing a closeable resource", th);
+                } catch (Throwable loggingFailure) {
+                    // Do nothing
+                }
+                root = ExceptionUtils.suppress(root, th);
+            }
+        }
+        return root;
+    }
+
+    /**
+     * Close the IIndexDataflowHelpers and suppress any Throwable thrown by any close call.
+     * This method must NEVER throw any Throwable
+     *
+     * @param indexHelpers
+     *            the indexHelpers to close
+     * @param root
+     *            the first exception encountered during release of resources
+     * @return the root Throwable if not null or a new Throwable if any was thrown, otherwise, it returns null
+     */
+    public static Throwable close(List<IIndexDataflowHelper> indexHelpers, Throwable root) {
+        for (int i = 0; i < indexHelpers.size(); i++) {
+            root = close(indexHelpers.get(i), root);
+        }
+        return root;
+    }
+
+    /**
+     * Close the IFrameWriter and suppress any Throwable thrown by the close call.
+     * This method must NEVER throw any Throwable
+     *
+     * @param writer
+     *            the writer to close
+     * @param root
+     *            the first exception encountered during release of resources
+     * @return the root Throwable if not null or a new Throwable if any was thrown, otherwise, it returns null
+     */
+    public static Throwable close(IFrameWriter writer, Throwable root) {
+        if (writer != null) {
+            try {
+                writer.close();
+            } catch (Throwable th) { // NOSONAR Will be re-thrown
+                try {
+                    LOGGER.log(Level.WARN, "Failure closing a closeable resource", th);
+                } catch (Throwable loggingFailure) {
+                    // Do nothing
+                }
+                root = ExceptionUtils.suppress(root, th);
+            }
+        }
+        return root;
+    }
+}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/test/java/org/apache/hyracks/storage/am/common/test/DummyEnforcedIndexCursor.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/test/java/org/apache/hyracks/storage/am/common/test/DummyEnforcedIndexCursor.java b/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/test/java/org/apache/hyracks/storage/am/common/test/DummyEnforcedIndexCursor.java
new file mode 100644
index 0000000..eec9542
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/test/java/org/apache/hyracks/storage/am/common/test/DummyEnforcedIndexCursor.java
@@ -0,0 +1,59 @@
+/*
+ * 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.hyracks.storage.am.common.test;
+
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.dataflow.common.data.accessors.ITupleReference;
+import org.apache.hyracks.storage.common.EnforcedIndexCursor;
+import org.apache.hyracks.storage.common.ICursorInitialState;
+import org.apache.hyracks.storage.common.ISearchPredicate;
+
+public class DummyEnforcedIndexCursor extends EnforcedIndexCursor {
+    @Override
+    protected void doOpen(ICursorInitialState initialState, ISearchPredicate searchPred) throws HyracksDataException {
+        // Dummy
+    }
+
+    @Override
+    protected boolean doHasNext() throws HyracksDataException {
+        // Dummy
+        return false;
+    }
+
+    @Override
+    protected void doNext() throws HyracksDataException {
+        // Dummy
+    }
+
+    @Override
+    protected void doDestroy() throws HyracksDataException {
+        // Dummy
+    }
+
+    @Override
+    protected void doClose() throws HyracksDataException {
+        // Dummy
+    }
+
+    @Override
+    protected ITupleReference doGetTuple() {
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/test/java/org/apache/hyracks/storage/am/common/test/EnforcedIndexCursorTest.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/test/java/org/apache/hyracks/storage/am/common/test/EnforcedIndexCursorTest.java b/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/test/java/org/apache/hyracks/storage/am/common/test/EnforcedIndexCursorTest.java
new file mode 100644
index 0000000..9625c2f
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/test/java/org/apache/hyracks/storage/am/common/test/EnforcedIndexCursorTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.hyracks.storage.am.common.test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.storage.common.EnforcedIndexCursor;
+import org.apache.hyracks.storage.common.IIndexAccessor;
+import org.apache.hyracks.storage.common.IIndexCursor;
+import org.apache.hyracks.storage.common.ISearchPredicate;
+import org.mockito.Matchers;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+public class EnforcedIndexCursorTest extends IIndexCursorTest {
+    @Override
+    protected List<ISearchPredicate> createSearchPredicates() {
+        List<ISearchPredicate> predicates = new ArrayList<>();
+        for (int i = 0; i < 10; i++) {
+            predicates.add(Mockito.mock(ISearchPredicate.class));
+        }
+        return predicates;
+    }
+
+    @Override
+    protected IIndexAccessor createAccessor() throws HyracksDataException {
+        EnforcedIndexCursor cursor = new DummyEnforcedIndexCursor();
+        IIndexAccessor accessor = Mockito.mock(IIndexAccessor.class);
+        Mockito.when(accessor.createSearchCursor(Mockito.anyBoolean())).thenReturn(cursor);
+        Mockito.doAnswer(new Answer<Object>() {
+            @Override
+            public Object answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                IIndexCursor cursor = (IIndexCursor) args[0];
+                ISearchPredicate pred = (ISearchPredicate) args[1];
+                cursor.open(null, pred);
+                return null;
+            }
+        }).when(accessor).search(Matchers.any(IIndexCursor.class), Matchers.any(ISearchPredicate.class));
+        return accessor;
+    }
+}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/test/java/org/apache/hyracks/storage/am/common/test/IIndexCursorTest.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/test/java/org/apache/hyracks/storage/am/common/test/IIndexCursorTest.java b/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/test/java/org/apache/hyracks/storage/am/common/test/IIndexCursorTest.java
new file mode 100644
index 0000000..6a3fdf1
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-common/src/test/java/org/apache/hyracks/storage/am/common/test/IIndexCursorTest.java
@@ -0,0 +1,294 @@
+/*
+ * 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.hyracks.storage.am.common.test;
+
+import java.util.List;
+
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.storage.common.IIndexAccessor;
+import org.apache.hyracks.storage.common.IIndexCursor;
+import org.apache.hyracks.storage.common.ISearchPredicate;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * This is a test class that forms the basis for unit tests of different implementations of the IIndexCursor interface
+ */
+public abstract class IIndexCursorTest {
+    @Test
+    public void testNormalLifeCycle() throws Exception {
+        IIndexAccessor accessor = createAccessor();
+        IIndexCursor cursor = createCursor(accessor);
+        List<ISearchPredicate> predicates = createSearchPredicates();
+        for (ISearchPredicate predicate : predicates) {
+            open(accessor, cursor, predicate);
+            while (cursor.hasNext()) {
+                cursor.next();
+            }
+            cursor.close();
+        }
+        cursor.destroy();
+        destroy(accessor);
+    }
+
+    protected void destroy(IIndexAccessor accessor) throws HyracksDataException {
+        accessor.destroy();
+    }
+
+    @Test
+    public void testCreateDestroySucceed() throws Exception {
+        IIndexAccessor accessor = createAccessor();
+        IIndexCursor cursor = createCursor(accessor);
+        cursor.destroy();
+        destroy(accessor);
+    }
+
+    @Test
+    public void testDoubleOpenFails() throws Exception {
+        IIndexAccessor accessor = createAccessor();
+        IIndexCursor cursor = createCursor(accessor);
+        List<ISearchPredicate> predicates = createSearchPredicates();
+        open(accessor, cursor, predicates.get(0));
+        boolean expectedExceptionThrown = false;
+        try {
+            open(accessor, cursor, predicates.get(0));
+        } catch (Exception e) {
+            expectedExceptionThrown = true;
+        }
+        cursor.close();
+        cursor.destroy();
+        destroy(accessor);
+        Assert.assertTrue(expectedExceptionThrown);
+    }
+
+    @Test
+    public void testCloseWithoutOpenSucceeds() throws Exception {
+        IIndexAccessor accessor = createAccessor();
+        IIndexCursor cursor = createCursor(accessor);
+        cursor.close();
+        cursor.destroy();
+        destroy(accessor);
+    }
+
+    @Test
+    public void testDoubleCloseSucceeds() throws Exception {
+        IIndexAccessor accessor = createAccessor();
+        IIndexCursor cursor = createCursor(accessor);
+        List<ISearchPredicate> predicates = createSearchPredicates();
+        open(accessor, cursor, predicates.get(0));
+        cursor.close();
+        cursor.close();
+        cursor.destroy();
+        destroy(accessor);
+    }
+
+    @Test
+    public void testDoubleDestroySucceeds() throws Exception {
+        IIndexAccessor accessor = createAccessor();
+        IIndexCursor cursor = createCursor(accessor);
+        List<ISearchPredicate> predicates = createSearchPredicates();
+        open(accessor, cursor, predicates.get(0));
+        cursor.close();
+        cursor.destroy();
+        cursor.destroy();
+        destroy(accessor);
+    }
+
+    @Test
+    public void testHasNextBeforeOpenFails() throws Exception {
+        IIndexAccessor accessor = createAccessor();
+        IIndexCursor cursor = createCursor(accessor);
+        boolean expectedExceptionThrown = false;
+        try {
+            cursor.hasNext();
+        } catch (Exception e) {
+            expectedExceptionThrown = true;
+        }
+        cursor.destroy();
+        destroy(accessor);
+        Assert.assertTrue(expectedExceptionThrown);
+    }
+
+    @Test
+    public void testHasNextAfterCloseFails() throws Exception {
+        IIndexAccessor accessor = createAccessor();
+        IIndexCursor cursor = createCursor(accessor);
+        List<ISearchPredicate> predicates = createSearchPredicates();
+        open(accessor, cursor, predicates.get(0));
+        cursor.close();
+        boolean expectedExceptionThrown = false;
+        try {
+            cursor.hasNext();
+        } catch (Exception e) {
+            expectedExceptionThrown = true;
+        }
+        cursor.destroy();
+        destroy(accessor);
+        Assert.assertTrue(expectedExceptionThrown);
+    }
+
+    @Test
+    public void testNextBeforeOpenFails() throws Exception {
+        IIndexAccessor accessor = createAccessor();
+        IIndexCursor cursor = createCursor(accessor);
+        boolean expectedExceptionThrown = false;
+        try {
+            cursor.next();
+        } catch (Exception e) {
+            expectedExceptionThrown = true;
+        }
+        cursor.destroy();
+        destroy(accessor);
+        Assert.assertTrue(expectedExceptionThrown);
+    }
+
+    @Test
+    public void testNextAfterCloseFails() throws Exception {
+        IIndexAccessor accessor = createAccessor();
+        IIndexCursor cursor = createCursor(accessor);
+        List<ISearchPredicate> predicates = createSearchPredicates();
+        open(accessor, cursor, predicates.get(0));
+        cursor.close();
+        boolean expectedExceptionThrown = false;
+        try {
+            cursor.next();
+        } catch (Exception e) {
+            expectedExceptionThrown = true;
+        }
+        cursor.destroy();
+        destroy(accessor);
+        Assert.assertTrue(expectedExceptionThrown);
+    }
+
+    @Test
+    public void testDestroyWhileOpenFails() throws Exception {
+        IIndexAccessor accessor = createAccessor();
+        IIndexCursor cursor = createCursor(accessor);
+        List<ISearchPredicate> predicates = createSearchPredicates();
+        open(accessor, cursor, predicates.get(0));
+        boolean expectedExceptionThrown = false;
+        try {
+            cursor.destroy();
+        } catch (Exception e) {
+            expectedExceptionThrown = true;
+        }
+        cursor.close();
+        cursor.destroy();
+        destroy(accessor);
+        Assert.assertTrue(expectedExceptionThrown);
+    }
+
+    @Test
+    public void testOpenAfterDestroyFails() throws Exception {
+        IIndexAccessor accessor = createAccessor();
+        IIndexCursor cursor = createCursor(accessor);
+        List<ISearchPredicate> predicates = createSearchPredicates();
+        open(accessor, cursor, predicates.get(0));
+        cursor.close();
+        cursor.destroy();
+        boolean expectedExceptionThrown = false;
+        try {
+            open(accessor, cursor, predicates.get(0));
+        } catch (Exception e) {
+            expectedExceptionThrown = true;
+        }
+        destroy(accessor);
+        Assert.assertTrue(expectedExceptionThrown);
+    }
+
+    @Test
+    public void testCloseAfterDestroyFails() throws Exception {
+        IIndexAccessor accessor = createAccessor();
+        IIndexCursor cursor = createCursor(accessor);
+        List<ISearchPredicate> predicates = createSearchPredicates();
+        open(accessor, cursor, predicates.get(0));
+        cursor.close();
+        cursor.destroy();
+        boolean expectedExceptionThrown = false;
+        try {
+            cursor.close();
+        } catch (Exception e) {
+            expectedExceptionThrown = true;
+        }
+        destroy(accessor);
+        Assert.assertTrue(expectedExceptionThrown);
+    }
+
+    @Test
+    public void testNextAfterDestroyFails() throws Exception {
+        IIndexAccessor accessor = createAccessor();
+        IIndexCursor cursor = createCursor(accessor);
+        List<ISearchPredicate> predicates = createSearchPredicates();
+        open(accessor, cursor, predicates.get(0));
+        cursor.close();
+        cursor.destroy();
+        boolean expectedExceptionThrown = false;
+        try {
+            cursor.next();
+        } catch (Exception e) {
+            expectedExceptionThrown = true;
+        }
+        destroy(accessor);
+        Assert.assertTrue(expectedExceptionThrown);
+    }
+
+    @Test
+    public void testHasNextAfterDestroyFails() throws Exception {
+        IIndexAccessor accessor = createAccessor();
+        IIndexCursor cursor = createCursor(accessor);
+        List<ISearchPredicate> predicates = createSearchPredicates();
+        open(accessor, cursor, predicates.get(0));
+        cursor.close();
+        cursor.destroy();
+        boolean expectedExceptionThrown = false;
+        try {
+            cursor.hasNext();
+        } catch (Exception e) {
+            expectedExceptionThrown = true;
+        }
+        destroy(accessor);
+        Assert.assertTrue(expectedExceptionThrown);
+    }
+
+    @Test
+    public void testGetTupleReturnsNullAfterDestroy() throws Exception {
+        IIndexAccessor accessor = createAccessor();
+        IIndexCursor cursor = createCursor(accessor);
+        List<ISearchPredicate> predicates = createSearchPredicates();
+        open(accessor, cursor, predicates.get(0));
+        cursor.close();
+        cursor.destroy();
+        destroy(accessor);
+        Assert.assertNull(cursor.getTuple());
+    }
+
+    protected IIndexCursor createCursor(IIndexAccessor accessor) {
+        return accessor.createSearchCursor(false);
+    }
+
+    protected void open(IIndexAccessor accessor, IIndexCursor cursor, ISearchPredicate predicate)
+            throws HyracksDataException {
+        accessor.search(cursor, predicate);
+    }
+
+    protected abstract List<ISearchPredicate> createSearchPredicates() throws Exception;
+
+    protected abstract IIndexAccessor createAccessor() throws Exception;
+}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-btree/pom.xml
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-btree/pom.xml b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-btree/pom.xml
index 0e9c501..934ab65 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-btree/pom.xml
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-btree/pom.xml
@@ -16,17 +16,15 @@
  ! specific language governing permissions and limitations
  ! under the License.
  !-->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <artifactId>hyracks-storage-am-lsm-btree</artifactId>
-
   <parent>
     <groupId>org.apache.hyracks</groupId>
     <artifactId>hyracks</artifactId>
     <version>0.3.4-SNAPSHOT</version>
   </parent>
-
   <licenses>
     <license>
       <name>Apache License, Version 2.0</name>
@@ -35,11 +33,9 @@
       <comments>A business-friendly OSS license</comments>
     </license>
   </licenses>
-
   <properties>
     <root.dir>${basedir}/../..</root.dir>
   </properties>
-
   <dependencies>
     <dependency>
       <groupId>org.apache.hyracks</groupId>
@@ -92,4 +88,4 @@
       <version>${project.version}</version>
     </dependency>
   </dependencies>
-</project>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-btree/src/main/java/org/apache/hyracks/storage/am/lsm/btree/impls/ExternalBTreeOpContext.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-btree/src/main/java/org/apache/hyracks/storage/am/lsm/btree/impls/ExternalBTreeOpContext.java b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-btree/src/main/java/org/apache/hyracks/storage/am/lsm/btree/impls/ExternalBTreeOpContext.java
index b0c31ad..0b49b03 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-btree/src/main/java/org/apache/hyracks/storage/am/lsm/btree/impls/ExternalBTreeOpContext.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-btree/src/main/java/org/apache/hyracks/storage/am/lsm/btree/impls/ExternalBTreeOpContext.java
@@ -19,6 +19,7 @@
 package org.apache.hyracks.storage.am.lsm.btree.impls;
 
 import org.apache.hyracks.api.dataflow.value.IBinaryComparatorFactory;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.storage.am.btree.api.IBTreeLeafFrame;
 import org.apache.hyracks.storage.am.common.api.ITreeIndexFrameFactory;
 import org.apache.hyracks.storage.am.lsm.common.api.ILSMHarness;
@@ -73,4 +74,9 @@ public class ExternalBTreeOpContext extends AbstractLSMIndexOperationContext {
     public LSMBTreeCursorInitialState getSearchInitialState() {
         return searchInitialState;
     }
+
+    @Override
+    public void destroy() throws HyracksDataException {
+        // No Op
+    }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-btree/src/main/java/org/apache/hyracks/storage/am/lsm/btree/impls/ExternalBTreeWithBuddy.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-btree/src/main/java/org/apache/hyracks/storage/am/lsm/btree/impls/ExternalBTreeWithBuddy.java b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-btree/src/main/java/org/apache/hyracks/storage/am/lsm/btree/impls/ExternalBTreeWithBuddy.java
index 6993013..0be5556 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-btree/src/main/java/org/apache/hyracks/storage/am/lsm/btree/impls/ExternalBTreeWithBuddy.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-btree/src/main/java/org/apache/hyracks/storage/am/lsm/btree/impls/ExternalBTreeWithBuddy.java
@@ -331,9 +331,7 @@ public class ExternalBTreeWithBuddy extends AbstractLSMIndex implements ITreeInd
                 numElements += ((AbstractLSMWithBloomFilterDiskComponent) mergeOp.getMergingComponents().get(i))
                         .getBloomFilter().getNumElements();
             }
-
             componentBulkLoader = mergedComponent.createBulkLoader(1.0f, false, numElements, false, false, false);
-
             try {
                 while (buddyBtreeCursor.hasNext()) {
                     buddyBtreeCursor.next();
@@ -341,7 +339,7 @@ public class ExternalBTreeWithBuddy extends AbstractLSMIndex implements ITreeInd
                     componentBulkLoader.delete(tuple);
                 }
             } finally {
-                buddyBtreeCursor.destroy();
+                buddyBtreeCursor.close();
             }
         } else {
             componentBulkLoader = mergedComponent.createBulkLoader(1.0f, false, 0L, false, false, false);
@@ -354,7 +352,7 @@ public class ExternalBTreeWithBuddy extends AbstractLSMIndex implements ITreeInd
                 componentBulkLoader.add(frameTuple);
             }
         } finally {
-            cursor.destroy();
+            cursor.close();
         }
         componentBulkLoader.end();
         return mergedComponent;

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/4ff6a36d/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-btree/src/main/java/org/apache/hyracks/storage/am/lsm/btree/impls/ExternalBTreeWithBuddyOpContext.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-btree/src/main/java/org/apache/hyracks/storage/am/lsm/btree/impls/ExternalBTreeWithBuddyOpContext.java b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-btree/src/main/java/org/apache/hyracks/storage/am/lsm/btree/impls/ExternalBTreeWithBuddyOpContext.java
index 85d4ab2..1c74275 100644
--- a/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-btree/src/main/java/org/apache/hyracks/storage/am/lsm/btree/impls/ExternalBTreeWithBuddyOpContext.java
+++ b/hyracks-fullstack/hyracks/hyracks-storage-am-lsm-btree/src/main/java/org/apache/hyracks/storage/am/lsm/btree/impls/ExternalBTreeWithBuddyOpContext.java
@@ -19,6 +19,7 @@
 package org.apache.hyracks.storage.am.lsm.btree.impls;
 
 import org.apache.hyracks.api.dataflow.value.IBinaryComparatorFactory;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.storage.am.common.api.ITreeIndexFrameFactory;
 import org.apache.hyracks.storage.am.common.impls.NoOpOperationCallback;
 import org.apache.hyracks.storage.am.lsm.common.api.ILSMHarness;
@@ -75,4 +76,9 @@ public class ExternalBTreeWithBuddyOpContext extends AbstractLSMIndexOperationCo
     public LSMBTreeWithBuddyCursorInitialState getSearchInitialState() {
         return searchInitialState;
     }
+
+    @Override
+    public void destroy() throws HyracksDataException {
+        // No Op
+    }
 }