*/
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;
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) {
}
ProducerLayout reshard(final Map<DOMDataTreeIdentifier, DOMDataTreeShard> newShardMap) {
+ close();
return new ProducerLayout(newShardMap, mapIdsToProducer(newShardMap), children);
}
"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);
+ }
+
}
void subshardAdded(final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
checkIdle();
-
layout = layout.reshard(shardMap);
}
if (CLOSED_UPDATER.compareAndSet(this, 0, 1)) {
synchronized (this) {
dataTree.destroyProducer(this);
+ layout.close();
}
}
}
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;
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);
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();
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;
doReturn(new TestDOMShardWriteTransaction()).when(mockedProducer).createTransaction();
doReturn(mockedProducer).when(rootShard).createProducer(any(Collection.class));
+ doNothing().when(mockedProducer).close();
final ShardedDOMDataTree shardedDOMDataTree =
new ShardedDOMDataTree();
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();
}
@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);
}
}
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);
+ }
}
}
producer.getModificationFactory().createModification((CursorAwareDataTreeSnapshot) snapshot), dataTree,
shardChangePublisher, executor);
}
+
+ @VisibleForTesting
+ public Collection<InMemoryDOMDataTreeShardProducer> getProducers() {
+ return producers;
+ }
}
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;
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);
return prefixes;
}
+ @NonNull InMemoryDOMDataTreeShard getParentShard() {
+ return parentShard;
+ }
+
InMemoryShardDataModificationFactory getModificationFactory() {
return modificationFactory;
}
void setModificationFactory(final InMemoryShardDataModificationFactory modificationFactory) {
+ this.getModificationFactory().close();
this.modificationFactory = requireNonNull(modificationFactory);
}
}
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;
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);
+ }
}
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;
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);
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));
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;
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);
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.
* @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() {
+
+ }
}
return identifier;
}
+ public DOMDataTreeShardProducer getProducer() {
+ return producer;
+ }
+
public ListenableFuture<Boolean> validate() {
return tx.validate();
}