Fix up release old producers to avoid memory leak 01/76501/22
authorhan <han.jie@zte.com.cn>
Sun, 30 Sep 2018 08:49:54 +0000 (16:49 +0800)
committerRobert Varga <nite@hq.sk>
Tue, 27 Nov 2018 11:03:08 +0000 (11:03 +0000)
- It should close all correlative old 'InMemoryDOMDataTreeShardProducer'
  when close a 'ShardedDOMDataTreeProducer', or a memory leak happens
  for repeatly creating and closing the same producer as well as in the
  case of attach/detach subshards.

JIRA: MDSAL-386
Change-Id: I9c6ad6b63de4dfc7b75a315fce23a5da3be9d06d
Signed-off-by: Jie Han <han.jie@zte.com.cn>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
12 files changed:
dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/ProducerLayout.java
dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/ShardedDOMDataTreeProducer.java
dom/mdsal-dom-broker/src/test/java/org/opendaylight/mdsal/dom/broker/ShardedDOMDataTreeProducerMultiShardTest.java
dom/mdsal-dom-broker/src/test/java/org/opendaylight/mdsal/dom/broker/ShardedDOMDataTreeProducerSingleShardTest.java
dom/mdsal-dom-broker/src/test/java/org/opendaylight/mdsal/dom/broker/ShardedDOMDataWriteTransactionTest.java
dom/mdsal-dom-inmemory-datastore/src/main/java/org/opendaylight/mdsal/dom/store/inmemory/InMemoryDOMDataTreeShard.java
dom/mdsal-dom-inmemory-datastore/src/main/java/org/opendaylight/mdsal/dom/store/inmemory/InMemoryDOMDataTreeShardProducer.java
dom/mdsal-dom-inmemory-datastore/src/main/java/org/opendaylight/mdsal/dom/store/inmemory/InMemoryShardDataModificationFactory.java
dom/mdsal-dom-inmemory-datastore/src/test/java/org/opendaylight/mdsal/dom/store/inmemory/InMemoryDOMDataTreeShardTest.java
dom/mdsal-dom-inmemory-datastore/src/test/java/org/opendaylight/mdsal/dom/store/inmemory/TestUtils.java
dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/shard/DOMDataTreeShardProducer.java
dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/shard/ForeignShardModificationContext.java

index e383d5be6a0a4849c03d0149922cb65d2964beed..6b5a0a531bd116122bfba2f76df6e679f45673bb 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.mdsal.dom.broker;
 
+import static java.util.Objects.requireNonNull;
+
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.BiMap;
@@ -42,8 +44,8 @@ final class ProducerLayout {
             final BiMap<DOMDataTreeIdentifier, DOMDataTreeShardProducer> idToProducer,
             final Map<DOMDataTreeIdentifier, DOMDataTreeProducer> children) {
         this.shardMap = ImmutableMap.copyOf(shardMap);
-        this.idToProducer = Preconditions.checkNotNull(idToProducer);
-        this.children = Preconditions.checkNotNull(children);
+        this.idToProducer = requireNonNull(idToProducer);
+        this.children = requireNonNull(children);
     }
 
     static ProducerLayout create(final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
@@ -87,6 +89,7 @@ final class ProducerLayout {
     }
 
     ProducerLayout reshard(final Map<DOMDataTreeIdentifier, DOMDataTreeShard> newShardMap) {
+        close();
         return new ProducerLayout(newShardMap, mapIdsToProducer(newShardMap), children);
     }
 
@@ -134,4 +137,9 @@ final class ProducerLayout {
                 "Cannot create transaction since the producer is not mapped to any shard");
         return Maps.transformValues(idToProducer, DOMDataTreeShardProducer::createTransaction);
     }
+
+    void close() {
+        idToProducer.values().forEach(DOMDataTreeShardProducer::close);
+    }
+
 }
index e842e433fd2c1a9e43f9c8d57e0a060e53735a33..f377c9322fd23e78e39002ba3e68e294bde86095 100644 (file)
@@ -79,7 +79,6 @@ class ShardedDOMDataTreeProducer implements DOMDataTreeProducer {
 
     void subshardAdded(final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
         checkIdle();
-
         layout = layout.reshard(shardMap);
     }
 
@@ -195,6 +194,7 @@ class ShardedDOMDataTreeProducer implements DOMDataTreeProducer {
         if (CLOSED_UPDATER.compareAndSet(this, 0, 1)) {
             synchronized (this) {
                 dataTree.destroyProducer(this);
+                layout.close();
             }
         }
     }
index cd2ed50643bf2392ad35501850402cfc39b9300f..a497ffff6f2114f7503b2dc3dfd7a889d3482c45 100644 (file)
@@ -20,6 +20,7 @@ import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
+import com.google.common.collect.Iterables;
 import com.google.common.util.concurrent.Futures;
 import java.util.Collection;
 import java.util.Collections;
@@ -252,6 +253,134 @@ public class ShardedDOMDataTreeProducerMultiShardTest {
         verifyNoMoreInteractions(mockedDataTreeListener);
     }
 
+    @Test
+    public void testMultipleShardsProducerClose() throws Exception {
+        final InMemoryDOMDataTreeShard innerShard = InMemoryDOMDataTreeShard.create(INNER_CONTAINER_ID, executor, 1);
+        innerShard.onGlobalContextUpdated(SCHEMA_CONTEXT);
+
+        assertTrue(rootShard.getProducers().isEmpty());
+
+        final DOMDataTreeProducer innerShardRegProducer =
+                dataTreeService.createProducer(Collections.singletonList(INNER_CONTAINER_ID));
+        assertTrue(rootShard.getProducers().size() == 1);
+        final DOMDataTreeShardProducer rootShardProducer = Iterables.getOnlyElement(rootShard.getProducers());
+        assertEquals(rootShardProducer.getPrefixes().toString(),
+                Collections.singletonList(INNER_CONTAINER_ID).toString());
+
+        dataTreeService.registerDataTreeShard(INNER_CONTAINER_ID, innerShard, innerShardRegProducer);
+
+        assertTrue(rootShard.getProducers().isEmpty());
+        assertTrue(innerShard.getProducers().size() == 1);
+        final DOMDataTreeShardProducer innerShardProducer = Iterables.getOnlyElement(innerShard.getProducers());
+        assertEquals(innerShardProducer.getPrefixes().toString(),
+                Collections.singletonList(INNER_CONTAINER_ID).toString());
+
+        innerShardRegProducer.close();
+        assertTrue(rootShard.getProducers().isEmpty());
+        assertTrue(innerShard.getProducers().isEmpty());
+
+        final DOMDataTreeProducer testProducer =
+                dataTreeService.createProducer(Collections.singletonList(TEST_ID));
+        assertTrue(rootShard.getProducers().size() == 1);
+        final DOMDataTreeShardProducer rootShardProducer2 = Iterables.getOnlyElement(rootShard.getProducers());
+        assertEquals(rootShardProducer2.getPrefixes().toString(),
+                Collections.singletonList(TEST_ID).toString());
+
+        assertTrue(innerShard.getProducers().size() == 1);
+        final DOMDataTreeShardProducer innerShardProducer2 = Iterables.getOnlyElement(innerShard.getProducers());
+        assertEquals(innerShardProducer2.getPrefixes().toString(),
+                Collections.singletonList(INNER_CONTAINER_ID).toString());
+
+        testProducer.close();
+        assertTrue(rootShard.getProducers().isEmpty());
+        assertTrue(innerShard.getProducers().isEmpty());
+    }
+
+    @Test
+    public void testMultipleShardsChildProducerClose() throws Exception {
+        final InMemoryDOMDataTreeShard innerShard = InMemoryDOMDataTreeShard.create(INNER_CONTAINER_ID, executor, 1);
+        innerShard.onGlobalContextUpdated(SCHEMA_CONTEXT);
+
+        final DOMDataTreeProducer innerShardRegProducer =
+                dataTreeService.createProducer(Collections.singletonList(INNER_CONTAINER_ID));
+        dataTreeService.registerDataTreeShard(INNER_CONTAINER_ID, innerShard, innerShardRegProducer);
+        innerShardRegProducer.close();
+        assertTrue(rootShard.getProducers().isEmpty());
+        assertTrue(innerShard.getProducers().isEmpty());
+
+        final DOMDataTreeProducer testProducer =
+                dataTreeService.createProducer(Collections.singletonList(TEST_ID));
+        final DOMDataTreeProducer testChildProducer = testProducer.createProducer(
+                Collections.singletonList(INNER_CONTAINER_ID));
+        assertTrue(rootShard.getProducers().size() == 1);
+        assertTrue(innerShard.getProducers().size() == 2);
+
+        final DOMDataTreeShardProducer rootShardProducer = Iterables.getOnlyElement(rootShard.getProducers());
+        assertEquals(rootShardProducer.getPrefixes().toString(),
+                Collections.singletonList(TEST_ID).toString());
+
+        for (DOMDataTreeShardProducer producer : innerShard.getProducers()) {
+            assertEquals(producer.getPrefixes().toString(),
+                    Collections.singletonList(INNER_CONTAINER_ID).toString());
+        }
+
+        testProducer.close();
+        assertTrue(rootShard.getProducers().isEmpty());
+        assertTrue(innerShard.getProducers().size() == 1);
+        final DOMDataTreeShardProducer innerShardProducer = Iterables.getOnlyElement(innerShard.getProducers());
+        assertEquals(innerShardProducer.getPrefixes().toString(),
+                Collections.singletonList(INNER_CONTAINER_ID).toString());
+
+        testChildProducer.close();
+        assertTrue(rootShard.getProducers().isEmpty());
+        assertTrue(innerShard.getProducers().isEmpty());
+    }
+
+    @Test
+    public void testMultipleShardsProducerCloseForSubshardAttached() throws Exception {
+        final InMemoryDOMDataTreeShard innerShard = InMemoryDOMDataTreeShard.create(INNER_CONTAINER_ID, executor, 1);
+        innerShard.onGlobalContextUpdated(SCHEMA_CONTEXT);
+
+        final DOMDataTreeProducer innerShardRegProducer =
+                dataTreeService.createProducer(Collections.singletonList(INNER_CONTAINER_ID));
+        dataTreeService.registerDataTreeShard(INNER_CONTAINER_ID, innerShard, innerShardRegProducer);
+        innerShardRegProducer.close();
+        assertTrue(rootShard.getProducers().isEmpty());
+        assertTrue(innerShard.getProducers().isEmpty());
+
+        final DOMDataTreeProducer testProducer =
+                dataTreeService.createProducer(Collections.singletonList(TEST_ID));
+        assertTrue(rootShard.getProducers().size() == 1);
+        assertTrue(innerShard.getProducers().size() == 1);
+
+        final DOMDataTreeShardProducer rootShardProducer = Iterables.getOnlyElement(rootShard.getProducers());
+        assertEquals(rootShardProducer.getPrefixes().toString(),
+                Collections.singletonList(TEST_ID).toString());
+
+        final DOMDataTreeShardProducer innerShardProducer = Iterables.getOnlyElement(innerShard.getProducers());
+        assertEquals(innerShardProducer.getPrefixes().toString(),
+                Collections.singletonList(INNER_CONTAINER_ID).toString());
+
+        final InMemoryDOMDataTreeShard test2Shard = InMemoryDOMDataTreeShard.create(TEST2_ID, executor, 1);
+        innerShard.onGlobalContextUpdated(SCHEMA_CONTEXT);
+
+        final DOMDataTreeProducer test2ShardRegProducer =
+                dataTreeService.createProducer(Collections.singletonList(TEST2_ID));
+        dataTreeService.registerDataTreeShard(TEST2_ID, test2Shard, test2ShardRegProducer);
+        test2ShardRegProducer.close();
+
+        assertTrue(rootShard.getProducers().size() == 1);
+        assertTrue(innerShard.getProducers().size() == 1);
+
+        final DOMDataTreeShardProducer rootShardProducer2 = Iterables.getOnlyElement(rootShard.getProducers());
+        assertEquals(rootShardProducer2.getPrefixes().toString(),
+                Collections.singletonList(TEST_ID).toString());
+
+        final DOMDataTreeShardProducer innerShardProducer2 = Iterables.getOnlyElement(innerShard.getProducers());
+        assertEquals(innerShardProducer2.getPrefixes().toString(),
+                Collections.singletonList(INNER_CONTAINER_ID).toString());
+    }
+
     @Test
     public void testMultipleWritesIntoSingleShard() throws Exception {
         final DOMDataTreeListener mockedDataTreeListener = mock(DOMDataTreeListener.class);
index cd1558642b4672156d60e19f8b5fdde745625f5c..0809cec4f4fa8a14e6ea8c99ff3bad01f08b45f8 100644 (file)
@@ -87,6 +87,7 @@ public class ShardedDOMDataTreeProducerSingleShardTest {
         doReturn("rootShard").when(rootShard).toString();
         doReturn(producerMock).when(rootShard).createProducer(any(Collection.class));
         doReturn(shardTxMock).when(producerMock).createTransaction();
+        doNothing().when(producerMock).close();
         doNothing().when(shardTxMock).ready();
         doReturn(Futures.immediateFuture(null)).when(shardTxMock).submit();
 
index b277ea92a6e8ee7347e5d0e8f8c813f989b166b9..035acaca996f8d7c9d26fd353928ad5efb816feb 100644 (file)
@@ -13,6 +13,7 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.MockitoAnnotations.initMocks;
 
@@ -59,6 +60,7 @@ public class ShardedDOMDataWriteTransactionTest {
 
         doReturn(new TestDOMShardWriteTransaction()).when(mockedProducer).createTransaction();
         doReturn(mockedProducer).when(rootShard).createProducer(any(Collection.class));
+        doNothing().when(mockedProducer).close();
 
         final ShardedDOMDataTree shardedDOMDataTree =
                 new ShardedDOMDataTree();
index 393e1cd2ef83ed1fe4323cb7a58b8bb351fdde4e..e03795b2143f0b85bd26303c44160ce13235193f 100644 (file)
@@ -117,6 +117,7 @@ public class InMemoryDOMDataTreeShard implements ReadableWriteableDOMDataTreeSha
     public void onChildDetached(final DOMDataTreeIdentifier childPrefix, final DOMDataTreeShard child) {
         childShards.remove(childPrefix);
         childShardsTable.remove(childPrefix);
+        //FIXME: Producers not being affected could be skipped over.
         updateProducers();
     }
 
@@ -165,7 +166,7 @@ public class InMemoryDOMDataTreeShard implements ReadableWriteableDOMDataTreeSha
     @Override
     public InMemoryDOMDataTreeShardProducer createProducer(final Collection<DOMDataTreeIdentifier> prefixes) {
         for (final DOMDataTreeIdentifier prodPrefix : prefixes) {
-            checkArgument(prefix.contains(prodPrefix), "Prefix %s is not contained under shart root", prodPrefix,
+            checkArgument(prefix.contains(prodPrefix), "Prefix %s is not contained under shard root", prodPrefix,
                 prefix);
         }
 
@@ -176,8 +177,10 @@ public class InMemoryDOMDataTreeShard implements ReadableWriteableDOMDataTreeSha
     }
 
     void closeProducer(final InMemoryDOMDataTreeShardProducer producer) {
-        if (!producers.remove(producer)) {
-            LOG.warn("Producer {} not found in shard {}", producer, this);
+        synchronized (this) {
+            if (!producers.remove(producer)) {
+                LOG.warn("Producer {} not found in shard {}", producer, this);
+            }
         }
     }
 
@@ -249,4 +252,9 @@ public class InMemoryDOMDataTreeShard implements ReadableWriteableDOMDataTreeSha
                 producer.getModificationFactory().createModification((CursorAwareDataTreeSnapshot) snapshot), dataTree,
                 shardChangePublisher, executor);
     }
+
+    @VisibleForTesting
+    public Collection<InMemoryDOMDataTreeShardProducer> getProducers() {
+        return producers;
+    }
 }
index 17e062464afe083013fac06e5aca305a6511f952..a91362d2328fcbbb1b36c1aceebe53a260813dbb 100644 (file)
@@ -14,6 +14,7 @@ import com.google.common.collect.ImmutableSet;
 import java.util.Collection;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.mdsal.dom.spi.shard.DOMDataTreeShardProducer;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
@@ -130,6 +131,19 @@ class InMemoryDOMDataTreeShardProducer implements DOMDataTreeShardProducer {
         return ret;
     }
 
+    @Override
+    public void close() {
+        final Shutdown shutdown = new Shutdown("Producer closed");
+        if (!STATE_UPDATER.compareAndSet(this, idleState, shutdown)) {
+            throw new IllegalStateException("Producer " + this + " in unexpected state " + state);
+        }
+
+        // FIXME: This call is ugly, it's better to clean up all by exposing only one entrance,
+        // 'closeProducer' of shard or this 'close'.
+        getParentShard().closeProducer(this);
+        getModificationFactory().close();
+    }
+
     void transactionReady(final InmemoryDOMDataTreeShardWriteTransaction tx, final DataTreeModification modification) {
         final State localState = state;
         LOG.debug("Transaction was readied {}, current state {}", tx.getIdentifier(), localState);
@@ -204,11 +218,16 @@ class InMemoryDOMDataTreeShardProducer implements DOMDataTreeShardProducer {
         return prefixes;
     }
 
+    @NonNull InMemoryDOMDataTreeShard getParentShard() {
+        return parentShard;
+    }
+
     InMemoryShardDataModificationFactory getModificationFactory() {
         return modificationFactory;
     }
 
     void setModificationFactory(final InMemoryShardDataModificationFactory modificationFactory) {
+        this.getModificationFactory().close();
         this.modificationFactory = requireNonNull(modificationFactory);
     }
 }
index ffb487b15d6cf06cb7b7b27ad2228cb2edc02542..c772ee90ed7f4ff6eeff9ee8f079d7fa5b8faf06 100644 (file)
@@ -12,6 +12,7 @@ import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;
 import java.util.Map;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.mdsal.dom.spi.shard.DOMDataTreeShardProducer;
 import org.opendaylight.mdsal.dom.spi.shard.ForeignShardModificationContext;
 import org.opendaylight.mdsal.dom.spi.shard.WriteableModificationNode;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
@@ -44,4 +45,9 @@ final class InMemoryShardDataModificationFactory {
     ShardDataModification createModification(final CursorAwareDataTreeSnapshot snapshot) {
         return new ShardDataModification(new ShardRootModificationContext(root, snapshot), children, childShards);
     }
+
+    void close() {
+        childShards.values().stream().map(ForeignShardModificationContext::getProducer)
+                .forEach(DOMDataTreeShardProducer::close);
+    }
 }
index 181043d2af85b2bb2586f7e61249f5a3c8ff3d8c..28488bcba706f3c8901a95778b7b15fa6fc3dd35 100644 (file)
@@ -29,7 +29,6 @@ import org.junit.Test;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
-import org.opendaylight.mdsal.dom.spi.shard.ReadableWriteableDOMDataTreeShard;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -48,9 +47,11 @@ public class InMemoryDOMDataTreeShardTest {
                 new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION,
                         YangInstanceIdentifier.of(QName.create("", "Test")));
 
-        final ReadableWriteableDOMDataTreeShard domDataTreeShard = mock(ReadableWriteableDOMDataTreeShard.class);
+        final InMemoryDOMDataTreeShard domDataTreeShard = mock(InMemoryDOMDataTreeShard.class);
         doReturn("testReadableWriteableDOMDataTreeShard").when(domDataTreeShard).toString();
         doReturn(DOM_DATA_TREE_SHARD_PRODUCER).when(domDataTreeShard).createProducer(any());
+        doReturn(domDataTreeShard).when(DOM_DATA_TREE_SHARD_PRODUCER).getParentShard();
+        doNothing().when(DOM_DATA_TREE_SHARD_PRODUCER).close();
 
         assertFalse(inMemoryDOMDataTreeShard.getChildShards().containsValue(domDataTreeShard));
         inMemoryDOMDataTreeShard.onChildAttached(DOM_DATA_TREE_IDENTIFIER, domDataTreeShard);
@@ -63,7 +64,7 @@ public class InMemoryDOMDataTreeShardTest {
         final InMemoryDOMDataTreeShardProducer mockProducer = mock(InMemoryDOMDataTreeShardProducer.class);
         doReturn(prefixes).when(mockProducer).getPrefixes();
         doReturn(inMemoryDOMDataTreeShard.createModificationFactory(prefixes))
-            .when(mockProducer).getModificationFactory();
+                .when(mockProducer).getModificationFactory();
 
         inMemoryDOMDataTreeShard.onGlobalContextUpdated(createTestContext());
         inMemoryDOMDataTreeShard.createTransaction("", mockProducer, mock(CursorAwareDataTreeSnapshot.class));
index 3af5bfa469da7e0575d5c9d35985802e7d58c79a..768eaba4f4f7c0849e00f44e7a2a0330729a89c0 100644 (file)
@@ -17,8 +17,6 @@ import java.util.Map;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteCursor;
-import org.opendaylight.mdsal.dom.spi.shard.DOMDataTreeShardProducer;
-import org.opendaylight.mdsal.dom.spi.shard.DOMDataTreeShardWriteTransaction;
 import org.opendaylight.mdsal.dom.spi.shard.ForeignShardModificationContext;
 import org.opendaylight.mdsal.dom.spi.shard.WriteCursorStrategy;
 import org.opendaylight.mdsal.dom.spi.shard.WriteableModificationNode;
@@ -54,10 +52,11 @@ final class TestUtils {
 
     static final WriteCursorStrategy WRITE_CURSOR_STRATEGY = mock(WriteCursorStrategy.class);
 
-    static final DOMDataTreeShardProducer DOM_DATA_TREE_SHARD_PRODUCER = mock(DOMDataTreeShardProducer.class);
+    static final InMemoryDOMDataTreeShardProducer DOM_DATA_TREE_SHARD_PRODUCER =
+            mock(InMemoryDOMDataTreeShardProducer.class);
 
-    static final DOMDataTreeShardWriteTransaction DOM_DATA_TREE_SHARD_WRITE_TRANSACTION =
-            mock(DOMDataTreeShardWriteTransaction.class);
+    static final InmemoryDOMDataTreeShardWriteTransaction DOM_DATA_TREE_SHARD_WRITE_TRANSACTION =
+            mock(InmemoryDOMDataTreeShardWriteTransaction.class);
 
     static final DataTree DATA_TREE = mock(DataTree.class);
 
index cb1f2b1c808e8a7bc03242635ee45c216aa40e49..0ae5876c673431b720fd1dd6fbdac6c55ea928de 100644 (file)
@@ -11,9 +11,10 @@ import com.google.common.annotations.Beta;
 import java.util.Collection;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.yangtools.concepts.Registration;
 
 @Beta
-public interface DOMDataTreeShardProducer {
+public interface DOMDataTreeShardProducer extends Registration {
     /**
      * Return the collection of tree identifiers to which this producer is bound. This collection
      * is constant during the lifetime of a producer.
@@ -30,4 +31,14 @@ public interface DOMDataTreeShardProducer {
      * @throws IllegalStateException if a previous transaction has not been closed
      */
     @NonNull DOMDataTreeShardWriteTransaction createTransaction();
+
+    /**
+     * Close this producer, releasing all resources. Default implementation does nothing, implementations should provide
+     * an implementation.
+     */
+    // FIXME: 4.0.0: make this method non-default
+    @Override
+    default void close() {
+
+    }
 }
index bf14d7399697d2040d7f7c9ce32fbd849a37322f..ed7d00f62c5a601600555512fe44945598ff40d4 100644 (file)
@@ -79,6 +79,10 @@ public final class ForeignShardModificationContext implements Identifiable<DOMDa
         return identifier;
     }
 
+    public DOMDataTreeShardProducer getProducer() {
+        return producer;
+    }
+
     public ListenableFuture<Boolean> validate() {
         return tx.validate();
     }