transaction.ready();
transaction.submit();
- verify(mockedDataTreeListener, timeout(1000)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture());
+ verify(mockedDataTreeListener, timeout(1000).times(2)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture());
final Collection<DataTreeCandidate> capturedValue = captorForChanges.getValue();
assertTrue(capturedValue.size() == 1);
final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
writeCrossShardContainer(transaction);
- verify(mockedDataTreeListener, timeout(1000)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture());
+ verify(mockedDataTreeListener, timeout(1000).times(2)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture());
final Collection<DataTreeCandidate> capturedValue = captorForChanges.getValue();
assertTrue(capturedValue.size() == 1);
.build();
//verify listeners have been notified
- verify(mockedDataTreeListener, timeout(1000).times(2)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture());
+ verify(mockedDataTreeListener, timeout(1000).times(4)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture());
final List<Collection<DataTreeCandidate>> capturedChanges = captorForChanges.getAllValues();
final List<Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>>> capturedSubtrees = captorForSubtrees.getAllValues();
- final DataTreeCandidate firstNotificationCandidate = capturedChanges.get(0).iterator().next();
+ final DataTreeCandidate firstNotificationCandidate = capturedChanges.get(2).iterator().next();
- assertTrue(capturedSubtrees.get(0).size() == 1);
+ assertTrue(capturedSubtrees.get(2).size() == 1);
assertEquals(testContainerVerificationNode, firstNotificationCandidate.getRootNode().getDataAfter().get());
- assertEquals(testContainerVerificationNode, capturedSubtrees.get(0).get(TEST_ID));
+ assertEquals(testContainerVerificationNode, capturedSubtrees.get(2).get(TEST_ID));
- final DataTreeCandidate secondNotificationCandidate = capturedChanges.get(1).iterator().next();
- assertTrue(capturedSubtrees.get(1).size() == 1);
+ final DataTreeCandidate secondNotificationCandidate = capturedChanges.get(3).iterator().next();
+ assertTrue(capturedSubtrees.get(3).size() == 1);
assertEquals(crossShardContainer, secondNotificationCandidate.getRootNode().getDataAfter().get());
- assertEquals(crossShardContainer, capturedSubtrees.get(1).get(TEST_ID));
+ assertEquals(crossShardContainer, capturedSubtrees.get(3).get(TEST_ID));
verifyNoMoreInteractions(mockedDataTreeListener);
}
cursor.close();
tx.submit().checkedGet();
- verify(mockedDataTreeListener, timeout(1000).times(2)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture());
+ verify(mockedDataTreeListener, timeout(1000).times(3)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture());
final List<Collection<DataTreeCandidate>> capturedValue = captorForChanges.getAllValues();
- assertTrue(capturedValue.size() == 2);
+ assertTrue(capturedValue.size() == 3);
- final ContainerNode capturedChange = (ContainerNode) capturedValue.get(0).iterator().next().getRootNode().getDataAfter().get();
+ final ContainerNode capturedChange = (ContainerNode) capturedValue.get(1).iterator().next().getRootNode().getDataAfter().get();
final ContainerNode innerContainerVerify = (ContainerNode) crossShardContainer.getChild(TestModel.INNER_CONTAINER_PATH.getLastPathArgument()).get();
assertEquals(innerContainerVerify, capturedChange);
package org.opendaylight.mdsal.dom.store.inmemory;
+import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeListener;
import org.opendaylight.mdsal.dom.spi.AbstractDOMDataTreeChangeListenerRegistration;
import org.opendaylight.mdsal.dom.spi.RegistrationTreeNode;
import org.opendaylight.mdsal.dom.spi.store.AbstractDOMStoreTreeChangePublisher;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidates;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger LOG = LoggerFactory.getLogger(AbstractDOMShardTreeChangePublisher.class);
- private YangInstanceIdentifier shardPath;
+ private final YangInstanceIdentifier shardPath;
private final Map<DOMDataTreeIdentifier, ChildShardContext> childShards;
private final DataTree dataTree;
private <L extends DOMDataTreeChangeListener> AbstractDOMDataTreeChangeListenerRegistration<L> setupListenerContext(final YangInstanceIdentifier listenerPath, final L listener) {
// we need to register the listener registration path based on the shards root
// we have to strip the shard path from the listener path and then register
- YangInstanceIdentifier strippedIdentifier = listenerPath;
+ YangInstanceIdentifier strippedIdentifier = listenerPath;
if (!shardPath.isEmpty()) {
strippedIdentifier = YangInstanceIdentifier.create(stripShardPath(listenerPath));
}
for (final ChildShardContext maybeAffected : childShards.values()) {
if (listenerPath.contains(maybeAffected.getPrefix().getRootIdentifier())) {
- // consumer has a subshard somewhere on lower level
+ // consumer has initialDataChangeEvent subshard somewhere on lower level
// register to the notification manager with snapshot and forward child notifications to parent
LOG.debug("Adding new subshard{{}} to listener at {}", maybeAffected.getPrefix(), listenerPath);
subshardListener.addSubshard(maybeAffected);
} else if (maybeAffected.getPrefix().getRootIdentifier().contains(listenerPath)) {
// bind path is inside subshard
// TODO can this happen? seems like in ShardedDOMDataTree we are already registering to the lowest shard possible
- throw new UnsupportedOperationException("Listener should be registered directly into a subshard");
+ throw new UnsupportedOperationException("Listener should be registered directly into initialDataChangeEvent subshard");
}
}
+
+ initialDataChangeEvent(listenerPath, listener);
+
return reg;
}
+ private <L extends DOMDataTreeChangeListener> void initialDataChangeEvent(final YangInstanceIdentifier listenerPath, final L listener) {
+ // FIXME Add support for wildcard listeners
+ final Optional<NormalizedNode<?, ?>> preExistingData = dataTree.takeSnapshot().readNode(listenerPath);
+ final DataTreeCandidate initialCandidate;
+ if (preExistingData.isPresent()) {
+ initialCandidate = DataTreeCandidates.fromNormalizedNode(listenerPath, preExistingData.get());
+ } else {
+ initialCandidate = DataTreeCandidates.newDataTreeCandidate(listenerPath,
+ new EmptyDataTreeCandidateNode(listenerPath.getLastPathArgument()));
+ }
+
+ listener.onDataTreeChanged(Collections.singleton(initialCandidate));
+ }
+
private <L extends DOMDataTreeChangeListener> AbstractDOMDataTreeChangeListenerRegistration<L> setupContextWithoutSubshards(final YangInstanceIdentifier listenerPath, final DOMDataTreeListenerWithSubshards listener) {
LOG.debug("Registering root listener at {}", listenerPath);
final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> node = findNodeFor(listenerPath.getPathArguments());
void addSubshard(final ChildShardContext context) {
Preconditions.checkState(context.getShard() instanceof DOMStoreTreeChangePublisher,
- "All subshards that are a part of ListenerContext need to be listenable");
+ "All subshards that are initialDataChangeEvent part of ListenerContext need to be listenable");
final DOMStoreTreeChangePublisher listenableShard = (DOMStoreTreeChangePublisher) context.getShard();
// since this is going into subshard we want to listen for ALL changes in the subshard
modification.ready();
try {
dataTree.validate(modification);
- } catch (DataValidationFailedException e) {
+ } catch (final DataValidationFailedException e) {
LOG.error("Validation failed for built modification", e);
throw new RuntimeException("Notification validation failed", e);
}
// strip nodes we dont need since this listener doesn't have to be registered at the root of the DataTree
DataTreeCandidateNode modifiedChild = dataTree.prepare(modification).getRootNode();
for (final PathArgument pathArgument : listenerPath.getPathArguments()) {
- // there should be no null pointers since we wouldn't get a notification change
- // if there was no node modified at the listener's location
modifiedChild = modifiedChild.getModifiedChild(pathArgument);
}
+ if (modifiedChild == null) {
+ modifiedChild = new EmptyDataTreeCandidateNode(listenerPath.getLastPathArgument());
+ }
+
return DataTreeCandidates.newDataTreeCandidate(listenerPath, modifiedChild);
}
}
+
+ private static final class EmptyDataTreeCandidateNode implements DataTreeCandidateNode {
+
+ private final PathArgument identifier;
+
+ EmptyDataTreeCandidateNode(final PathArgument identifier) {
+ Preconditions.checkNotNull(identifier, "Identifier should not be null");
+ this.identifier = identifier;
+ }
+
+ @Nonnull
+ @Override
+ public PathArgument getIdentifier() {
+ return identifier;
+ }
+
+ @Nonnull
+ @Override
+ public Collection<DataTreeCandidateNode> getChildNodes() {
+ return Collections.<DataTreeCandidateNode>emptySet();
+ }
+
+ @Nullable
+ @Override
+ public DataTreeCandidateNode getModifiedChild(final PathArgument identifier) {
+ return null;
+ }
+
+ @Nonnull
+ @Override
+ public ModificationType getModificationType() {
+ return ModificationType.UNMODIFIED;
+ }
+
+ @Nonnull
+ @Override
+ public Optional<NormalizedNode<?, ?>> getDataAfter() {
+ return Optional.absent();
+ }
+
+ @Nonnull
+ @Override
+ public Optional<NormalizedNode<?, ?>> getDataBefore() {
+ return Optional.absent();
+ }
+ }
+
}
\ No newline at end of file
*/
package org.opendaylight.mdsal.dom.store.inmemory;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.opendaylight.mdsal.dom.store.inmemory.TestUtils.DATA_TREE;
import static org.opendaylight.mdsal.dom.store.inmemory.TestUtils.resetMocks;
+import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import java.util.Collection;
import java.util.Map;
import javax.annotation.Nonnull;
import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.MockitoAnnotations;
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
public class AbstractDOMShardTreeChangePublisherTest extends AbstractDOMShardTreeChangePublisher {
private static final Map<DOMDataTreeIdentifier, ChildShardContext> CHILD_SHARDS =
ImmutableMap.of(DOM_DATA_TREE_IDENTIFIER, CHILD_SHARD_CONTEXT);
+ @Captor
+ private ArgumentCaptor<Collection<DataTreeCandidate>> captorForChanges;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
@Test
public void registerTreeChangeListenerTest() throws Exception {
final DOMDataTreeChangeListener domDataTreeChangeListener = mock(DOMDataTreeChangeListener.class);
final ListenerRegistration listenerRegistration = mock(ListenerRegistration.class);
+ final DataTreeSnapshot initialSnapshot = mock(DataTreeSnapshot.class);
+ final NormalizedNode<?, ?> initialData = mock(NormalizedNode.class);
+ doReturn(initialSnapshot).when(DATA_TREE).takeSnapshot();
+ doReturn(Optional.of(initialData)).when(initialSnapshot).readNode(any());
+ doNothing().when(domDataTreeChangeListener).onDataTreeChanged(any());
+
doReturn(listenerRegistration)
.when(READABLE_WRITEABLE_DOM_DATA_TREE_SHARD).registerTreeChangeListener(any(), any());
assertNotNull(this.registerTreeChangeListener(YANG_INSTANCE_IDENTIFIER, domDataTreeChangeListener));
verify(READABLE_WRITEABLE_DOM_DATA_TREE_SHARD).registerTreeChangeListener(any(), any());
+
+ verify(domDataTreeChangeListener)
+ .onDataTreeChanged(captorForChanges.capture());
+
+ final Collection<DataTreeCandidate> initialChange = captorForChanges.getValue();
+
+ assertTrue(initialChange.size() == 1);
+ initialChange.forEach(dataTreeCandidate ->
+ assertEquals(dataTreeCandidate.getRootPath(), YANG_INSTANCE_IDENTIFIER));
+ initialChange.forEach(dataTreeCandidate ->
+ assertSame(dataTreeCandidate.getRootNode().getDataAfter().get(), initialData));
}
@Test(expected = UnsupportedOperationException.class)
final Map<DOMDataTreeIdentifier, ChildShardContext> childShardContextMap =
ImmutableMap.of(domDataTreeIdentifier, childShardContext);
- AbstractDOMShardTreeChangePublisherTest abstractDOMShardTreeChangePublisherTest =
+ final AbstractDOMShardTreeChangePublisherTest abstractDOMShardTreeChangePublisherTest =
new AbstractDOMShardTreeChangePublisherTest(childShardContextMap);
abstractDOMShardTreeChangePublisherTest
.registerTreeChangeListener(YANG_INSTANCE_IDENTIFIER, domDataTreeChangeListener);
super(DATA_TREE, YANG_INSTANCE_IDENTIFIER, CHILD_SHARDS);
}
- private AbstractDOMShardTreeChangePublisherTest(Map<DOMDataTreeIdentifier, ChildShardContext> childShardContextMap) {
+ private AbstractDOMShardTreeChangePublisherTest(final Map<DOMDataTreeIdentifier, ChildShardContext> childShardContextMap) {
super(DATA_TREE, YANG_INSTANCE_IDENTIFIER, childShardContextMap);
}
@Override
- protected void notifyListeners(@Nonnull Collection<AbstractDOMDataTreeChangeListenerRegistration<?>> registrations,
- @Nonnull YangInstanceIdentifier path, @Nonnull DataTreeCandidateNode node) {
+ protected void notifyListeners(@Nonnull final Collection<AbstractDOMDataTreeChangeListenerRegistration<?>> registrations,
+ @Nonnull final YangInstanceIdentifier path, @Nonnull final DataTreeCandidateNode node) {
// NOOP
}
@Override
- protected void registrationRemoved(@Nonnull AbstractDOMDataTreeChangeListenerRegistration<?> registration) {
+ protected void registrationRemoved(@Nonnull final AbstractDOMDataTreeChangeListenerRegistration<?> registration) {
// NOOP
}
@After
- public void reset(){
+ public void reset() {
resetMocks();
}
}
\ No newline at end of file
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
final DOMDataTreeChangeListener domDataTreeChangeListener = mock(DOMDataTreeChangeListener.class);
final ListenerRegistration listenerRegistration = mock(ListenerRegistration.class);
doReturn(listenerRegistration).when(domDataTreeShard).registerTreeChangeListener(any(), any());
+ doNothing().when(domDataTreeChangeListener).onDataTreeChanged(any());
inMemoryDOMDataTreeShard.registerTreeChangeListener(YangInstanceIdentifier.EMPTY, domDataTreeChangeListener);
verify(domDataTreeShard, atLeastOnce()).registerTreeChangeListener(any(), any());