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