2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.controller.cluster.datastore.persisted;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.annotations.Beta;
13 import com.google.common.annotations.VisibleForTesting;
14 import java.io.DataInput;
15 import java.io.DataOutput;
16 import java.io.IOException;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.List;
20 import org.eclipse.jdt.annotation.NonNull;
21 import org.opendaylight.yangtools.concepts.Immutable;
22 import org.opendaylight.yangtools.yang.data.api.schema.stream.ReusableStreamReceiver;
23 import org.opendaylight.yangtools.yang.data.codec.binfmt.NormalizedNodeDataInput;
24 import org.opendaylight.yangtools.yang.data.codec.binfmt.NormalizedNodeDataOutput;
25 import org.opendaylight.yangtools.yang.data.codec.binfmt.NormalizedNodeStreamVersion;
26 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidate;
27 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateNode;
28 import org.opendaylight.yangtools.yang.data.tree.api.ModificationType;
29 import org.opendaylight.yangtools.yang.data.tree.spi.DataTreeCandidateNodes;
30 import org.opendaylight.yangtools.yang.data.tree.spi.DataTreeCandidates;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
35 * Utility serialization/deserialization for {@link DataTreeCandidate}. Note that this utility does not maintain
36 * before-image information across serialization.
39 public final class DataTreeCandidateInputOutput {
40 public record DataTreeCandidateWithVersion(
41 @NonNull DataTreeCandidate candidate,
42 @NonNull NormalizedNodeStreamVersion version) implements Immutable {
43 public DataTreeCandidateWithVersion {
44 requireNonNull(candidate);
45 requireNonNull(version);
49 private static final Logger LOG = LoggerFactory.getLogger(DataTreeCandidateInputOutput.class);
50 private static final byte DELETE = 0;
51 private static final byte SUBTREE_MODIFIED = 1;
52 private static final byte UNMODIFIED = 2;
53 private static final byte WRITE = 3;
54 private static final byte APPEARED = 4;
55 private static final byte DISAPPEARED = 5;
57 private DataTreeCandidateInputOutput() {
58 throw new UnsupportedOperationException();
61 private static DataTreeCandidateNode readModifiedNode(final ModificationType type, final NormalizedNodeDataInput in,
62 final ReusableStreamReceiver receiver) throws IOException {
63 final var pathArg = in.readPathArgument();
64 final var children = readChildren(in, receiver);
65 if (children.isEmpty()) {
66 LOG.debug("Modified node {} does not have any children, not instantiating it", pathArg);
70 return ModifiedDataTreeCandidateNode.create(pathArg, type, children);
73 private static List<DataTreeCandidateNode> readChildren(final NormalizedNodeDataInput in,
74 final ReusableStreamReceiver receiver) throws IOException {
75 final int size = in.readInt();
80 final var ret = new ArrayList<DataTreeCandidateNode>(size);
81 for (int i = 0; i < size; ++i) {
82 final var child = readNode(in, receiver);
90 private static DataTreeCandidateNode readNode(final NormalizedNodeDataInput in,
91 final ReusableStreamReceiver receiver) throws IOException {
92 final byte type = in.readByte();
93 return switch (type) {
94 case APPEARED -> readModifiedNode(ModificationType.APPEARED, in, receiver);
95 case DELETE -> DeletedDataTreeCandidateNode.create(in.readPathArgument());
96 case DISAPPEARED -> readModifiedNode(ModificationType.DISAPPEARED, in, receiver);
97 case SUBTREE_MODIFIED -> readModifiedNode(ModificationType.SUBTREE_MODIFIED, in, receiver);
98 case UNMODIFIED -> null;
99 case WRITE -> DataTreeCandidateNodes.written(in.readNormalizedNode(receiver));
100 default -> throw new IllegalArgumentException("Unhandled node type " + type);
104 public static DataTreeCandidateWithVersion readDataTreeCandidate(final DataInput in,
105 final ReusableStreamReceiver receiver) throws IOException {
106 final var reader = NormalizedNodeDataInput.newDataInput(in);
107 final var rootPath = reader.readYangInstanceIdentifier();
108 final byte type = reader.readByte();
110 final DataTreeCandidateNode rootNode = switch (type) {
111 case APPEARED -> ModifiedDataTreeCandidateNode.create(ModificationType.APPEARED,
112 readChildren(reader, receiver));
113 case DELETE -> DeletedDataTreeCandidateNode.create();
114 case DISAPPEARED -> ModifiedDataTreeCandidateNode.create(ModificationType.DISAPPEARED,
115 readChildren(reader, receiver));
116 case SUBTREE_MODIFIED -> ModifiedDataTreeCandidateNode.create(ModificationType.SUBTREE_MODIFIED,
117 readChildren(reader, receiver));
118 case WRITE -> DataTreeCandidateNodes.written(reader.readNormalizedNode(receiver));
119 case UNMODIFIED -> AbstractDataTreeCandidateNode.createUnmodified();
120 default -> throw new IllegalArgumentException("Unhandled node type " + type);
122 return new DataTreeCandidateWithVersion(DataTreeCandidates.newDataTreeCandidate(rootPath, rootNode),
123 reader.getVersion());
126 private static void writeChildren(final NormalizedNodeDataOutput out,
127 final Collection<DataTreeCandidateNode> children) throws IOException {
128 out.writeInt(children.size());
129 for (var child : children) {
130 writeNode(out, child);
134 private static void writeNode(final NormalizedNodeDataOutput out, final DataTreeCandidateNode node)
136 switch (node.modificationType()) {
138 out.writeByte(APPEARED);
139 out.writePathArgument(node.name());
140 writeChildren(out, node.childNodes());
143 out.writeByte(DELETE);
144 out.writePathArgument(node.name());
146 case DISAPPEARED -> {
147 out.writeByte(DISAPPEARED);
148 out.writePathArgument(node.name());
149 writeChildren(out, node.childNodes());
151 case SUBTREE_MODIFIED -> {
152 out.writeByte(SUBTREE_MODIFIED);
153 out.writePathArgument(node.name());
154 writeChildren(out, node.childNodes());
157 out.writeByte(WRITE);
158 out.writeNormalizedNode(node.getDataAfter());
160 case UNMODIFIED -> out.writeByte(UNMODIFIED);
161 default -> throwUnhandledNodeType(node);
166 public static void writeDataTreeCandidate(final DataOutput out, final PayloadVersion version,
167 final DataTreeCandidate candidate) throws IOException {
168 try (var writer = version.getStreamVersion().newDataOutput(out)) {
169 writer.writeYangInstanceIdentifier(candidate.getRootPath());
171 final var node = candidate.getRootNode();
172 switch (node.modificationType()) {
174 writer.writeByte(APPEARED);
175 writeChildren(writer, node.childNodes());
177 case DELETE -> writer.writeByte(DELETE);
178 case DISAPPEARED -> {
179 writer.writeByte(DISAPPEARED);
180 writeChildren(writer, node.childNodes());
182 case SUBTREE_MODIFIED -> {
183 writer.writeByte(SUBTREE_MODIFIED);
184 writeChildren(writer, node.childNodes());
186 case UNMODIFIED -> writer.writeByte(UNMODIFIED);
188 writer.writeByte(WRITE);
189 writer.writeNormalizedNode(node.getDataAfter());
191 default -> throwUnhandledNodeType(node);
196 public static void writeDataTreeCandidate(final DataOutput out, final DataTreeCandidate candidate)
198 writeDataTreeCandidate(out, PayloadVersion.current(), candidate);
201 private static void throwUnhandledNodeType(final DataTreeCandidateNode node) {
202 throw new IllegalArgumentException("Unhandled node type " + node.modificationType());