BUG-2399: take into account new ModificationTypes
[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.DataInput;
16 import java.io.DataOutput;
17 import java.io.Externalizable;
18 import java.io.IOException;
19 import java.io.ObjectInput;
20 import java.io.ObjectOutput;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.Map;
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 NormalizedNodeOutputStreamWriter writer, final DataOutput out,
60             final Collection<DataTreeCandidateNode> children) throws IOException {
61         out.writeInt(children.size());
62         for (DataTreeCandidateNode child : children) {
63             writeNode(writer, out, child);
64         }
65     }
66
67     private static void writeNode(final NormalizedNodeOutputStreamWriter writer, final DataOutput out,
68             final DataTreeCandidateNode node) throws IOException {
69         switch (node.getModificationType()) {
70         case APPEARED:
71             out.writeByte(APPEARED);
72             writer.writePathArgument(node.getIdentifier());
73             writeChildren(writer, out, node.getChildNodes());
74             break;
75         case DELETE:
76             out.writeByte(DELETE);
77             writer.writePathArgument(node.getIdentifier());
78             break;
79         case DISAPPEARED:
80             out.writeByte(DISAPPEARED);
81             writer.writePathArgument(node.getIdentifier());
82             writeChildren(writer, out, node.getChildNodes());
83             break;
84         case SUBTREE_MODIFIED:
85             out.writeByte(SUBTREE_MODIFIED);
86             writer.writePathArgument(node.getIdentifier());
87             writeChildren(writer, out, node.getChildNodes());
88             break;
89         case WRITE:
90             out.writeByte(WRITE);
91             writer.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(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                 out.writeByte(APPEARED);
110                 writeChildren(writer, out, node.getChildNodes());
111                 break;
112             case DELETE:
113                 out.writeByte(DELETE);
114                 break;
115             case DISAPPEARED:
116                 out.writeByte(DISAPPEARED);
117                 writeChildren(writer, out, node.getChildNodes());
118                 break;
119             case SUBTREE_MODIFIED:
120                 out.writeByte(SUBTREE_MODIFIED);
121                 writeChildren(writer, out, node.getChildNodes());
122                 break;
123             case UNMODIFIED:
124                 out.writeByte(UNMODIFIED);
125                 break;
126             case WRITE:
127                 out.writeByte(WRITE);
128                 writer.writeNormalizedNode(node.getDataAfter().get());
129                 break;
130             default:
131                 throw new IllegalArgumentException("Unhandled node type " + node.getModificationType());
132             }
133
134             writer.close();
135         } catch (IOException e) {
136             throw new IllegalArgumentException(String.format("Failed to serialize candidate %s", candidate), e);
137         }
138
139         return new DataTreeCandidatePayload(out.toByteArray());
140     }
141
142     private static Collection<DataTreeCandidateNode> readChildren(final NormalizedNodeInputStreamReader reader,
143         final DataInput in) throws IOException {
144         final int size = in.readInt();
145         if (size != 0) {
146             final Collection<DataTreeCandidateNode> ret = new ArrayList<>(size);
147             for (int i = 0; i < size; ++i) {
148                 final DataTreeCandidateNode child = readNode(reader, in);
149                 if (child != null) {
150                     ret.add(child);
151                 }
152             }
153             return ret;
154         } else {
155             return Collections.emptyList();
156         }
157     }
158
159     private static DataTreeCandidateNode readModifiedNode(final ModificationType type,
160             final NormalizedNodeInputStreamReader reader, final DataInput in) throws IOException {
161
162         final PathArgument identifier = reader.readPathArgument();
163         final Collection<DataTreeCandidateNode> children = readChildren(reader, in);
164         if (children.isEmpty()) {
165             LOG.debug("Modified node {} does not have any children, not instantiating it", identifier);
166             return null;
167         } else {
168             return ModifiedDataTreeCandidateNode.create(identifier, type, children);
169         }
170     }
171
172     private static DataTreeCandidateNode readNode(final NormalizedNodeInputStreamReader reader,
173             final DataInput in) throws IOException {
174         final byte type = in.readByte();
175         switch (type) {
176         case APPEARED:
177             return readModifiedNode(ModificationType.APPEARED, reader, in);
178         case DELETE:
179             return DeletedDataTreeCandidateNode.create(reader.readPathArgument());
180         case DISAPPEARED:
181             return readModifiedNode(ModificationType.DISAPPEARED, reader, in);
182         case SUBTREE_MODIFIED:
183             return readModifiedNode(ModificationType.SUBTREE_MODIFIED, reader, in);
184         case UNMODIFIED:
185             return null;
186         case WRITE:
187             return DataTreeCandidateNodes.fromNormalizedNode(reader.readNormalizedNode());
188         default:
189             throw new IllegalArgumentException("Unhandled node type " + type);
190         }
191     }
192
193     private static DataTreeCandidate parseCandidate(final ByteArrayDataInput in) throws IOException {
194         final NormalizedNodeInputStreamReader reader = new NormalizedNodeInputStreamReader(in);
195         final YangInstanceIdentifier rootPath = reader.readYangInstanceIdentifier();
196         final byte type = in.readByte();
197
198         final DataTreeCandidateNode rootNode;
199         switch (type) {
200         case DELETE:
201             rootNode = DeletedDataTreeCandidateNode.create();
202             break;
203         case SUBTREE_MODIFIED:
204             rootNode = ModifiedDataTreeCandidateNode.create(readChildren(reader, in));
205             break;
206         case WRITE:
207             rootNode = DataTreeCandidateNodes.fromNormalizedNode(reader.readNormalizedNode());
208             break;
209         default:
210             throw new IllegalArgumentException("Unhandled node type " + type);
211         }
212
213         return DataTreeCandidates.newDataTreeCandidate(rootPath, rootNode);
214     }
215
216     DataTreeCandidate getCandidate() throws IOException {
217         return parseCandidate(ByteStreams.newDataInput(serialized));
218     }
219
220     @Override
221     @Deprecated
222     @SuppressWarnings("rawtypes")
223     public <T> Map<GeneratedExtension, T> encode() {
224         return null;
225     }
226
227     @Override
228     @Deprecated
229     public Payload decode(final AppendEntries.ReplicatedLogEntry.Payload payload) {
230         return null;
231     }
232
233     @Override
234     public int size() {
235         return serialized.length;
236     }
237
238     @Override
239     public void writeExternal(ObjectOutput out) throws IOException {
240         out.writeByte((byte)serialVersionUID);
241         out.writeInt(serialized.length);
242         out.write(serialized);
243     }
244
245     @Override
246     public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
247         final long version = in.readByte();
248         Preconditions.checkArgument(version == serialVersionUID, "Unsupported serialization version %s", version);
249
250         final int length = in.readInt();
251         serialized = new byte[length];
252         in.readFully(serialized);
253     }
254 }