2 * Copyright (c) 2016 Cisco and/or its affiliates.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package io.fd.honeycomb.v3po.data.impl;
19 import static com.google.common.base.Preconditions.checkNotNull;
20 import static com.google.common.collect.Iterables.getOnlyElement;
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.ReadableDataManager;
30 import io.fd.honeycomb.v3po.translate.MappingContext;
31 import io.fd.honeycomb.v3po.translate.ModificationCache;
32 import io.fd.honeycomb.v3po.translate.read.ReadContext;
33 import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
34 import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistry;
35 import io.fd.honeycomb.v3po.translate.util.TransactionMappingContext;
36 import java.util.Collection;
38 import javax.annotation.Nonnull;
39 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
40 import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
41 import org.opendaylight.yangtools.yang.binding.DataObject;
42 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
43 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
44 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
45 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
46 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
47 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
48 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
49 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
50 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
51 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
52 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
53 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
54 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
55 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
56 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
61 * ReadableDataTree implementation for operational data.
63 public final class ReadableDataTreeDelegator implements ReadableDataManager {
64 private static final Logger LOG = LoggerFactory.getLogger(ReadableDataTreeDelegator.class);
66 private final BindingNormalizedNodeSerializer serializer;
67 private final ReaderRegistry readerRegistry;
68 private final SchemaContext globalContext;
69 private final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker;
72 * Creates operational data tree instance.
73 * @param serializer service for serialization between Java Binding Data representation and NormalizedNode
75 * @param globalContext service for obtaining top level context data from all yang modules.
76 * @param readerRegistry service responsible for translation between DataObjects and data provider.
77 * @param contextBroker BA broker for context data
79 public ReadableDataTreeDelegator(@Nonnull BindingNormalizedNodeSerializer serializer,
80 @Nonnull final SchemaContext globalContext,
81 @Nonnull final ReaderRegistry readerRegistry,
82 @Nonnull final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker) {
83 this.contextBroker = checkNotNull(contextBroker, "contextBroker should not be null");
84 this.globalContext = checkNotNull(globalContext, "globalContext should not be null");
85 this.serializer = checkNotNull(serializer, "serializer should not be null");
86 this.readerRegistry = checkNotNull(readerRegistry, "reader should not be null");
90 public CheckedFuture<Optional<NormalizedNode<?, ?>>,
91 org.opendaylight.controller.md.sal.common.api.data.ReadFailedException> read(
92 @Nonnull final YangInstanceIdentifier yangInstanceIdentifier) {
94 try(TransactionMappingContext mappingContext = new TransactionMappingContext(contextBroker.newReadWriteTransaction());
95 ReadContext ctx = new ReadContextImpl(mappingContext)) {
97 final Optional<NormalizedNode<?, ?>> value;
98 if (checkNotNull(yangInstanceIdentifier).equals(YangInstanceIdentifier.EMPTY)) {
99 value = readRoot(ctx);
101 value = readNode(yangInstanceIdentifier, ctx);
104 // Submit context mapping updates
105 final CheckedFuture<Void, TransactionCommitFailedException> contextUpdateResult =
106 ((TransactionMappingContext) ctx.getMappingContext()).submit();
107 // Blocking on context data update
108 contextUpdateResult.checkedGet();
110 return Futures.immediateCheckedFuture(value);
112 } catch (ReadFailedException e) {
113 return Futures.immediateFailedCheckedFuture(
114 new org.opendaylight.controller.md.sal.common.api.data.ReadFailedException(
115 "Failed to read VPP data", e));
116 } catch (TransactionCommitFailedException e) {
117 // FIXME revert should probably occur when context is not written successfully
118 final String msg = "Error while updating mapping context data";
120 return Futures.immediateFailedCheckedFuture(
121 new org.opendaylight.controller.md.sal.common.api.data.ReadFailedException(msg, e)
126 private Optional<NormalizedNode<?, ?>> readNode(final YangInstanceIdentifier yangInstanceIdentifier,
127 final ReadContext ctx) throws ReadFailedException {
128 LOG.debug("OperationalDataTree.readNode(), yangInstanceIdentifier={}", yangInstanceIdentifier);
129 final InstanceIdentifier<?> path = serializer.fromYangInstanceIdentifier(yangInstanceIdentifier);
130 checkNotNull(path, "Invalid instance identifier %s. Cannot create BA equivalent.", yangInstanceIdentifier);
131 LOG.debug("OperationalDataTree.readNode(), path={}", path);
133 final Optional<? extends DataObject> dataObject;
135 dataObject = readerRegistry.read(path, ctx);
136 if (dataObject.isPresent()) {
137 final NormalizedNode<?, ?> value = toNormalizedNodeFunction(path).apply(dataObject.get());
138 return Optional.<NormalizedNode<?, ?>>fromNullable(value);
140 return Optional.absent();
144 private Optional<NormalizedNode<?, ?>> readRoot(final ReadContext ctx) throws ReadFailedException {
145 LOG.debug("OperationalDataTree.readRoot()");
147 final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> dataNodeBuilder =
148 Builders.containerBuilder()
149 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(SchemaContext.NAME));
151 final Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> dataObjects =
152 readerRegistry.readAll(ctx);
154 for (final InstanceIdentifier<? extends DataObject> instanceIdentifier : dataObjects.keySet()) {
155 final YangInstanceIdentifier rootElementId = serializer.toYangInstanceIdentifier(instanceIdentifier);
156 final NormalizedNode<?, ?> node =
157 wrapDataObjects(rootElementId, instanceIdentifier, dataObjects.get(instanceIdentifier));
158 dataNodeBuilder.withChild((DataContainerChild<?, ?>) node);
160 return Optional.<NormalizedNode<?, ?>>of(dataNodeBuilder.build());
163 private NormalizedNode<?, ?> wrapDataObjects(final YangInstanceIdentifier yangInstanceIdentifier,
164 final InstanceIdentifier<? extends DataObject> instanceIdentifier,
165 final Collection<? extends DataObject> dataObjects) {
166 final Collection<NormalizedNode<?, ?>> normalizedRootElements = Collections2
167 .transform(dataObjects, toNormalizedNodeFunction(instanceIdentifier));
169 final DataSchemaNode schemaNode =
170 globalContext.getDataChildByName(yangInstanceIdentifier.getLastPathArgument().getNodeType());
171 if (schemaNode instanceof ListSchemaNode) {
172 // In case of a list, wrap all the values in a Mixin parent node
173 final ListSchemaNode listSchema = (ListSchemaNode) schemaNode;
174 return wrapListIntoMixinNode(normalizedRootElements, listSchema);
176 Preconditions.checkState(dataObjects.size() == 1, "Singleton list was expected");
177 return getOnlyElement(normalizedRootElements);
181 private static DataContainerChild<?, ?> wrapListIntoMixinNode(
182 final Collection<NormalizedNode<?, ?>> normalizedRootElements, final ListSchemaNode listSchema) {
183 if (listSchema.getKeyDefinition().isEmpty()) {
184 final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> listBuilder =
185 Builders.unkeyedListBuilder();
186 for (NormalizedNode<?, ?> normalizedRootElement : normalizedRootElements) {
187 listBuilder.withChild((UnkeyedListEntryNode) normalizedRootElement);
189 return listBuilder.build();
191 final CollectionNodeBuilder<MapEntryNode, ? extends MapNode> listBuilder =
192 listSchema.isUserOrdered()
193 ? Builders.orderedMapBuilder()
194 : Builders.mapBuilder();
196 for (NormalizedNode<?, ?> normalizedRootElement : normalizedRootElements) {
197 listBuilder.withChild((MapEntryNode) normalizedRootElement);
199 return listBuilder.build();
203 @SuppressWarnings("unchecked")
204 private Function<DataObject, NormalizedNode<?, ?>> toNormalizedNodeFunction(final InstanceIdentifier path) {
205 return dataObject -> {
206 LOG.trace("OperationalDataTree.toNormalizedNode(), path={}, dataObject={}", path, dataObject);
207 final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry =
208 serializer.toNormalizedNode(path, dataObject);
210 LOG.trace("OperationalDataTree.toNormalizedNode(), normalizedNodeEntry={}", entry);
211 return entry.getValue();
215 private static final class ReadContextImpl implements ReadContext {
217 private final ModificationCache ctx = new ModificationCache();
218 private final MappingContext mappingContext;
220 private ReadContextImpl(final MappingContext mappingContext) {
221 this.mappingContext = mappingContext;
226 public ModificationCache getModificationCache() {
232 public MappingContext getMappingContext() {
233 return mappingContext;
237 public void close() {
238 // Make sure to clear the storage in case some customizer stored a reference to it to prevent memory leaks