Bug 3195: Cleanup on error paths and error handling
[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             out.writeByte(UNMODIFIED);
82             break;
83         default:
84             throw new IllegalArgumentException("Unhandled node type " + node.getModificationType());
85         }
86     }
87
88     static DataTreeCandidatePayload create(DataTreeCandidate candidate) {
89         final ByteArrayDataOutput out = ByteStreams.newDataOutput();
90         try (final NormalizedNodeOutputStreamWriter writer = new NormalizedNodeOutputStreamWriter(out)) {
91             writer.writeYangInstanceIdentifier(candidate.getRootPath());
92
93             final DataTreeCandidateNode node = candidate.getRootNode();
94             switch (node.getModificationType()) {
95             case DELETE:
96                 out.writeByte(DELETE);
97                 break;
98             case SUBTREE_MODIFIED:
99                 out.writeByte(SUBTREE_MODIFIED);
100                 writeChildren(writer, out, node.getChildNodes());
101                 break;
102             case UNMODIFIED:
103                 out.writeByte(UNMODIFIED);
104                 break;
105             case WRITE:
106                 out.writeByte(WRITE);
107                 writer.writeNormalizedNode(node.getDataAfter().get());
108                 break;
109             default:
110                 throw new IllegalArgumentException("Unhandled node type " + node.getModificationType());
111             }
112
113             writer.close();
114         } catch (IOException e) {
115             throw new IllegalArgumentException(String.format("Failed to serialize candidate %s", candidate), e);
116         }
117
118         return new DataTreeCandidatePayload(out.toByteArray());
119     }
120
121     private static Collection<DataTreeCandidateNode> readChildren(final NormalizedNodeInputStreamReader reader,
122         final DataInput in) throws IOException {
123         final int size = in.readInt();
124         if (size != 0) {
125             final Collection<DataTreeCandidateNode> ret = new ArrayList<>(size);
126             for (int i = 0; i < size; ++i) {
127                 final DataTreeCandidateNode child = readNode(reader, in);
128                 if (child != null) {
129                     ret.add(child);
130                 }
131             }
132             return ret;
133         } else {
134             return Collections.emptyList();
135         }
136     }
137
138     private static DataTreeCandidateNode readNode(final NormalizedNodeInputStreamReader reader,
139             final DataInput in) throws IOException {
140         final byte type = in.readByte();
141         switch (type) {
142         case DELETE:
143             return DeletedDataTreeCandidateNode.create(reader.readPathArgument());
144         case SUBTREE_MODIFIED:
145             final PathArgument identifier = reader.readPathArgument();
146             final Collection<DataTreeCandidateNode> children = readChildren(reader, in);
147             if (children.isEmpty()) {
148                 LOG.debug("Modified node {} does not have any children, not instantiating it", identifier);
149                 return null;
150             } else {
151                 return ModifiedDataTreeCandidateNode.create(identifier, children);
152             }
153         case UNMODIFIED:
154             return null;
155         case WRITE:
156             return DataTreeCandidateNodes.fromNormalizedNode(reader.readNormalizedNode());
157         default:
158             throw new IllegalArgumentException("Unhandled node type " + type);
159         }
160     }
161
162     private static DataTreeCandidate parseCandidate(final ByteArrayDataInput in) throws IOException {
163         final NormalizedNodeInputStreamReader reader = new NormalizedNodeInputStreamReader(in);
164         final YangInstanceIdentifier rootPath = reader.readYangInstanceIdentifier();
165         final byte type = in.readByte();
166
167         final DataTreeCandidateNode rootNode;
168         switch (type) {
169         case DELETE:
170             rootNode = DeletedDataTreeCandidateNode.create();
171             break;
172         case SUBTREE_MODIFIED:
173             rootNode = ModifiedDataTreeCandidateNode.create(readChildren(reader, in));
174             break;
175         case WRITE:
176             rootNode = DataTreeCandidateNodes.fromNormalizedNode(reader.readNormalizedNode());
177             break;
178         default:
179             throw new IllegalArgumentException("Unhandled node type " + type);
180         }
181
182         return DataTreeCandidates.newDataTreeCandidate(rootPath, rootNode);
183     }
184
185     DataTreeCandidate getCandidate() throws IOException {
186         return parseCandidate(ByteStreams.newDataInput(serialized));
187     }
188
189     @Override
190     @Deprecated
191     @SuppressWarnings("rawtypes")
192     public <T> Map<GeneratedExtension, T> encode() {
193         return null;
194     }
195
196     @Override
197     @Deprecated
198     public Payload decode(final AppendEntries.ReplicatedLogEntry.Payload payload) {
199         return null;
200     }
201
202     @Override
203     public int size() {
204         return serialized.length;
205     }
206
207     @Override
208     public void writeExternal(ObjectOutput out) throws IOException {
209         out.writeByte((byte)serialVersionUID);
210         out.writeInt(serialized.length);
211         out.write(serialized);
212     }
213
214     @Override
215     public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
216         final long version = in.readByte();
217         Preconditions.checkArgument(version == serialVersionUID, "Unsupported serialization version %s", version);
218
219         final int length = in.readInt();
220         serialized = new byte[length];
221         in.readFully(serialized);
222     }
223 }