BUG-4626: Introduce NormalizedNodeData{Input,Output}
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / main / java / org / opendaylight / controller / cluster / datastore / DataTreeCandidatePayload.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.cluster.datastore;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.io.ByteArrayDataInput;
12 import com.google.common.io.ByteArrayDataOutput;
13 import com.google.common.io.ByteStreams;
14 import com.google.protobuf.GeneratedMessage.GeneratedExtension;
15 import java.io.Externalizable;
16 import java.io.IOException;
17 import java.io.ObjectInput;
18 import java.io.ObjectOutput;
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.Map;
23 import org.opendaylight.controller.cluster.datastore.node.utils.stream.NormalizedNodeDataInput;
24 import org.opendaylight.controller.cluster.datastore.node.utils.stream.NormalizedNodeDataOutput;
25 import org.opendaylight.controller.cluster.datastore.node.utils.stream.NormalizedNodeInputStreamReader;
26 import org.opendaylight.controller.cluster.datastore.node.utils.stream.NormalizedNodeOutputStreamWriter;
27 import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload;
28 import org.opendaylight.controller.protobuff.messages.cluster.raft.AppendEntriesMessages.AppendEntries;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
31 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
32 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNodes;
34 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidates;
35 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 final class DataTreeCandidatePayload extends Payload implements Externalizable {
40     private static final Logger LOG = LoggerFactory.getLogger(DataTreeCandidatePayload.class);
41     private static final long serialVersionUID = 1L;
42     private static final byte DELETE = 0;
43     private static final byte SUBTREE_MODIFIED = 1;
44     private static final byte UNMODIFIED = 2;
45     private static final byte WRITE = 3;
46     private static final byte APPEARED = 4;
47     private static final byte DISAPPEARED = 5;
48
49     private transient byte[] serialized;
50
51     public DataTreeCandidatePayload() {
52         // Required by Externalizable
53     }
54
55     private DataTreeCandidatePayload(final byte[] serialized) {
56         this.serialized = Preconditions.checkNotNull(serialized);
57     }
58
59     private static void writeChildren(final NormalizedNodeDataOutput out,
60             final Collection<DataTreeCandidateNode> children) throws IOException {
61         out.writeInt(children.size());
62         for (DataTreeCandidateNode child : children) {
63             writeNode(out, child);
64         }
65     }
66
67     private static void writeNode(final NormalizedNodeDataOutput out, final DataTreeCandidateNode node)
68             throws IOException {
69         switch (node.getModificationType()) {
70         case APPEARED:
71             out.writeByte(APPEARED);
72             out.writePathArgument(node.getIdentifier());
73             writeChildren(out, node.getChildNodes());
74             break;
75         case DELETE:
76             out.writeByte(DELETE);
77             out.writePathArgument(node.getIdentifier());
78             break;
79         case DISAPPEARED:
80             out.writeByte(DISAPPEARED);
81             out.writePathArgument(node.getIdentifier());
82             writeChildren(out, node.getChildNodes());
83             break;
84         case SUBTREE_MODIFIED:
85             out.writeByte(SUBTREE_MODIFIED);
86             out.writePathArgument(node.getIdentifier());
87             writeChildren(out, node.getChildNodes());
88             break;
89         case WRITE:
90             out.writeByte(WRITE);
91             out.writeNormalizedNode(node.getDataAfter().get());
92             break;
93         case UNMODIFIED:
94             out.writeByte(UNMODIFIED);
95             break;
96         default:
97             throw new IllegalArgumentException("Unhandled node type " + node.getModificationType());
98         }
99     }
100
101     static DataTreeCandidatePayload create(final DataTreeCandidate candidate) {
102         final ByteArrayDataOutput out = ByteStreams.newDataOutput();
103         try (final NormalizedNodeOutputStreamWriter writer = new NormalizedNodeOutputStreamWriter(out)) {
104             writer.writeYangInstanceIdentifier(candidate.getRootPath());
105
106             final DataTreeCandidateNode node = candidate.getRootNode();
107             switch (node.getModificationType()) {
108             case APPEARED:
109                 writer.writeByte(APPEARED);
110                 writeChildren(writer, node.getChildNodes());
111                 break;
112             case DELETE:
113                 writer.writeByte(DELETE);
114                 break;
115             case DISAPPEARED:
116                 writer.writeByte(DISAPPEARED);
117                 writeChildren(writer, node.getChildNodes());
118                 break;
119             case SUBTREE_MODIFIED:
120                 writer.writeByte(SUBTREE_MODIFIED);
121                 writeChildren(writer, node.getChildNodes());
122                 break;
123             case UNMODIFIED:
124                 writer.writeByte(UNMODIFIED);
125                 break;
126             case WRITE:
127                 writer.writeByte(WRITE);
128                 writer.writeNormalizedNode(node.getDataAfter().get());
129                 break;
130             default:
131                 throw new IllegalArgumentException("Unhandled node type " + node.getModificationType());
132             }
133
134         } catch (IOException e) {
135             throw new IllegalArgumentException(String.format("Failed to serialize candidate %s", candidate), e);
136         }
137
138         return new DataTreeCandidatePayload(out.toByteArray());
139     }
140
141     private static Collection<DataTreeCandidateNode> readChildren(final NormalizedNodeDataInput in) throws IOException {
142         final int size = in.readInt();
143         if (size != 0) {
144             final Collection<DataTreeCandidateNode> ret = new ArrayList<>(size);
145             for (int i = 0; i < size; ++i) {
146                 final DataTreeCandidateNode child = readNode(in);
147                 if (child != null) {
148                     ret.add(child);
149                 }
150             }
151             return ret;
152         } else {
153             return Collections.emptyList();
154         }
155     }
156
157     private static DataTreeCandidateNode readModifiedNode(final ModificationType type,
158             final NormalizedNodeDataInput in) throws IOException {
159
160         final PathArgument identifier = in.readPathArgument();
161         final Collection<DataTreeCandidateNode> children = readChildren(in);
162         if (children.isEmpty()) {
163             LOG.debug("Modified node {} does not have any children, not instantiating it", identifier);
164             return null;
165         } else {
166             return ModifiedDataTreeCandidateNode.create(identifier, type, children);
167         }
168     }
169
170     private static DataTreeCandidateNode readNode(final NormalizedNodeDataInput in) throws IOException {
171         final byte type = in.readByte();
172         switch (type) {
173         case APPEARED:
174             return readModifiedNode(ModificationType.APPEARED, in);
175         case DELETE:
176             return DeletedDataTreeCandidateNode.create(in.readPathArgument());
177         case DISAPPEARED:
178             return readModifiedNode(ModificationType.DISAPPEARED, in);
179         case SUBTREE_MODIFIED:
180             return readModifiedNode(ModificationType.SUBTREE_MODIFIED, in);
181         case UNMODIFIED:
182             return null;
183         case WRITE:
184             return DataTreeCandidateNodes.fromNormalizedNode(in.readNormalizedNode());
185         default:
186             throw new IllegalArgumentException("Unhandled node type " + type);
187         }
188     }
189
190     private static DataTreeCandidate parseCandidate(final ByteArrayDataInput in) throws IOException {
191         final NormalizedNodeDataInput reader = new NormalizedNodeInputStreamReader(in);
192         final YangInstanceIdentifier rootPath = reader.readYangInstanceIdentifier();
193         final byte type = reader.readByte();
194
195         final DataTreeCandidateNode rootNode;
196         switch (type) {
197         case DELETE:
198             rootNode = DeletedDataTreeCandidateNode.create();
199             break;
200         case SUBTREE_MODIFIED:
201             rootNode = ModifiedDataTreeCandidateNode.create(readChildren(reader));
202             break;
203         case WRITE:
204             rootNode = DataTreeCandidateNodes.fromNormalizedNode(reader.readNormalizedNode());
205             break;
206         default:
207             throw new IllegalArgumentException("Unhandled node type " + type);
208         }
209
210         return DataTreeCandidates.newDataTreeCandidate(rootPath, rootNode);
211     }
212
213     DataTreeCandidate getCandidate() throws IOException {
214         return parseCandidate(ByteStreams.newDataInput(serialized));
215     }
216
217     @Override
218     @Deprecated
219     @SuppressWarnings("rawtypes")
220     public <T> Map<GeneratedExtension, T> encode() {
221         return null;
222     }
223
224     @Override
225     @Deprecated
226     public Payload decode(final AppendEntries.ReplicatedLogEntry.Payload payload) {
227         return null;
228     }
229
230     @Override
231     public int size() {
232         return serialized.length;
233     }
234
235     @Override
236     public void writeExternal(ObjectOutput out) throws IOException {
237         out.writeByte((byte)serialVersionUID);
238         out.writeInt(serialized.length);
239         out.write(serialized);
240     }
241
242     @Override
243     public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
244         final long version = in.readByte();
245         Preconditions.checkArgument(version == serialVersionUID, "Unsupported serialization version %s", version);
246
247         final int length = in.readInt();
248         serialized = new byte[length];
249         in.readFully(serialized);
250     }
251 }