Merge "Add missing copyright text"
[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.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 final class DataTreeCandidatePayload extends Payload implements Externalizable {
39     private static final Logger LOG = LoggerFactory.getLogger(DataTreeCandidatePayload.class);
40     private static final long serialVersionUID = 1L;
41     private static final byte DELETE = 0;
42     private static final byte SUBTREE_MODIFIED = 1;
43     private static final byte UNMODIFIED = 2;
44     private static final byte WRITE = 3;
45
46     private transient byte[] serialized;
47
48     public DataTreeCandidatePayload() {
49         // Required by Externalizable
50     }
51
52     private DataTreeCandidatePayload(final byte[] serialized) {
53         this.serialized = Preconditions.checkNotNull(serialized);
54     }
55
56     private static void writeChildren(final NormalizedNodeOutputStreamWriter writer, final DataOutput out,
57             final Collection<DataTreeCandidateNode> children) throws IOException {
58         out.writeInt(children.size());
59         for (DataTreeCandidateNode child : children) {
60             writeNode(writer, out, child);
61         }
62     }
63
64     private static void writeNode(final NormalizedNodeOutputStreamWriter writer, final DataOutput out,
65             final DataTreeCandidateNode node) throws IOException {
66         switch (node.getModificationType()) {
67         case DELETE:
68             out.writeByte(DELETE);
69             writer.writePathArgument(node.getIdentifier());
70             break;
71         case SUBTREE_MODIFIED:
72             out.writeByte(SUBTREE_MODIFIED);
73             writer.writePathArgument(node.getIdentifier());
74             writeChildren(writer, out, node.getChildNodes());
75             break;
76         case WRITE:
77             out.writeByte(WRITE);
78             writer.writeNormalizedNode(node.getDataAfter().get());
79             break;
80         case UNMODIFIED:
81             throw new IllegalArgumentException("Unmodified candidate should never be in the payload");
82         default:
83             throw new IllegalArgumentException("Unhandled node type " + node.getModificationType());
84         }
85     }
86
87     static DataTreeCandidatePayload create(DataTreeCandidate candidate) {
88         final ByteArrayDataOutput out = ByteStreams.newDataOutput();
89         try (final NormalizedNodeOutputStreamWriter writer = new NormalizedNodeOutputStreamWriter(out)) {
90             writer.writeYangInstanceIdentifier(candidate.getRootPath());
91
92             final DataTreeCandidateNode node = candidate.getRootNode();
93             switch (node.getModificationType()) {
94             case DELETE:
95                 out.writeByte(DELETE);
96                 break;
97             case SUBTREE_MODIFIED:
98                 out.writeByte(SUBTREE_MODIFIED);
99                 writeChildren(writer, out, node.getChildNodes());
100                 break;
101             case UNMODIFIED:
102                 out.writeByte(UNMODIFIED);
103                 break;
104             case WRITE:
105                 out.writeByte(WRITE);
106                 writer.writeNormalizedNode(node.getDataAfter().get());
107                 break;
108             default:
109                 throw new IllegalArgumentException("Unhandled node type " + node.getModificationType());
110             }
111
112             writer.close();
113         } catch (IOException e) {
114             throw new IllegalArgumentException(String.format("Failed to serialize candidate %s", candidate), e);
115         }
116
117         return new DataTreeCandidatePayload(out.toByteArray());
118     }
119
120     private static Collection<DataTreeCandidateNode> readChildren(final NormalizedNodeInputStreamReader reader,
121         final DataInput in) throws IOException {
122         final int size = in.readInt();
123         if (size != 0) {
124             final Collection<DataTreeCandidateNode> ret = new ArrayList<>(size);
125             for (int i = 0; i < size; ++i) {
126                 final DataTreeCandidateNode child = readNode(reader, in);
127                 if (child != null) {
128                     ret.add(child);
129                 }
130             }
131             return ret;
132         } else {
133             return Collections.emptyList();
134         }
135     }
136
137     private static DataTreeCandidateNode readNode(final NormalizedNodeInputStreamReader reader,
138             final DataInput in) throws IOException {
139         final byte type = in.readByte();
140         switch (type) {
141         case DELETE:
142             return DeletedDataTreeCandidateNode.create(reader.readPathArgument());
143         case SUBTREE_MODIFIED:
144             final PathArgument identifier = reader.readPathArgument();
145             final Collection<DataTreeCandidateNode> children = readChildren(reader, in);
146             if (children.isEmpty()) {
147                 LOG.debug("Modified node {} does not have any children, not instantiating it", identifier);
148                 return null;
149             } else {
150                 return ModifiedDataTreeCandidateNode.create(identifier, children);
151             }
152         case UNMODIFIED:
153             return null;
154         case WRITE:
155             return DataTreeCandidateNodes.fromNormalizedNode(reader.readNormalizedNode());
156         default:
157             throw new IllegalArgumentException("Unhandled node type " + type);
158         }
159     }
160
161     private static DataTreeCandidate parseCandidate(final ByteArrayDataInput in) throws IOException {
162         final NormalizedNodeInputStreamReader reader = new NormalizedNodeInputStreamReader(in);
163         final YangInstanceIdentifier rootPath = reader.readYangInstanceIdentifier();
164         final byte type = in.readByte();
165
166         final DataTreeCandidateNode rootNode;
167         switch (type) {
168         case DELETE:
169             rootNode = DeletedDataTreeCandidateNode.create();
170             break;
171         case SUBTREE_MODIFIED:
172             rootNode = ModifiedDataTreeCandidateNode.create(readChildren(reader, in));
173             break;
174         case WRITE:
175             rootNode = DataTreeCandidateNodes.fromNormalizedNode(reader.readNormalizedNode());
176             break;
177         default:
178             throw new IllegalArgumentException("Unhandled node type " + type);
179         }
180
181         return DataTreeCandidates.newDataTreeCandidate(rootPath, rootNode);
182     }
183
184     DataTreeCandidate getCandidate() throws IOException {
185         return parseCandidate(ByteStreams.newDataInput(serialized));
186     }
187
188     @Override
189     @Deprecated
190     @SuppressWarnings("rawtypes")
191     public <T> Map<GeneratedExtension, T> encode() {
192         return null;
193     }
194
195     @Override
196     @Deprecated
197     public Payload decode(final AppendEntries.ReplicatedLogEntry.Payload payload) {
198         return null;
199     }
200
201     @Override
202     public int size() {
203         return serialized.length;
204     }
205
206     @Override
207     public void writeExternal(ObjectOutput out) throws IOException {
208         out.writeByte((byte)serialVersionUID);
209         out.writeInt(serialized.length);
210         out.write(serialized);
211     }
212
213     @Override
214     public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
215         final long version = in.readByte();
216         Preconditions.checkArgument(version == serialVersionUID, "Unsupported serialization version %s", version);
217
218         final int length = in.readInt();
219         serialized = new byte[length];
220         in.readFully(serialized);
221     }
222 }