}
if (!d.isPresent() && iterator.hasNext()) {
- writeTransaction.put(store, currentPath, currentOp.createDefault(currentArg));
+ writeTransaction.merge(store, currentPath, currentOp.createDefault(currentArg));
}
}
//LOG .info("Tx: {} : Putting data {}",getDelegate().getIdentifier(),normalized.getKey());
@Override
public void merge(final LogicalDatastoreType store, final InstanceIdentifier path,
final NormalizedNode<?, ?> data) {
- // TODO Auto-generated method stub
- throw new UnsupportedOperationException("Not implemented yet.");
+ getSubtransaction(store).merge(path,data);
}
@Override
final InstanceIdentifier path) {
return getSubtransaction(store).read(path);
}
-
- @Override
- public void merge(final LogicalDatastoreType store, final InstanceIdentifier path,
- final NormalizedNode<?, ?> data) {
-
- }
}
private final class CommitCoordination implements Callable<RpcResult<TransactionStatus>> {
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
-import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.ListenableFuture;
public abstract class BackwardsCompatibleTransaction<T extends DOMDataReadTransaction> implements
InstanceIdentifier currentPath = new InstanceIdentifier(currentArguments);
boolean isPresent = getDelegate().read(store, currentPath).get().isPresent();
if(isPresent == false && iterator.hasNext()) {
- getDelegate().put(store, currentPath, currentOp.createDefault(currentArg));
+ getDelegate().merge(store, currentPath, currentOp.createDefault(currentArg));
}
}
} catch (InterruptedException | ExecutionException e) {
getDelegate().put(store, normalizedPath, normalizedData);
}
- private boolean isAugmentationChild(final InstanceIdentifier normalizedPath) {
- List<PathArgument> parentArgs = parentPath(normalizedPath).getPath();
- if(parentArgs.isEmpty()) {
- return false;
- }
- return Iterables.getLast(parentArgs) instanceof AugmentationIdentifier;
- }
-
- private void ensureParentNode(final LogicalDatastoreType store, final InstanceIdentifier normalizedPath,
- final NormalizedNode<?, ?> normalizedData) {
- InstanceIdentifier parentPath = parentPath(normalizedPath);
- PathArgument parentType = Iterables.getLast(parentPath.getPath());
- if(parentType instanceof AugmentationIdentifier) {
- AugmentationNode node = Builders.augmentationBuilder()
- .withNodeIdentifier((AugmentationIdentifier) parentType)
- .build();
- getDelegate().put(store, parentPath, node);
- }
- if(normalizedData instanceof MapEntryNode) {
- MapNode mapNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(normalizedData.getNodeType())).build();
- getDelegate().put(store, parentPath, mapNode);
- } else if (normalizedData instanceof LeafSetNode<?>){
- LeafSetNode<Object> leafNode = Builders.leafSetBuilder().withNodeIdentifier(new NodeIdentifier(normalizedData.getNodeType())).build();
- getDelegate().put(store, parentPath, leafNode);
- }
-
-
- }
-
- private InstanceIdentifier parentPath(final InstanceIdentifier normalizedPath) {
- List<PathArgument> childArgs = normalizedPath.getPath();
- return new InstanceIdentifier(childArgs.subList(0, childArgs.size() -1));
- }
-
- private boolean parentNodeDoesNotExists(final LogicalDatastoreType store, final InstanceIdentifier normalizedPath) {
- try {
- return !getDelegate().read(store, parentPath(normalizedPath)).get().isPresent();
- } catch (InterruptedException | ExecutionException e) {
- throw new IllegalStateException(e);
- }
- }
-
@Override
public void removeConfigurationData(final InstanceIdentifier legacyPath) {
checkNotNull(legacyPath, "Path MUST NOT be null.");
}
}
+ @Override
+ public void merge(final InstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ checkNotReady();
+ try {
+ LOG.trace("Tx: {} Merge: {}:{}",getIdentifier(),path,data);
+ mutableTree.merge(path, data);
+ // FIXME: Add checked exception
+ } catch (Exception e) {
+ LOG.error("Tx: {}, failed to write {}:{} in {}",getIdentifier(),path,data,mutableTree,e);
+ }
+ }
+
@Override
public void delete(final InstanceIdentifier path) {
checkNotReady();
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
resolveModificationFor(path).write(value);
}
+ public void merge(final InstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ checkSealed();
+ mergeImpl(resolveModificationFor(path),data);
+ }
+
+ private void mergeImpl(final OperationWithModification op,final NormalizedNode<?,?> data) {
+
+ if(data instanceof NormalizedNodeContainer<?,?,?>) {
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ NormalizedNodeContainer<?,?,NormalizedNode<PathArgument, ?>> dataContainer = (NormalizedNodeContainer) data;
+ for(NormalizedNode<PathArgument, ?> child : dataContainer.getValue()) {
+ PathArgument childId = child.getIdentifier();
+ mergeImpl(op.forChild(childId), child);
+ }
+ }
+ op.merge(data);
+ }
+
public void delete(final InstanceIdentifier path) {
checkSealed();
resolveModificationFor(path).delete();
import org.opendaylight.controller.md.sal.dom.store.impl.tree.NodeModification;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import com.google.common.base.Optional;
public class OperationWithModification {
private final NodeModification modification;
+
private final ModificationApplyOperation applyOperation;
private OperationWithModification(final ModificationApplyOperation op, final NodeModification mod) {
return this;
}
+ public NodeModification getModification() {
+ return modification;
+ }
+
+ public ModificationApplyOperation getApplyOperation() {
+ return applyOperation;
+ }
+
public boolean isApplicable(final Optional<StoreMetadataNode> data) {
return applyOperation.isApplicable(modification, data);
}
return new OperationWithModification(operation, modification);
}
+
+ public void merge(final NormalizedNode<?, ?> data) {
+ modification.merge(data);
+ applyOperation.verifyStructure(modification);
+
+ }
+
+ public OperationWithModification forChild(final PathArgument childId) {
+ NodeModification childMod = modification.modifyChild(childId);
+ Optional<ModificationApplyOperation> childOp = applyOperation.getChild(childId);
+ return from(childOp.get(),childMod);
+ }
}
\ No newline at end of file
switch (modification.getModificationType()) {
case SUBTREE_MODIFIED:
return resolveSubtreeChangeEvent(path, listeners, modification, before.get(), after.get());
+ case MERGE:
case WRITE:
if (before.isPresent()) {
return resolveReplacedEvent(path, listeners, before.get().getData(), after.get().getData());
switch (childMod.getModificationType()) {
case WRITE:
+ case MERGE:
case DELETE:
one.merge(resolveAnyChangeEvent(childPath, childListeners, childMod, childBefore, childAfter));
break;
return isSubtreeModificationApplicable(modification, current);
case WRITE:
return isWriteApplicable(modification, current);
+ case MERGE:
+ return isMergeApplicable(modification,current);
case UNMODIFIED:
return true;
default:
}
}
+ private boolean isMergeApplicable(final NodeModification modification, final Optional<StoreMetadataNode> current) {
+ Optional<StoreMetadataNode> original = modification.getOriginal();
+ if (original.isPresent() && current.isPresent()) {
+ return isNotConflicting(original.get(), current.get());
+ } else if (current.isPresent()) {
+ return true;
+ }
+ return true;
+ }
+
protected boolean isWriteApplicable(final NodeModification modification, final Optional<StoreMetadataNode> current) {
Optional<StoreMetadataNode> original = modification.getOriginal();
if (original.isPresent() && current.isPresent()) {
modification);
return modification.storeSnapshot(Optional.of(applySubtreeChange(modification, currentMeta.get(),
subtreeVersion)));
+ case MERGE:
+ if(currentMeta.isPresent()) {
+ return modification.storeSnapshot(Optional.of(applyMerge(modification,currentMeta.get(),subtreeVersion)));
+ } // Fallback to write is intentional - if node is not preexisting merge is same as write
case WRITE:
return modification.storeSnapshot(Optional.of(applyWrite(modification, currentMeta, subtreeVersion)));
case UNMODIFIED:
}
}
+ protected abstract StoreMetadataNode applyMerge(NodeModification modification,
+ StoreMetadataNode currentMeta, UnsignedLong subtreeVersion);
+
protected abstract StoreMetadataNode applyWrite(NodeModification modification,
Optional<StoreMetadataNode> currentMeta, UnsignedLong subtreeVersion);
+ "is leaf type node. Subtree change is not allowed.");
}
+ @Override
+ protected StoreMetadataNode applyMerge(final NodeModification modification, final StoreMetadataNode currentMeta,
+ final UnsignedLong subtreeVersion) {
+ return applyWrite(modification, Optional.of(currentMeta), subtreeVersion);
+ }
+
@Override
protected StoreMetadataNode applyWrite(final NodeModification modification,
final Optional<StoreMetadataNode> currentMeta, final UnsignedLong subtreeVersion) {
UnsignedLong nodeVersion = subtreeVersion;
- if (currentMeta.isPresent()) {
- nodeVersion = StoreUtils.increase(currentMeta.get().getNodeVersion());
- }
-
return StoreMetadataNode.builder().setNodeVersion(nodeVersion).setSubtreeVersion(subtreeVersion)
.setData(modification.getWritenValue()).build();
}
}
+ @Override
+ protected StoreMetadataNode applyMerge(final NodeModification modification, final StoreMetadataNode currentMeta,
+ final UnsignedLong subtreeVersion) {
+ // For Node Containers - merge is same as subtree change - we only replace children.
+ return applySubtreeChange(modification, currentMeta, subtreeVersion);
+ }
+
@Override
public StoreMetadataNode applySubtreeChange(final NodeModification modification,
final StoreMetadataNode currentMeta, final UnsignedLong subtreeVersion) {
entryStrategy = Optional.<ModificationApplyOperation> of(new UnkeyedListItemModificationStrategy(schema));
}
-
+ @Override
+ protected StoreMetadataNode applyMerge(final NodeModification modification, final StoreMetadataNode currentMeta,
+ final UnsignedLong subtreeVersion) {
+ return applyWrite(modification, Optional.of(currentMeta), subtreeVersion);
+ }
@Override
protected StoreMetadataNode applySubtreeChange(final NodeModification modification,
* Tree node is to be deleted.
*
*/
- DELETE
+ DELETE,
+
+ /**
+ *
+ * Tree node is to be merged with existing one.
+ *
+ */
+ MERGE
}
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
-import com.google.common.primitives.UnsignedLong;
/**
* Node Modification Node and Tree
public static final Predicate<NodeModification> IS_TERMINAL_PREDICATE = new Predicate<NodeModification>() {
@Override
public boolean apply(final NodeModification input) {
- return input.getModificationType() == ModificationType.WRITE || input.getModificationType() == ModificationType.DELETE;
+ return input.getModificationType() == ModificationType.WRITE //
+ || input.getModificationType() == ModificationType.DELETE //
+ || input.getModificationType() == ModificationType.MERGE;
}
};
private final PathArgument identifier;
private NormalizedNode<?, ?> value;
- private UnsignedLong subtreeVersion;
private Optional<StoreMetadataNode> snapshotCache;
private final Map<PathArgument, NodeModification> childModification;
this.value = value;
}
+ public synchronized void merge(final NormalizedNode<?, ?> data) {
+ checkSealed();
+ clearSnapshot();
+ updateModificationType(ModificationType.MERGE);
+ // FIXME: Probably merge with previous value.
+ this.value = data;
+ }
+
@GuardedBy("this")
private void checkSealed() {
checkState(!sealed, "Node Modification is sealed. No further changes allowed.");
*/
void write(InstanceIdentifier path, NormalizedNode<?, ?> data);
+ /**
+ * Store a provided data at specified path. This acts as a add / replace
+ * operation, which is to say that whole subtree will be replaced by
+ * specified path.
+ *
+ * If you need add or merge of current object with specified use
+ * {@link #merge(LogicalDatastoreType, Path, Object)}
+ *
+ *
+ * @param path
+ * @param data
+ * Data object to be written
+ *
+ * @throws IllegalStateException
+ * if the client code already sealed transaction and invoked
+ * {@link #ready()}
+ */
+ void merge(InstanceIdentifier path, NormalizedNode<?, ?> data);
+
/**
*
* Deletes data and whole subtree located at provided path.