HONEYCOMB-34: Configurable serializer dependency
[honeycomb.git] / v3po / data-impl / src / main / java / io / fd / honeycomb / v3po / data / impl / OperationalDataTree.java
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package io.fd.honeycomb.v3po.data.impl;
18
19 import static com.google.common.base.Preconditions.checkNotNull;
20 import static com.google.common.collect.Iterables.getOnlyElement;
21
22 import com.google.common.base.Function;
23 import com.google.common.base.Optional;
24 import com.google.common.base.Preconditions;
25 import com.google.common.collect.Collections2;
26 import com.google.common.collect.Multimap;
27 import com.google.common.util.concurrent.CheckedFuture;
28 import com.google.common.util.concurrent.Futures;
29 import io.fd.honeycomb.v3po.data.ReadableDataTree;
30 import io.fd.honeycomb.v3po.translate.Context;
31 import io.fd.honeycomb.v3po.translate.read.ReadContext;
32 import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
33 import io.fd.honeycomb.v3po.translate.read.ReaderRegistry;
34 import java.util.Collection;
35 import java.util.Map;
36 import javax.annotation.Nonnull;
37 import javax.annotation.Nullable;
38 import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
39 import org.opendaylight.yangtools.yang.binding.DataObject;
40 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
41 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
42 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
43 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
44 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
45 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
46 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
47 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
48 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
49 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
50 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
51 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
52 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
53 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
54 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58 /**
59  * ReadableDataTree implementation for operational data.
60  */
61 public final class OperationalDataTree implements ReadableDataTree {
62     private static final Logger LOG = LoggerFactory.getLogger(OperationalDataTree.class);
63
64     private final BindingNormalizedNodeSerializer serializer;
65     private final ReaderRegistry readerRegistry;
66     private final SchemaContext globalContext;
67
68     /**
69      * Creates operational data tree instance.
70      *
71      * @param serializer     service for serialization between Java Binding Data representation and NormalizedNode
72      *                       representation.
73      * @param globalContext  service for obtaining top level context data from all yang modules.
74      * @param readerRegistry service responsible for translation between DataObjects and data provider.
75      */
76     public OperationalDataTree(@Nonnull BindingNormalizedNodeSerializer serializer,
77                                @Nonnull final SchemaContext globalContext, @Nonnull ReaderRegistry readerRegistry) {
78         this.globalContext = checkNotNull(globalContext, "globalContext should not be null");
79         this.serializer = checkNotNull(serializer, "serializer should not be null");
80         this.readerRegistry = checkNotNull(readerRegistry, "reader should not be null");
81     }
82
83     @Override
84     public CheckedFuture<Optional<NormalizedNode<?, ?>>,
85             org.opendaylight.controller.md.sal.common.api.data.ReadFailedException> read(
86             @Nonnull final YangInstanceIdentifier yangInstanceIdentifier) {
87
88         try(ReadContext ctx = new ReadContextImpl()) {
89             if (checkNotNull(yangInstanceIdentifier).equals(YangInstanceIdentifier.EMPTY)) {
90                 return Futures.immediateCheckedFuture(readRoot(ctx));
91             } else {
92                 return Futures.immediateCheckedFuture(readNode(yangInstanceIdentifier, ctx));
93             }
94         } catch (ReadFailedException e) {
95             return Futures.immediateFailedCheckedFuture(
96                     new org.opendaylight.controller.md.sal.common.api.data.ReadFailedException(
97                             "Failed to read VPP data", e));
98         }
99     }
100
101     private Optional<NormalizedNode<?, ?>> readNode(final YangInstanceIdentifier yangInstanceIdentifier,
102                                                     final ReadContext ctx)
103             throws ReadFailedException {
104         LOG.debug("OperationalDataTree.readNode(), yangInstanceIdentifier={}", yangInstanceIdentifier);
105         final InstanceIdentifier<?> path = serializer.fromYangInstanceIdentifier(yangInstanceIdentifier);
106         checkNotNull(path, "Invalid instance identifier %s. Cannot create BA equivalent.", yangInstanceIdentifier);
107         LOG.debug("OperationalDataTree.readNode(), path={}", path);
108
109         final Optional<? extends DataObject> dataObject;
110
111         dataObject = readerRegistry.read(path, ctx);
112         if (dataObject.isPresent()) {
113             final NormalizedNode<?, ?> value = toNormalizedNodeFunction(path).apply(dataObject.get());
114             return Optional.<NormalizedNode<?, ?>>fromNullable(value);
115         } else {
116             return Optional.absent();
117         }
118     }
119
120     private Optional<NormalizedNode<?, ?>> readRoot(final ReadContext ctx) throws ReadFailedException {
121         LOG.debug("OperationalDataTree.readRoot()");
122
123         final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> dataNodeBuilder =
124                 Builders.containerBuilder()
125                         .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(SchemaContext.NAME));
126
127         final Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> dataObjects =
128                 readerRegistry.readAll(ctx);
129
130         for (final InstanceIdentifier<? extends DataObject> instanceIdentifier : dataObjects.keySet()) {
131             final YangInstanceIdentifier rootElementId = serializer.toYangInstanceIdentifier(instanceIdentifier);
132             final NormalizedNode<?, ?> node =
133                     wrapDataObjects(rootElementId, instanceIdentifier, dataObjects.get(instanceIdentifier));
134             dataNodeBuilder.withChild((DataContainerChild<?, ?>) node);
135         }
136
137         return Optional.<NormalizedNode<?, ?>>of(dataNodeBuilder.build());
138     }
139
140     private NormalizedNode<?, ?> wrapDataObjects(final YangInstanceIdentifier yangInstanceIdentifier,
141                                                  final InstanceIdentifier<? extends DataObject> instanceIdentifier,
142                                                  final Collection<? extends DataObject> dataObjects) {
143         final Collection<NormalizedNode<?, ?>> normalizedRootElements = Collections2
144                 .transform(dataObjects, toNormalizedNodeFunction(instanceIdentifier));
145
146         final DataSchemaNode schemaNode =
147                 globalContext.getDataChildByName(yangInstanceIdentifier.getLastPathArgument().getNodeType());
148         if (schemaNode instanceof ListSchemaNode) {
149             // In case of a list, wrap all the values in a Mixin parent node
150             final ListSchemaNode listSchema = (ListSchemaNode) schemaNode;
151             return wrapListIntoMixinNode(normalizedRootElements, listSchema);
152         } else {
153             Preconditions.checkState(dataObjects.size() == 1, "Singleton list was expected");
154             return getOnlyElement(normalizedRootElements);
155         }
156     }
157
158     private static DataContainerChild<?, ?> wrapListIntoMixinNode(
159             final Collection<NormalizedNode<?, ?>> normalizedRootElements, final ListSchemaNode listSchema) {
160         if (listSchema.getKeyDefinition().isEmpty()) {
161             final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> listBuilder =
162                     Builders.unkeyedListBuilder();
163             for (NormalizedNode<?, ?> normalizedRootElement : normalizedRootElements) {
164                 listBuilder.withChild((UnkeyedListEntryNode) normalizedRootElement);
165             }
166             return listBuilder.build();
167         } else {
168             final CollectionNodeBuilder<MapEntryNode, ? extends MapNode> listBuilder =
169                     listSchema.isUserOrdered()
170                             ? Builders.orderedMapBuilder()
171                             : Builders.mapBuilder();
172
173             for (NormalizedNode<?, ?> normalizedRootElement : normalizedRootElements) {
174                 listBuilder.withChild((MapEntryNode) normalizedRootElement);
175             }
176             return listBuilder.build();
177         }
178     }
179
180     @SuppressWarnings("unchecked")
181     private Function<DataObject, NormalizedNode<?, ?>> toNormalizedNodeFunction(final InstanceIdentifier path) {
182         return new Function<DataObject, NormalizedNode<?, ?>>() {
183             @Override
184             public NormalizedNode<?, ?> apply(@Nullable final DataObject dataObject) {
185                 LOG.trace("OperationalDataTree.toNormalizedNode(), path={}, dataObject={}", path, dataObject);
186                 final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry =
187                         serializer.toNormalizedNode(path, dataObject);
188
189                 LOG.trace("OperationalDataTree.toNormalizedNode(), normalizedNodeEntry={}", entry);
190                 return entry.getValue();
191             }
192         };
193     }
194
195     private static final class ReadContextImpl implements ReadContext {
196         public final Context ctx = new Context();
197
198         @Nonnull
199         @Override
200         public Context getContext() {
201             return ctx;
202         }
203
204         @Override
205         public void close() {
206             // Make sure to clear the storage in case some customizer stored it  to prevent memory leaks
207             ctx.close();
208         }
209     }
210 }