1 基本步骤
// 创建实例
DataTree dataTree = InMemoryDataTreeFactory.getInstance().create(TreeType.OPERATIONAL);
dataTree.setSchemaContext(schemaContext);
// 获取一个snapshot
DataTreeSnapshot snapshot = dataTree.takeSnapshot();
// 修改snapshot
DataTreeModification modification = snapshot.newModification();
YangInstanceIdentifier yiid = YangInstanceIdentifier.of(TEST1_OUTER_CONTAINER1_QNAME);
ContainerNode outerContainer1 = buildOuterContainer1();
modification.write(yiid, outerContainer1);
modification.ready();
// 验证
dataTree.validate(modification);
// 生成一个新的DataTree
DataTreeCandidate candidate = dataTree.prepare(modification);
// 写入DataTree
dataTree.commit(candidate);
2 创建DataTree实例
InMemoryDataTree包含的字段:
InMemoryDataTree {
private final DataTreeConfiguration treeConfig;
private final boolean maskMandatory;
private volatile DataTreeState state;
}
final class DataTreeState {
private final LatestOperationHolder holder;
private final SchemaContext schemaContext;
private final TreeNode root;
}
TreeNode的几个实现类:
MaterializedContainerNode
ValueNode
LazyContainerNode
SimpleContainerNode
TreeNode包含关键信息:
NormalizedNode<?, ?> data;
Version version;
创建InMemoryDataTree时,如果没有指定YangInstanceIdentifier,则treeconfig中的YangInstanceIdentifier值为YangInstanceIdentifier.EMPTY;TreeNode的NormalizedNode的PathArgument的QName值为
QName NAME = QName.create(URI.create("urn:ietf:params:xml:ns:netconf:base:1.0"), null, "data")
data
值为null,TreeNode是SimpleContainerNode
如果指定YangInstanceIdentifier,treeconfig中的YangInstanceIdentifier值为YangInstanceIdentifier,NormalizedNode值为:
final PathArgument arg = path.getLastPathArgument();
if (arg instanceof NodeIdentifier) {
return ImmutableContainerNodeBuilder.create().withNodeIdentifier((NodeIdentifier) arg).build();
}
if (arg instanceof NodeIdentifierWithPredicates) {
return ImmutableNodes.mapEntryBuilder().withNodeIdentifier((NodeIdentifierWithPredicates) arg).build();
}
TreeNode是SimpleContainerNode
DataSchemaContextNode
包含了schema node的PathArgument
和DataSchemaNode
,DataSchemaContextNode
的实例:
ContainerContextNode
UnkeyedListMixinContextNode
OrderedMapMixinContextNode
UnorderedMapMixinContextNode
LeafContextNode
OrderedLeafListMixinContextNode
UnorderedLeafListMixinContextNode
3 snapshot
InMemoryDataTreeSnapshot {
// 值为 UpgradableModificationApplyOperation
private final RootModificationApplyOperation applyOper;
private final SchemaContext schemaContext;
private final TreeNode rootNode;
}
3 修改datatree
基本思路为:
使用InMemoryDataTreeSnapshot
产生一个InMemoryDataTreeModification
对象,同时产生一个新的Version,代表这个修改。
从根节点开始到修改的节点,每个节点创建一个ModifiedNode,它保存了节点的PathArgument、原始的TreeNode,修改的孩子等。write的节点标记的LogicOperation.WRITE,所有的父节点标记为LogicOperation.TOUCH
class InMemoryDataTreeModification {
// UpgradableModificationApplyOperation, 不同的节点有不通的数据修改方式,strategyTree可以遍历到语法树的所有节点的修改方法
// 根节点对应的 Strategy,为 ContainerModificationStrategy
private final RootModificationApplyOperation strategyTree;
private final InMemoryDataTreeSnapshot snapshot;
private final ModifiedNode rootNode;
// 本次修改对应的新版本号
private final Version version;
public void write(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
// 当前modification还没有关闭
checkSealed();
// data 所表示节点的identifirer 与 path的identifier需要一致
checkIdentifierReferencesData(path, data);
// 创建modifiedNode
resolveModificationFor(path).write(data);
}
/*
* 从根节点开始,直到被修改节点,创建modified node
*/
private OperationWithModification resolveModificationFor(final YangInstanceIdentifier path) {
upgradeIfPossible();
/*
* Walk the strategy and modification trees in-sync, creating modification nodes as needed.
*
* If the user has provided wrong input, we may end up with a bunch of TOUCH nodes present
* ending with an empty one, as we will throw the exception below. This fact could end up
* being a problem, as we'd have bunch of phantom operations.
*
* That is fine, as we will prune any empty TOUCH nodes in the last phase of the ready
* process.
*/
ModificationApplyOperation operation = strategyTree;
ModifiedNode modification = rootNode;
int i = 1;
for (final PathArgument pathArg : path.getPathArguments()) {
// 孩子 schema node对应的strategy
final Optional<ModificationApplyOperation> potential = operation.getChild(pathArg);
if (!potential.isPresent()) {
throw new SchemaValidationFailedException(String.format("Child %s is not present in schema tree.",
path.getAncestor(i)));
}
operation = potential.get();
++i;
// 孩子节点对应的modified node
modification = modification.modifyChild(pathArg, operation, version);
}
return OperationWithModification.from(operation, modification);
}
}
class ModifiedNode {
// 根据 节点的 child policy确定实例是hashmap,还是linked hashmap
private final Map<PathArgument, ModifiedNode> children;
// 修改前的TreeNode
private final Optional<TreeNode> original;
// 节点的identifier
private final PathArgument identifier;
private LogicalOperation operation = LogicalOperation.NONE;
// 修改后的 tree node
private Optional<TreeNode> snapshotCache;
// 修改后的值
private NormalizedNode<?, ?> value;
// 修改后标记的修改类型
private ModificationType modType;
}
ModifiedNode modifyChild(@Nonnull final PathArgument child, @Nonnull final ModificationApplyOperation childOper,
@Nonnull final Version modVersion) {
clearSnapshot();
if (operation == LogicalOperation.NONE) {
updateOperationType(LogicalOperation.TOUCH);
}
// 这个节点是否已经创建了 ModifiedNode
final ModifiedNode potential = children.get(child);
if (potential != null) {
return potential;
}
// 为孩子节点创建treenode
final Optional<TreeNode> currentMetadata = findOriginalMetadata(child, modVersion);
final ModifiedNode newlyCreated = new ModifiedNode(child, currentMetadata, childOper.getChildPolicy());
if (operation == LogicalOperation.MERGE && value != null) {
/*
* We are attempting to modify a previously-unmodified part of a MERGE node. If the
* value contains this component, we need to materialize it as a MERGE modification.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
final Optional<NormalizedNode<?, ?>> childData = ((NormalizedNodeContainer)value).getChild(child);
if (childData.isPresent()) {
childOper.mergeIntoModifiedNode(newlyCreated, childData.get(), modVersion);
}
}
children.put(child, newlyCreated);
return newlyCreated;
}
ModificationApplyOperation
代表了修改datatree节点的修改策略,常用节点的策略有:
UpgradableModificationApplyOperation extends RootModificationApplyOperation
ContainerModificationStrategy
ListEntryModificationStrategy
OrderedMapModificationStrategy
UnkeyedListItemModificationStrategy
UnkeyedListModificationStrategy
UnorderedMapModificationStrategy
LeafModificationStrategy
LeafSetEntryModificationStrategy
OrderedLeafSetModificationStrategy
UnorderedLeafSetModificationStrategy
/*
* 根据孩子节点的schemanode类型,确定相应的modification strategy
*/
public static ModificationApplyOperation from(final DataSchemaNode schemaNode, final DataTreeConfiguration treeConfig) {
if (treeConfig.getTreeType() == TreeType.CONFIGURATION) {
Preconditions.checkArgument(schemaNode.isConfiguration(), "Supplied %s does not belongs to configuration tree.", schemaNode.getPath());
}
if (schemaNode instanceof ContainerSchemaNode) {
final ContainerSchemaNode containerSchema = (ContainerSchemaNode) schemaNode;
if (containerSchema.isPresenceContainer()) {
return new PresenceContainerModificationStrategy(containerSchema, treeConfig);
} else {
return new StructuralContainerModificationStrategy(containerSchema, treeConfig);
}
} else if (schemaNode instanceof ListSchemaNode) {
return fromListSchemaNode((ListSchemaNode) schemaNode, treeConfig);
} else if (schemaNode instanceof ChoiceSchemaNode) {
return new ChoiceModificationStrategy((ChoiceSchemaNode) schemaNode, treeConfig);
} else if (schemaNode instanceof LeafListSchemaNode) {
return fromLeafListSchemaNode((LeafListSchemaNode) schemaNode, treeConfig);
} else if (schemaNode instanceof LeafSchemaNode) {
return new LeafModificationStrategy((LeafSchemaNode) schemaNode);
}
throw new IllegalArgumentException("Not supported schema node type for " + schemaNode.getClass());
}
4 validate
逐级检查节点,snapshot中的节点与当前datatree中的节点需要一致,没有改变:
1)节点要不都存在,要不都不存在
对于写入的节点还要检查:
1)如果存在,version一致
2)如果存在,subversion一致
入口方法为AbstractDataTreeTip.validate()
, 这个方法会从根节点开始逐级调用节点对应的ModificationApplyOperation
的checkApplicable()
方法
5 prepare
以当前Datatree内容为基础,根据data tree snapshot修改后的内容,生成替换当前Datatree的candidate Datatree。不用snapshot修改后的dataree替换当前datatree的值,是因为这样可以支持不同子树的同时修改。candidate Datatree包含了当前datatree,修改后的待替换的datatree信息。
TreeNode的实现类
LazyContainerNode
MaterializedContainerNode
SimpleContainerNode
上述TreeNode实现类相应的mutable类:
LazyMutableContainerNode
MaterializedMutableContainerNode
入口方法为:AbstractDataTreeTip.prepare()
,这个方法会从根节点开始深度优先遍历,调用相关节点的apply()
,applyTouch()
,applyWrite()
等方法。
/*
* 创建 candidate datatree
*/
@Override
final Optional<TreeNode> apply(final ModifiedNode modification, final Optional<TreeNode> currentMeta, final Version version) {
switch (modification.getOperation()) {
case DELETE:
LOG.info("++++++++++++apply DELETE: {}, class name: {}", modification.getIdentifier(), getClass().getSimpleName());
// Deletion of a non-existing node is a no-op, report it as such
modification.resolveModificationType(currentMeta.isPresent() ? ModificationType.DELETE : ModificationType.UNMODIFIED);
return modification.setSnapshot(Optional.absent());
case TOUCH:
LOG.info("++++++++++++apply TOUCH: {}, class name: {}", modification.getIdentifier(), getClass().getSimpleName());
Preconditions.checkArgument(currentMeta.isPresent(), "Metadata not available for modification %s",
modification);
return modification.setSnapshot(Optional.of(applyTouch(modification, currentMeta.get(), version)));
case MERGE:
LOG.info("++++++++++++apply MERGE: {}, class name: {}", modification.getIdentifier(), getClass().getSimpleName());
final TreeNode result;
if (!currentMeta.isPresent()) {
// This is a slight optimization: a merge on a non-existing node equals to a write. Written data
// structure is usually verified when the transaction is sealed. To preserve correctness, we have
// to run that validation here.
modification.resolveModificationType(ModificationType.WRITE);
result = applyWrite(modification, currentMeta, version);
verifyStructure(result.getData(), true);
} else {
result = applyMerge(modification, currentMeta.get(), version);
}
return modification.setSnapshot(Optional.of(result));
case WRITE:
LOG.info("++++++++++++apply WRITE: {}, class name: {}", modification.getIdentifier(), getClass().getSimpleName());
modification.resolveModificationType(ModificationType.WRITE);
return modification.setSnapshot(Optional.of(applyWrite(modification, currentMeta, version)));
case NONE:
LOG.info("++++++++++++apply NONE: {}, class name: {}", modification.getIdentifier());
modification.resolveModificationType(ModificationType.UNMODIFIED);
return currentMeta;
default:
throw new IllegalArgumentException("Provided modification type is not supported.");
}
}
protected TreeNode applyTouch(final ModifiedNode modification, final TreeNode currentMeta, final Version version) {
/*
* The user may have issued an empty merge operation. In this case we do not perform
* a data tree mutation, do not pass GO, and do not collect useless garbage. It
* also means the ModificationType is UNMODIFIED.
*/
final Collection<ModifiedNode> children = modification.getChildren();
if (!children.isEmpty()) {
// builder 初始化为 当前data tree的 normalized node值
@SuppressWarnings("rawtypes")
final NormalizedNodeContainerBuilder dataBuilder = createBuilder(currentMeta.getData());
// 当前 data tree变为 可变
final MutableTreeNode newMeta = currentMeta.mutable();
// 设置节点的subversion为修改后的version
newMeta.setSubtreeVersion(version);
final TreeNode ret = mutateChildren(newMeta, dataBuilder, version, children);
/*
* It is possible that the only modifications under this node were empty merges,
* which were turned into UNMODIFIED. If that is the case, we can turn this operation
* into UNMODIFIED, too, potentially cascading it up to root. This has the benefit
* of speeding up any users, who can skip processing child nodes.
*
* In order to do that, though, we have to check all child operations are UNMODIFIED.
* Let's do precisely that, stopping as soon we find a different result.
*/
for (final ModifiedNode child : children) {
if (child.getModificationType() != ModificationType.UNMODIFIED) {
// 设置修改类型
modification.resolveModificationType(ModificationType.SUBTREE_MODIFIED);
return ret;
}
}
}
// The merge operation did not have any children, or all of them turned out to be UNMODIFIED, hence do not
// replace the metadata node.
modification.resolveModificationType(ModificationType.UNMODIFIED);
return currentMeta;
}
protected TreeNode applyWrite(final ModifiedNode modification,
final Optional<TreeNode> currentMeta, final Version version) {
final NormalizedNode<?, ?> newValue = modification.getWrittenValue();
final TreeNode newValueMeta = TreeNodeFactory.createTreeNode(newValue, version);
// 整个节点都是修改后的节点
if (modification.getChildren().isEmpty()) {
return newValueMeta;
}
// 节点修改后,又修改了它的子节点
/*
* This is where things get interesting. The user has performed a write and
* then she applied some more modifications to it. So we need to make sense
* of that an apply the operations on top of the written value. We could have
* done it during the write, but this operation is potentially expensive, so
* we have left it out of the fast path.
*
* As it turns out, once we materialize the written data, we can share the
* code path with the subtree change. So let's create an unsealed TreeNode
* and run the common parts on it -- which end with the node being sealed.
*
* FIXME: this code needs to be moved out from the prepare() path and into
* the read() and seal() paths. Merging of writes needs to be charged
* to the code which originated this, not to the code which is
* attempting to make it visible.
*/
final MutableTreeNode mutable = newValueMeta.mutable();
mutable.setSubtreeVersion(version);
@SuppressWarnings("rawtypes")
final NormalizedNodeContainerBuilder dataBuilder = createBuilder(newValue);
final TreeNode result = mutateChildren(mutable, dataBuilder, version, modification.getChildren());
// We are good to go except one detail: this is a single logical write, but
// we have a result TreeNode which has been forced to materialized, e.g. it
// is larger than it needs to be. Create a new TreeNode to host the data.
return TreeNodeFactory.createTreeNode(result.getData(), version);
}
6 commit
用candidate data tree替换当前的data tree,会进一步通过比较地址的方式校验当前datatree是不是candidate中的old data tree,也就是要求在生成candidate data tree后,当前data tree不能发生了变化。
InMemoryDataTree.commit()
方法实现了具体的实现逻辑:
public void commit(final DataTreeCandidate candidate) {
if (candidate instanceof NoopDataTreeCandidate) {
return;
}
Preconditions.checkArgument(candidate instanceof InMemoryDataTreeCandidate, "Invalid candidate class %s", candidate.getClass());
final InMemoryDataTreeCandidate c = (InMemoryDataTreeCandidate)candidate;
if (LOG.isTraceEnabled()) {
LOG.trace("Data Tree is {}", NormalizedNodes.toStringTree(c.getTipRoot().getData()));
}
final TreeNode newRoot = c.getTipRoot();
DataTreeState currentState, newState;
do {
currentState = state;
// 执行 commit()函数时 datatree 的 tree node
final TreeNode currentRoot = currentState.getRoot();
LOG.debug("Updating datastore from {} to {}", currentRoot, newRoot);
// 执行 prepare时 datatree 的tree node
final TreeNode oldRoot = c.getBeforeRoot();
LOG.info("======currentRoot: {}, oldRoot: {}, newRoot: {}",
System.identityHashCode(currentRoot),
System.identityHashCode(oldRoot),
System.identityHashCode(newRoot));
if (oldRoot != currentRoot) {
final String oldStr = simpleToString(oldRoot);
final String currentStr = simpleToString(currentRoot);
throw new IllegalStateException("Store tree " + currentStr + " and candidate base " + oldStr + " differ.");
}
newState = currentState.withRoot(newRoot);
LOG.trace("Updated state from {} to {}", currentState, newState);
} while (!STATE_UPDATER.compareAndSet(this, currentState, newState));
}
网友评论