Bug 2003: CDS serialization improvements
[controller.git] / opendaylight / md-sal / sal-clustering-commons / src / main / java / org / opendaylight / controller / cluster / datastore / node / utils / serialization / NormalizedNodeSerializer.java
1 /*
2  * Copyright (c) 2014 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
9 package org.opendaylight.controller.cluster.datastore.node.utils.serialization;
10
11 import com.google.common.base.Preconditions;
12 import org.opendaylight.controller.cluster.datastore.util.InstanceIdentifierUtils;
13 import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages;
14 import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node.Builder;
15 import org.opendaylight.yangtools.yang.data.api.Node;
16 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
17 import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
18 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
19 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
20 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
21 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
22 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
23 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
24 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
25 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
26 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
27 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
29 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
30 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
31 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
32 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
33 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
34 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
35 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
36 import java.util.EnumMap;
37 import java.util.Map;
38 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.ANY_XML_NODE_TYPE;
39 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.AUGMENTATION_NODE_TYPE;
40 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.CHOICE_NODE_TYPE;
41 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.CONTAINER_NODE_TYPE;
42 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.LEAF_NODE_TYPE;
43 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.LEAF_SET_ENTRY_NODE_TYPE;
44 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.LEAF_SET_NODE_TYPE;
45 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.MAP_ENTRY_NODE_TYPE;
46 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.MAP_NODE_TYPE;
47 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.ORDERED_LEAF_SET_NODE_TYPE;
48 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.ORDERED_MAP_NODE_TYPE;
49 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.UNKEYED_LIST_ENTRY_NODE_TYPE;
50 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.UNKEYED_LIST_NODE_TYPE;
51 import static org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeType.getSerializableNodeType;
52
53 /**
54  * NormalizedNodeSerializer can be used to convert a Normalized node to and and
55  * from a protocol buffer message.
56  *
57  *
58  */
59 public class NormalizedNodeSerializer {
60
61     /**
62      * Serialize a NormalizedNode into a protocol buffer message
63      * <p>
64      * The significant things to be aware of the Serialization process are
65      * <ul>
66      *     <li>Repeated strings like namespaces, revisions and localNames are
67      *     compressed to codes and stored in the top level of the normalized
68      *     node protocol buffer message
69      *     </li>
70      *     <li>All value types are encoded for each leaf value. This is so that
71      *     the deSerialization process does not need to use the schema context to
72      *     figure out how to decode values
73      *     </li>
74      * </ul>
75      *
76      * One question which may arise is why not use something like gzip to
77      * compress the protocol buffer message instead of rolling our own
78      * encoding scheme. This has to be explored further as it is a more
79      * general solution.
80      *
81      * @param node
82      * @return
83      */
84     public static NormalizedNodeMessages.Node serialize(NormalizedNode node){
85         Preconditions.checkNotNull(node, "node should not be null");
86         return new Serializer(node).serialize();
87     }
88
89     public static Serializer newSerializer(NormalizedNode node) {
90         Preconditions.checkNotNull(node, "node should not be null");
91         return new Serializer(node);
92     }
93
94     /**
95      * DeSerialize a protocol buffer message back into a NormalizedNode
96      *
97      * @param node
98      * @return
99      */
100     public static NormalizedNode deSerialize(NormalizedNodeMessages.Node node) {
101         Preconditions.checkNotNull(node, "node should not be null");
102         return new DeSerializer(null, node).deSerialize();
103     }
104
105     public static DeSerializer newDeSerializer(NormalizedNodeMessages.InstanceIdentifier path,
106             NormalizedNodeMessages.Node node) {
107         Preconditions.checkNotNull(node, "node should not be null");
108         return new DeSerializer(path, node);
109     }
110
111     /**
112      * DeSerialize a PathArgument which is in the protocol buffer format into
113      * a yang PathArgument. The protocol buffer path argument is specially
114      * encoded and can only be interpreted in the context of a top level
115      * serialized NormalizedNode protocol buffer message. The reason for this
116      * is that during the NormalizedNode serialization process certain repeated
117      * strings are encoded into a "codes" list and the actual strings are
118      * replaced by an integer which is an index into that list.
119      *
120      * @param node
121      * @param pathArgument
122      * @return
123      */
124     public static YangInstanceIdentifier.PathArgument deSerialize(NormalizedNodeMessages.Node node,
125             NormalizedNodeMessages.PathArgument pathArgument){
126         Preconditions.checkNotNull(node, "node should not be null");
127         Preconditions.checkNotNull(pathArgument, "pathArgument should not be null");
128         return new DeSerializer(null, node).deSerialize(pathArgument);
129     }
130
131     public static class Serializer extends QNameSerializationContextImpl
132                                    implements NormalizedNodeSerializationContext {
133
134         private final NormalizedNode node;
135
136         private NormalizedNodeMessages.InstanceIdentifier serializedPath;
137
138         private Serializer(NormalizedNode node) {
139             this.node = node;
140         }
141
142         public NormalizedNodeMessages.InstanceIdentifier getSerializedPath() {
143             return serializedPath;
144         }
145
146         public NormalizedNodeMessages.Node serialize() {
147             return this.serialize(node).addAllCode(getCodes()).build();
148         }
149
150         public NormalizedNodeMessages.Node serialize(YangInstanceIdentifier path) {
151             Builder builder = serialize(node);
152             serializedPath = InstanceIdentifierUtils.toSerializable(path, this);
153             return builder.addAllCode(getCodes()).build();
154         }
155
156         private NormalizedNodeMessages.Node.Builder serialize(
157             NormalizedNode node) {
158             NormalizedNodeMessages.Node.Builder builder =
159                 NormalizedNodeMessages.Node.newBuilder();
160
161             builder.setPathArgument(PathArgumentSerializer.serialize(this, node.getIdentifier()));
162             Integer nodeType = getSerializableNodeType(node).ordinal();
163             builder.setIntType(nodeType);
164             Object value = node.getValue();
165
166             // We need to do a specific check of the type of the node here
167             // because if we looked at the value type alone we will not be
168             // able to distinguish if the value was supposed to be added
169             // as a child or whether the value should be added as a value of the
170             // node.
171             // One use case where this check is necessary when you have a node
172             // with a bits value. In that case the value of the node is a Set
173             // which is also a Collection. Without the following check being
174             // done first the code would flow into the Collection if condition
175             // and the Set would be added as child nodes
176             if(nodeType == NormalizedNodeType.LEAF_NODE_TYPE.ordinal() ||
177                nodeType == NormalizedNodeType.LEAF_SET_ENTRY_NODE_TYPE.ordinal()){
178
179                 ValueSerializer.serialize(builder, this, value);
180
181             } else if (value instanceof Iterable) {
182                 Iterable iterable = (Iterable) value;
183
184                 for (Object o : iterable) {
185                     if (o instanceof NormalizedNode) {
186                         builder.addChild(serialize((NormalizedNode) o));
187                     }
188                 }
189
190             } else if (value instanceof NormalizedNode) {
191
192                 builder.addChild(serialize((NormalizedNode) value));
193
194             } else {
195
196                 ValueSerializer.serialize(builder, this, value);
197             }
198
199             return builder;
200         }
201     }
202
203     public static class DeSerializer extends QNameDeSerializationContextImpl
204                                      implements NormalizedNodeDeSerializationContext {
205         private static Map<NormalizedNodeType, DeSerializationFunction>
206             deSerializationFunctions = new EnumMap<>(NormalizedNodeType.class);
207
208         static {
209             deSerializationFunctions.put(CONTAINER_NODE_TYPE,
210                 new DeSerializationFunction() {
211                     @Override public NormalizedNode apply(
212                         DeSerializer deSerializer,
213                         NormalizedNodeMessages.Node node) {
214                         DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode>
215                             builder = Builders.containerBuilder();
216
217                         builder
218                             .withNodeIdentifier(deSerializer.toNodeIdentifier(
219                                 node.getPathArgument()));
220
221                         return deSerializer.buildDataContainer(builder, node);
222
223                     }
224
225                 });
226
227             deSerializationFunctions.put(LEAF_NODE_TYPE,
228                 new DeSerializationFunction() {
229                     @Override public NormalizedNode apply(
230                         DeSerializer deSerializer,
231                         NormalizedNodeMessages.Node node) {
232                         NormalizedNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, Object, LeafNode<Object>>
233                             builder = Builders.leafBuilder();
234
235                         builder
236                             .withNodeIdentifier(deSerializer.toNodeIdentifier(
237                                 node.getPathArgument()));
238
239                         return deSerializer.buildNormalizedNode(builder, node);
240
241                     }
242                 });
243
244             deSerializationFunctions.put(MAP_NODE_TYPE,
245                 new DeSerializationFunction() {
246                     @Override public NormalizedNode apply(
247                         DeSerializer deSerializer,
248                         NormalizedNodeMessages.Node node) {
249                         CollectionNodeBuilder<MapEntryNode, MapNode>
250                             builder = Builders.mapBuilder();
251
252                         return deSerializer.buildCollectionNode(builder, node);
253                     }
254                 });
255
256             deSerializationFunctions.put(MAP_ENTRY_NODE_TYPE,
257                 new DeSerializationFunction() {
258                     @Override public NormalizedNode apply(
259                         DeSerializer deSerializer,
260                         NormalizedNodeMessages.Node node) {
261                         DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode>
262                             builder = Builders.mapEntryBuilder();
263
264                         builder.withNodeIdentifier(deSerializer.toNodeIdentifierWithPredicates(
265                             node.getPathArgument()));
266
267                         return deSerializer.buildDataContainer(builder, node);
268                     }
269                 });
270
271             deSerializationFunctions.put(AUGMENTATION_NODE_TYPE,
272                 new DeSerializationFunction() {
273                     @Override public NormalizedNode apply(
274                         DeSerializer deSerializer,
275                         NormalizedNodeMessages.Node node) {
276                         DataContainerNodeBuilder<YangInstanceIdentifier.AugmentationIdentifier, AugmentationNode>
277                             builder = Builders.augmentationBuilder();
278
279                         builder.withNodeIdentifier(
280                             deSerializer.toAugmentationIdentifier(
281                                 node.getPathArgument()));
282
283                         return deSerializer.buildDataContainer(builder, node);
284                     }
285                 });
286
287             deSerializationFunctions.put(LEAF_SET_NODE_TYPE,
288                 new DeSerializationFunction() {
289                     @Override public NormalizedNode apply(
290                         DeSerializer deSerializer,
291                         NormalizedNodeMessages.Node node) {
292                         ListNodeBuilder<Object, LeafSetEntryNode<Object>>
293                             builder = Builders.leafSetBuilder();
294
295                         return deSerializer.buildListNode(builder, node);
296                     }
297                 });
298
299             deSerializationFunctions.put(LEAF_SET_ENTRY_NODE_TYPE,
300                 new DeSerializationFunction() {
301                     @Override public NormalizedNode apply(
302                         DeSerializer deSerializer,
303                         NormalizedNodeMessages.Node node) {
304                         NormalizedNodeAttrBuilder<YangInstanceIdentifier.NodeWithValue, Object, LeafSetEntryNode<Object>>
305                             builder = Builders.leafSetEntryBuilder();
306
307                         builder.withNodeIdentifier(deSerializer.toNodeWithValue(
308                             node.getPathArgument()));
309
310                         return deSerializer.buildNormalizedNode(builder, node);
311                     }
312                 });
313
314             deSerializationFunctions.put(CHOICE_NODE_TYPE,
315                 new DeSerializationFunction() {
316                     @Override public NormalizedNode apply(
317                         DeSerializer deSerializer,
318                         NormalizedNodeMessages.Node node) {
319                         DataContainerNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ChoiceNode>
320                             builder =
321                             Builders.choiceBuilder();
322
323                         builder
324                             .withNodeIdentifier(deSerializer.toNodeIdentifier(
325                                 node.getPathArgument()));
326
327                         return deSerializer.buildDataContainer(builder, node);
328                     }
329                 });
330
331             deSerializationFunctions.put(ORDERED_LEAF_SET_NODE_TYPE,
332                 new DeSerializationFunction() {
333                     @Override public NormalizedNode apply(
334                         DeSerializer deSerializer,
335                         NormalizedNodeMessages.Node node) {
336                         ListNodeBuilder<Object, LeafSetEntryNode<Object>>
337                             builder =
338                             Builders.orderedLeafSetBuilder();
339
340                         return deSerializer.buildListNode(builder, node);
341
342
343                     }
344                 });
345
346             deSerializationFunctions.put(ORDERED_MAP_NODE_TYPE,
347                 new DeSerializationFunction() {
348                     @Override public NormalizedNode apply(
349                         DeSerializer deSerializer,
350                         NormalizedNodeMessages.Node node) {
351                         CollectionNodeBuilder<MapEntryNode, OrderedMapNode>
352                             builder =
353                             Builders.orderedMapBuilder();
354
355                         return deSerializer.buildCollectionNode(builder, node);
356                     }
357                 });
358
359             deSerializationFunctions.put(UNKEYED_LIST_NODE_TYPE,
360                 new DeSerializationFunction() {
361                     @Override public NormalizedNode apply(
362                         DeSerializer deSerializer,
363                         NormalizedNodeMessages.Node node) {
364                         CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode>
365                             builder =
366                             Builders.unkeyedListBuilder();
367
368                         return deSerializer.buildCollectionNode(builder, node);
369                     }
370                 });
371
372             deSerializationFunctions.put(UNKEYED_LIST_ENTRY_NODE_TYPE,
373                 new DeSerializationFunction() {
374                     @Override public NormalizedNode apply(
375                         DeSerializer deSerializer,
376                         NormalizedNodeMessages.Node node) {
377                         DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, UnkeyedListEntryNode>
378                             builder =
379                             Builders.unkeyedListEntryBuilder();
380
381                         builder
382                             .withNodeIdentifier(deSerializer.toNodeIdentifier(
383                                 node.getPathArgument()));
384
385                         return deSerializer.buildDataContainer(builder, node);
386                     }
387                 });
388
389             deSerializationFunctions.put(ANY_XML_NODE_TYPE,
390                 new DeSerializationFunction() {
391
392                     @Override public NormalizedNode apply(
393                         DeSerializer deSerializer,
394                         NormalizedNodeMessages.Node node) {
395                         NormalizedNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, Node<?>, AnyXmlNode>
396                             builder =
397                             Builders.anyXmlBuilder();
398
399                         builder
400                             .withNodeIdentifier(deSerializer.toNodeIdentifier(
401                                 node.getPathArgument()));
402
403                         return deSerializer.buildNormalizedNode(builder, node);
404                     }
405                 });
406
407         }
408
409         private final NormalizedNodeMessages.Node node;
410         private final NormalizedNodeMessages.InstanceIdentifier path;
411         private YangInstanceIdentifier deserializedPath;
412
413         public DeSerializer(NormalizedNodeMessages.InstanceIdentifier path,
414                 NormalizedNodeMessages.Node node) {
415             super(node.getCodeList());
416             this.path = path;
417             this.node = node;
418         }
419
420         public YangInstanceIdentifier getDeserializedPath() {
421             return deserializedPath;
422         }
423
424         public NormalizedNode deSerialize() {
425             NormalizedNode deserializedNode = deSerialize(node);
426             if(path != null) {
427                 deserializedPath = InstanceIdentifierUtils.fromSerializable(path, this);
428             }
429
430             return deserializedNode;
431         }
432
433         private NormalizedNode deSerialize(NormalizedNodeMessages.Node node){
434             Preconditions.checkNotNull(node, "node should not be null");
435
436             DeSerializationFunction deSerializationFunction = deSerializationFunctions.get(
437                     NormalizedNodeType.values()[node.getIntType()]);
438
439             return deSerializationFunction.apply(this, node);
440         }
441
442
443         private NormalizedNode buildCollectionNode(
444             CollectionNodeBuilder builder,
445             NormalizedNodeMessages.Node node) {
446
447             builder.withNodeIdentifier(toNodeIdentifier(node.getPathArgument()));
448
449             for(NormalizedNodeMessages.Node child : node.getChildList()){
450                 builder.withChild(deSerialize(child));
451             }
452
453             return builder.build();
454         }
455
456
457         private NormalizedNode buildListNode(
458             ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder,
459             NormalizedNodeMessages.Node node) {
460             builder.withNodeIdentifier(toNodeIdentifier(node.getPathArgument()));
461
462             for(NormalizedNodeMessages.Node child : node.getChildList()){
463                 builder.withChild((LeafSetEntryNode<Object>) deSerialize(child));
464             }
465
466             return builder.build();
467         }
468
469         private NormalizedNode buildDataContainer(DataContainerNodeBuilder builder, NormalizedNodeMessages.Node node){
470
471             for(NormalizedNodeMessages.Node child : node.getChildList()){
472                 builder.withChild((DataContainerChild<?, ?>) deSerialize(child));
473             }
474
475             //TODO : Also handle attributes
476
477             return builder.build();
478         }
479
480         private NormalizedNode buildNormalizedNode(NormalizedNodeAttrBuilder builder, NormalizedNodeMessages.Node node){
481
482             builder.withValue(ValueSerializer.deSerialize(this, node));
483
484             //TODO : Also handle attributes
485
486             return builder.build();
487
488         }
489
490
491         private YangInstanceIdentifier.NodeIdentifierWithPredicates toNodeIdentifierWithPredicates(
492             NormalizedNodeMessages.PathArgument path) {
493             return (YangInstanceIdentifier.NodeIdentifierWithPredicates) PathArgumentSerializer.deSerialize(this, path);
494         }
495
496         private YangInstanceIdentifier.AugmentationIdentifier toAugmentationIdentifier(
497             NormalizedNodeMessages.PathArgument path) {
498             return (YangInstanceIdentifier.AugmentationIdentifier) PathArgumentSerializer.deSerialize(this, path);
499         }
500
501         private YangInstanceIdentifier.NodeWithValue toNodeWithValue(
502             NormalizedNodeMessages.PathArgument path) {
503             return (YangInstanceIdentifier.NodeWithValue) PathArgumentSerializer.deSerialize(
504                 this, path);
505         }
506
507         private YangInstanceIdentifier.NodeIdentifier toNodeIdentifier(NormalizedNodeMessages.PathArgument path){
508             return (YangInstanceIdentifier.NodeIdentifier) PathArgumentSerializer.deSerialize(
509                 this, path);
510         }
511
512         public YangInstanceIdentifier.PathArgument deSerialize(
513             NormalizedNodeMessages.PathArgument pathArgument) {
514             return PathArgumentSerializer.deSerialize(this, pathArgument);
515         }
516
517         private static interface DeSerializationFunction {
518             NormalizedNode apply(DeSerializer deserializer, NormalizedNodeMessages.Node node);
519         }
520     }
521 }