* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-
package org.opendaylight.mdsal.dom.store.inmemory;
-import com.google.common.base.Preconditions;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.stream.Collectors;
-import javax.annotation.Nonnull;
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.shard.ChildShardContext;
import org.opendaylight.mdsal.dom.spi.store.AbstractDOMStoreTreeChangePublisher;
import org.opendaylight.mdsal.dom.spi.store.DOMStoreTreeChangePublisher;
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.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+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.DataTreeCandidateNodes;
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.impl.schema.builder.api.NormalizedNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-abstract class AbstractDOMShardTreeChangePublisher extends AbstractDOMStoreTreeChangePublisher implements DOMStoreTreeChangePublisher {
+abstract class AbstractDOMShardTreeChangePublisher extends AbstractDOMStoreTreeChangePublisher {
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;
protected AbstractDOMShardTreeChangePublisher(final DataTree dataTree,
- final YangInstanceIdentifier shardPath,
- final Map<DOMDataTreeIdentifier, ChildShardContext> childShards) {
- this.dataTree = Preconditions.checkNotNull(dataTree);
- this.shardPath = Preconditions.checkNotNull(shardPath);
- this.childShards = Preconditions.checkNotNull(childShards);
+ final YangInstanceIdentifier shardPath, final Map<DOMDataTreeIdentifier, ChildShardContext> childShards) {
+ this.dataTree = requireNonNull(dataTree);
+ this.shardPath = requireNonNull(shardPath);
+ this.childShards = requireNonNull(childShards);
}
@Override
- public <L extends DOMDataTreeChangeListener> AbstractDOMDataTreeChangeListenerRegistration<L> registerTreeChangeListener(final YangInstanceIdentifier path, final L listener) {
+ public <L extends DOMDataTreeChangeListener> AbstractDOMDataTreeChangeListenerRegistration<L>
+ registerTreeChangeListener(final YangInstanceIdentifier path, final L listener) {
takeLock();
try {
return setupListenerContext(path, listener);
}
}
- private <L extends DOMDataTreeChangeListener> AbstractDOMDataTreeChangeListenerRegistration<L> setupListenerContext(final YangInstanceIdentifier listenerPath, final L listener) {
+ 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));
}
- final DOMDataTreeListenerWithSubshards subshardListener = new DOMDataTreeListenerWithSubshards(dataTree, strippedIdentifier, listener);
- final AbstractDOMDataTreeChangeListenerRegistration<L> reg = setupContextWithoutSubshards(strippedIdentifier, subshardListener);
+ final DOMDataTreeListenerWithSubshards subshardListener =
+ new DOMDataTreeListenerWithSubshards(dataTree, strippedIdentifier, listener);
+ final AbstractDOMDataTreeChangeListenerRegistration<L> reg =
+ setupContextWithoutSubshards(strippedIdentifier, subshardListener);
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");
+ // 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 initialDataChangeEvent subshard");
}
}
+
+ initialDataChangeEvent(listenerPath, listener);
+
return reg;
}
- private <L extends DOMDataTreeChangeListener> AbstractDOMDataTreeChangeListenerRegistration<L> setupContextWithoutSubshards(final YangInstanceIdentifier listenerPath, final DOMDataTreeListenerWithSubshards listener) {
+ 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(YangInstanceIdentifier.create(stripShardPath(listenerPath)));
+ final DataTreeCandidate initialCandidate;
+
+ if (preExistingData.isPresent()) {
+ final NormalizedNode<?, ?> data = preExistingData.get();
+ checkState(data instanceof DataContainerNode, "Expected DataContainer node, but was {}", data.getClass());
+ // if we are listening on root of some shard we still get
+ // empty normalized node, root is always present
+ if (((DataContainerNode<?>) data).getValue().isEmpty()) {
+ initialCandidate = DataTreeCandidates.newDataTreeCandidate(listenerPath,
+ DataTreeCandidateNodes.empty(data.getIdentifier()));
+ } else {
+ initialCandidate = DataTreeCandidates.fromNormalizedNode(listenerPath,
+ translateRootShardIdentifierToListenerPath(listenerPath, preExistingData.get()));
+ }
+ } else {
+ initialCandidate = DataTreeCandidates.newDataTreeCandidate(listenerPath,
+ DataTreeCandidateNodes.empty(listenerPath.getLastPathArgument()));
+ }
+
+ listener.onDataTreeChanged(Collections.singleton(initialCandidate));
+ }
+
+ private static NormalizedNode<?, ?> translateRootShardIdentifierToListenerPath(
+ final YangInstanceIdentifier listenerPath, final NormalizedNode<?, ?> node) {
+ if (listenerPath.isEmpty()) {
+ return node;
+ }
+
+ final NormalizedNodeBuilder nodeBuilder;
+ if (node instanceof ContainerNode) {
+ nodeBuilder = ImmutableContainerNodeBuilder.create().withValue(((ContainerNode) node).getValue());
+ } else if (node instanceof MapEntryNode) {
+ nodeBuilder = ImmutableMapEntryNodeBuilder.create().withValue(((MapEntryNode) node).getValue());
+ } else {
+ throw new IllegalArgumentException("Expected ContainerNode or MapEntryNode, but was " + node.getClass());
+ }
+ nodeBuilder.withNodeIdentifier(listenerPath.getLastPathArgument());
+ return nodeBuilder.build();
+ }
+
+ 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());
- final AbstractDOMDataTreeChangeListenerRegistration<L> registration = new AbstractDOMDataTreeChangeListenerRegistration<L>((L) listener) {
+ final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> node =
+ findNodeFor(listenerPath.getPathArguments());
+ @SuppressWarnings("unchecked")
+ final AbstractDOMDataTreeChangeListenerRegistration<L> registration =
+ new AbstractDOMDataTreeChangeListenerRegistration<L>((L) listener) {
@Override
protected void removeRegistration() {
listener.close();
private static final class DOMDataTreeListenerWithSubshards implements DOMDataTreeChangeListener {
- // TODO should we synchronize the access to the dataTree snapshots?
private final DataTree dataTree;
private final YangInstanceIdentifier listenerPath;
private final DOMDataTreeChangeListener delegate;
private final Map<YangInstanceIdentifier, ListenerRegistration<DOMDataTreeChangeListener>> registrations =
new HashMap<>();
- DOMDataTreeListenerWithSubshards(final DataTree dataTree,
- final YangInstanceIdentifier listenerPath,
- final DOMDataTreeChangeListener delegate) {
- this.dataTree = Preconditions.checkNotNull(dataTree);
- this.listenerPath = Preconditions.checkNotNull(listenerPath);
- this.delegate = Preconditions.checkNotNull(delegate);
+ DOMDataTreeListenerWithSubshards(final DataTree dataTree, final YangInstanceIdentifier listenerPath,
+ final DOMDataTreeChangeListener delegate) {
+ this.dataTree = requireNonNull(dataTree);
+ this.listenerPath = requireNonNull(listenerPath);
+ this.delegate = requireNonNull(delegate);
}
@Override
- public void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
- LOG.debug("Received data changed {}", changes.iterator().next());
- final DataTreeCandidate newCandidate = applyChanges(changes);
- delegate.onDataTreeChanged(Collections.singleton(newCandidate));
+ public void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
+ LOG.debug("Received data changed {}", changes);
+ delegate.onDataTreeChanged(changes);
}
void onDataTreeChanged(final YangInstanceIdentifier rootPath, final Collection<DataTreeCandidate> changes) {
- onDataTreeChanged(changes.stream()
+ final List<DataTreeCandidate> newCandidates = changes.stream()
.map(candidate -> DataTreeCandidates.newDataTreeCandidate(rootPath, candidate.getRootNode()))
- .collect(Collectors.toList()));
+ .collect(Collectors.toList());
+ delegate.onDataTreeChanged(Collections.singleton(applyChanges(newCandidates)));
}
void addSubshard(final ChildShardContext context) {
- Preconditions.checkState(context.getShard() instanceof DOMStoreTreeChangePublisher,
- "All subshards that are a part of ListenerContext need to be listenable");
+ checkState(context.getShard() instanceof DOMStoreTreeChangePublisher,
+ "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
registrations.put(context.getPrefix().getRootIdentifier(),
- listenableShard.registerTreeChangeListener(context.getPrefix().getRootIdentifier(),
- changes -> onDataTreeChanged(context.getPrefix().getRootIdentifier(), changes)));
+ listenableShard.registerTreeChangeListener(
+ context.getPrefix().getRootIdentifier(), changes -> onDataTreeChanged(
+ context.getPrefix().getRootIdentifier(), changes)));
}
void close() {
- for (ListenerRegistration<DOMDataTreeChangeListener> registration : registrations.values()) {
+ for (final ListenerRegistration<DOMDataTreeChangeListener> registration : registrations.values()) {
registration.close();
}
registrations.clear();
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);
+ throw new IllegalStateException("Notification validation failed", e);
}
- // strip nodes we dont need since this listener doesn't have to be registered at the root of the DataTree
+ // strip nodes we do not 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 = DataTreeCandidateNodes.empty(listenerPath.getLastPathArgument());
+ }
+
return DataTreeCandidates.newDataTreeCandidate(listenerPath, modifiedChild);
}
}