Bug 6325 - Fix for draft15 update
[netconf.git] / restconf / sal-rest-connector / src / main / java / org / opendaylight / restconf / restful / utils / ReadDataTransactionUtil.java
1 /*
2  * Copyright (c) 2016 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.restconf.restful.utils;
9
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.util.concurrent.CheckedFuture;
13 import java.util.Collection;
14 import javax.annotation.Nonnull;
15 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
16 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
17 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
18 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
19 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
20 import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
21 import org.opendaylight.yangtools.yang.common.QNameModule;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
25 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
26 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
27 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
29 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
35 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
36 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
37 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
38 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
39 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
40
41 /**
42  * Util class for read data from data store via transaction.
43  * <ul>
44  * <li>config
45  * <li>state
46  * <li>all (config + state)
47  * </ul>
48  *
49  */
50 public final class ReadDataTransactionUtil {
51
52     private ReadDataTransactionUtil() {
53         throw new UnsupportedOperationException("Util class.");
54     }
55
56     /**
57      * Read specific type of data from data store via transaction.
58      *
59      * @param valueOfContent
60      *            - type of data to read (config, state, all)
61      * @param transactionNode
62      *            - {@link TransactionVarsWrapper} - wrapper for variables
63      * @return {@link NormalizedNode}
64      */
65     public static NormalizedNode<?, ?> readData(final String valueOfContent, final TransactionVarsWrapper transactionNode) {
66         if (valueOfContent != null) {
67             switch (valueOfContent) {
68                 case RestconfDataServiceConstant.ReadData.CONFIG:
69                     transactionNode.setLogicalDatastoreType(LogicalDatastoreType.CONFIGURATION);
70                     return readDataViaTransaction(transactionNode);
71                 case RestconfDataServiceConstant.ReadData.NONCONFIG:
72                     transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
73                     return readDataViaTransaction(transactionNode);
74                 case RestconfDataServiceConstant.ReadData.ALL:
75                     return readDataViaTransaction(transactionNode);
76                 default:
77                     throw new RestconfDocumentedException("Bad querry parameter for content.", ErrorType.APPLICATION,
78                             ErrorTag.INVALID_VALUE);
79             }
80         } else {
81             return readDataViaTransaction(transactionNode);
82         }
83     }
84
85     /**
86      * If is set specific {@link LogicalDatastoreType} in
87      * {@link TransactionVarsWrapper}, then read this type of data from DS. If
88      * don't, we have to read all data from DS (state + config)
89      *
90      * @param transactionNode
91      *            - {@link TransactionVarsWrapper} - wrapper for variables
92      * @return {@link NormalizedNode}
93      */
94     private static NormalizedNode<?, ?> readDataViaTransaction(final TransactionVarsWrapper transactionNode) {
95         if (transactionNode.getLogicalDatastoreType() != null) {
96             final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> listenableFuture = transactionNode
97                     .getTransactionChain().newReadOnlyTransaction().read(transactionNode.getLogicalDatastoreType(),
98                             transactionNode.getInstanceIdentifier().getInstanceIdentifier());
99             final NormalizedNodeFactory dataFactory = new NormalizedNodeFactory();
100             FutureCallbackTx.addCallback(listenableFuture, RestconfDataServiceConstant.ReadData.READ_TYPE_TX,
101                     dataFactory);
102             return dataFactory.build();
103         } else {
104             return readAllData(transactionNode);
105         }
106     }
107
108     /**
109      * Read config and state data, then map them.
110      *
111      * @param transactionNode
112      *            - {@link TransactionVarsWrapper} - wrapper for variables
113      * @return {@link NormalizedNode}
114      */
115     private static NormalizedNode<?, ?> readAllData(final TransactionVarsWrapper transactionNode) {
116         // PREPARE STATE DATA NODE
117         transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
118         final NormalizedNode<?, ?> stateDataNode = readDataViaTransaction(transactionNode);
119
120         // PREPARE CONFIG DATA NODE
121         transactionNode.setLogicalDatastoreType(LogicalDatastoreType.CONFIGURATION);
122         final NormalizedNode<?, ?> configDataNode = readDataViaTransaction(transactionNode);
123
124         // if no data exists
125         if ((stateDataNode == null) && (configDataNode == null)) {
126             throw new RestconfDocumentedException(
127                     "Request could not be completed because the relevant data model content does not exist",
128                     ErrorType.PROTOCOL,
129                     ErrorTag.DATA_MISSING);
130         }
131
132         // return config data
133         if (stateDataNode == null) {
134             return configDataNode;
135         }
136
137         // return state data
138         if (configDataNode == null) {
139             return stateDataNode;
140         }
141
142         // merge data from config and state
143         return mapNode(stateDataNode, configDataNode);
144     }
145
146     /**
147      * Map data by type of read node.
148      *
149      * @param stateDataNode
150      *            - data node of state data
151      * @param configDataNode
152      *            - data node of config data
153      * @param transactionNode
154      *            - {@link TransactionVarsWrapper} - wrapper for variables
155      * @return {@link NormalizedNode}
156      */
157     private static NormalizedNode<?, ?> mapNode(final NormalizedNode<?, ?> stateDataNode,
158             final NormalizedNode<?, ?> configDataNode) {
159         validPossibilityOfMergeNodes(stateDataNode, configDataNode);
160         if (configDataNode instanceof RpcDefinition) {
161             return prepareRpcData(configDataNode, stateDataNode);
162         } else {
163             return prepareData(configDataNode, stateDataNode);
164         }
165     }
166
167     /**
168      * Prepare and map data for rpc
169      *
170      * @param configDataNode
171      *            - data node of config data
172      * @param stateDataNode
173      *            - data node of state data
174      * @return {@link NormalizedNode}
175      */
176     private static NormalizedNode<?, ?> prepareRpcData(final NormalizedNode<?, ?> configDataNode,
177             final NormalizedNode<?, ?> stateDataNode) {
178         final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder = ImmutableNodes
179                 .mapEntryBuilder();
180         mapEntryBuilder.withNodeIdentifier((NodeIdentifierWithPredicates) configDataNode.getIdentifier());
181
182         // MAP CONFIG DATA
183         mapRpcDataNode(configDataNode, mapEntryBuilder);
184         // MAP STATE DATA
185         mapRpcDataNode(stateDataNode, mapEntryBuilder);
186
187         return ImmutableNodes.mapNodeBuilder(configDataNode.getNodeType()).addChild(mapEntryBuilder.build()).build();
188     }
189
190     /**
191      * Map node to map entry builder.
192      *
193      * @param dataNode
194      *            - data node
195      * @param mapEntryBuilder
196      *            - builder for mapping data
197      */
198     private static void mapRpcDataNode(final NormalizedNode<?, ?> dataNode,
199             final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder) {
200         for (final DataContainerChild<? extends PathArgument, ?> child : ((ContainerNode) dataNode).getValue()) {
201             mapEntryBuilder.addChild(child);
202         }
203     }
204
205     /**
206      * Prepare and map all data from DS
207      *
208      * @param configDataNode
209      *            - data node of config data
210      * @param stateDataNode
211      *            - data node of state data
212      * @return {@link NormalizedNode}
213      */
214     private static NormalizedNode<?, ?> prepareData(final NormalizedNode<?, ?> configDataNode,
215             final NormalizedNode<?, ?> stateDataNode) {
216
217         if (configDataNode instanceof MapNode) { // part for lists mapping
218             final MapNode immutableStateData = ImmutableNodes.mapNodeBuilder(stateDataNode.getNodeType())
219                 .addChild((MapEntryNode) stateDataNode).build();
220             final MapNode immutableConfigData = ImmutableNodes.mapNodeBuilder(configDataNode.getNodeType())
221                 .addChild((MapEntryNode) configDataNode).build();
222             final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder = ImmutableNodes
223                 .mapEntryBuilder();
224             mapEntryBuilder.withNodeIdentifier((NodeIdentifierWithPredicates) configDataNode.getIdentifier());
225
226             // MAP CONFIG DATA
227             mapDataNode(immutableConfigData, mapEntryBuilder);
228             // MAP STATE DATA
229             mapDataNode(immutableStateData, mapEntryBuilder);
230             return ImmutableNodes.mapNodeBuilder(configDataNode.getNodeType()).addChild(mapEntryBuilder.build()).build();
231         } else if (configDataNode instanceof ContainerNode) { // part for
232                                                               // containers
233                                                               // mapping
234             final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerBuilder = Builders
235                     .containerBuilder((ContainerNode) configDataNode);
236             // MAP CONFIG DATA
237             mapCont(containerBuilder, ((ContainerNode) configDataNode).getValue());
238             // MAP STATE DATA
239             mapCont(containerBuilder, ((ContainerNode) stateDataNode).getValue());
240
241             return containerBuilder.build();
242         } else {
243             throw new RestconfDocumentedException("Bad type of node.");
244         }
245     }
246
247     /**
248      * Map data to builder
249      *
250      * @param containerBuilder
251      *            - builder for mapping data
252      * @param childs
253      *            - childs of data (container)
254      */
255     private static void mapCont(final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerBuilder,
256             final Collection<DataContainerChild<? extends PathArgument, ?>> childs) {
257         for (final DataContainerChild<? extends PathArgument, ?> child : childs) {
258             containerBuilder.addChild(child);
259         }
260     }
261
262     /**
263      * Map data to builder
264      *
265      * @param immutableData
266      *            - immutable data - {@link MapNode}
267      * @param mapEntryBuilder
268      *            - builder for mapping data
269      */
270     private static void mapDataNode(final MapNode immutableData,
271             final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder) {
272         for (final DataContainerChild<? extends PathArgument, ?> child : immutableData.getValue().iterator()
273                 .next().getValue()) {
274             Preconditions.checkNotNull(child);
275             if (child instanceof ContainerNode) {
276                 addChildToMap(ContainerNode.class, child, mapEntryBuilder);
277             } else if (child instanceof AugmentationNode) {
278                 addChildToMap(AugmentationNode.class, child, mapEntryBuilder);
279             } else if(child instanceof MapNode){
280                 final MapNode listNode = (MapNode) child;
281                 for (final MapEntryNode listChild : listNode.getValue()) {
282                     for (final DataContainerChild<? extends PathArgument, ?> entryChild : listChild.getValue()) {
283                         addChildToMap(MapEntryNode.class, entryChild, mapEntryBuilder);
284                     }
285                 }
286             } else if (child instanceof ChoiceNode) {
287                 addChildToMap(ChoiceNode.class, child, mapEntryBuilder);
288             } else if ((child instanceof LeafSetNode<?>) || (child instanceof LeafNode)) {
289                 mapEntryBuilder.addChild(child);
290             }
291
292         }
293     }
294
295     /**
296      * Mapping child
297      *
298      * @param type
299      *            - type of data
300      * @param child
301      *            - child to map
302      * @param mapEntryBuilder
303      *            - builder for mapping child
304      */
305     private static <T extends DataContainerNode<? extends PathArgument>> void addChildToMap(final Class<T> type,
306             final DataContainerChild<? extends PathArgument, ?> child,
307             final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder) {
308         @SuppressWarnings("unchecked")
309         final T node = (T) child;
310         for (final DataContainerChild<? extends PathArgument, ?> childNode : node.getValue()) {
311             mapEntryBuilder.addChild(childNode);
312         }
313     }
314
315     /**
316      * Valid of can be data merged together.
317      *
318      * @param stateDataNode
319      *            - data node of state data
320      * @param configDataNode
321      *            - data node of config data
322      */
323     private static void validPossibilityOfMergeNodes(@Nonnull final NormalizedNode<?, ?> stateDataNode,
324             @Nonnull final NormalizedNode<?, ?> configDataNode) {
325         final QNameModule moduleOfStateData = stateDataNode.getIdentifier().getNodeType().getModule();
326         final QNameModule moduleOfConfigData = configDataNode.getIdentifier().getNodeType().getModule();
327         if (moduleOfStateData != moduleOfConfigData) {
328             throw new RestconfDocumentedException("It is not possible to merge ");
329         }
330     }
331 }