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