Merge "Bug 225 - added support for depth RESTCONF parameter in URI"
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / main / java / org / opendaylight / controller / sal / restconf / impl / RestconfImpl.java
1 /**
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  * Copyright (c) 2014 Brocade Communication Systems, Inc.
4  *
5  * This program and the accompanying materials are made available under the
6  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7  * and is available at http://www.eclipse.org/legal/epl-v10.html
8  */
9 package org.opendaylight.controller.sal.restconf.impl;
10
11 import java.net.URI;
12 import java.text.ParseException;
13 import java.text.SimpleDateFormat;
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.Date;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Set;
22 import java.util.concurrent.Future;
23
24 import javax.ws.rs.core.Response;
25 import javax.ws.rs.core.Response.Status;
26 import javax.ws.rs.core.UriBuilder;
27 import javax.ws.rs.core.UriInfo;
28
29 import org.apache.commons.lang3.StringUtils;
30 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
31 import org.opendaylight.controller.sal.core.api.mount.MountInstance;
32 import org.opendaylight.controller.sal.rest.api.Draft02;
33 import org.opendaylight.controller.sal.rest.api.RestconfService;
34 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
35 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
36 import org.opendaylight.controller.sal.restconf.rpc.impl.BrokerRpcExecutor;
37 import org.opendaylight.controller.sal.restconf.rpc.impl.MountPointRpcExecutor;
38 import org.opendaylight.controller.sal.restconf.rpc.impl.RpcExecutor;
39 import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
40 import org.opendaylight.controller.sal.streams.listeners.Notificator;
41 import org.opendaylight.controller.sal.streams.websockets.WebSocketServer;
42 import org.opendaylight.yangtools.concepts.Codec;
43 import org.opendaylight.yangtools.yang.common.QName;
44 import org.opendaylight.yangtools.yang.common.RpcError;
45 import org.opendaylight.yangtools.yang.common.RpcResult;
46 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
47 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
48 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder;
49 import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
50 import org.opendaylight.yangtools.yang.data.api.Node;
51 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
52 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
53 import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
54 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
55 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
56 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
57 import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
58 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
59 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
60 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
61 import org.opendaylight.yangtools.yang.model.api.Module;
62 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
63 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
64 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
65 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
66 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
67 import org.opendaylight.yangtools.yang.model.util.EmptyType;
68 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
69 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
70
71 import com.google.common.base.Objects;
72 import com.google.common.base.Preconditions;
73 import com.google.common.base.Splitter;
74 import com.google.common.base.Strings;
75 import com.google.common.collect.ImmutableList;
76 import com.google.common.collect.Iterables;
77 import com.google.common.collect.Lists;
78
79 public class RestconfImpl implements RestconfService {
80     private final static RestconfImpl INSTANCE = new RestconfImpl();
81
82     private static final int CHAR_NOT_FOUND = -1;
83
84     private final static String MOUNT_POINT_MODULE_NAME = "ietf-netconf";
85
86     private final static SimpleDateFormat REVISION_FORMAT =  new SimpleDateFormat("yyyy-MM-dd");
87
88     private final static String SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote";
89
90     private final static String SAL_REMOTE_RPC_SUBSRCIBE = "create-data-change-event-subscription";
91
92     private BrokerFacade broker;
93
94     private ControllerContext controllerContext;
95
96     public void setBroker(final BrokerFacade broker) {
97         this.broker = broker;
98     }
99
100     public void setControllerContext(final ControllerContext controllerContext) {
101         this.controllerContext = controllerContext;
102     }
103
104     private RestconfImpl() {
105     }
106
107     public static RestconfImpl getInstance() {
108         return INSTANCE;
109     }
110
111     @Override
112     public StructuredData getModules() {
113         final Module restconfModule = this.getRestconfModule();
114
115         final List<Node<?>> modulesAsData = new ArrayList<Node<?>>();
116         final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
117                                         restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
118
119         Set<Module> allModules = this.controllerContext.getAllModules();
120         for (final Module module : allModules) {
121             CompositeNode moduleCompositeNode = this.toModuleCompositeNode(module, moduleSchemaNode);
122             modulesAsData.add(moduleCompositeNode);
123         }
124
125         final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
126                                    restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
127         QName qName = modulesSchemaNode.getQName();
128         final CompositeNode modulesNode = NodeFactory.createImmutableCompositeNode(qName, null, modulesAsData);
129         return new StructuredData(modulesNode, modulesSchemaNode, null);
130     }
131
132     @Override
133     public StructuredData getAvailableStreams() {
134         Set<String> availableStreams = Notificator.getStreamNames();
135
136         final List<Node<?>> streamsAsData = new ArrayList<Node<?>>();
137         Module restconfModule = this.getRestconfModule();
138         final DataSchemaNode streamSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
139                                              restconfModule, Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
140         for (final String streamName : availableStreams) {
141             streamsAsData.add(this.toStreamCompositeNode(streamName, streamSchemaNode));
142         }
143
144         final DataSchemaNode streamsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
145                                      restconfModule, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
146         QName qName = streamsSchemaNode.getQName();
147         final CompositeNode streamsNode = NodeFactory.createImmutableCompositeNode(qName, null, streamsAsData);
148         return new StructuredData(streamsNode, streamsSchemaNode, null);
149     }
150
151     @Override
152     public StructuredData getModules(final String identifier) {
153         Set<Module> modules = null;
154         MountInstance mountPoint = null;
155         if (identifier.contains(ControllerContext.MOUNT)) {
156             InstanceIdWithSchemaNode mountPointIdentifier =
157                                            this.controllerContext.toMountPointIdentifier(identifier);
158             mountPoint = mountPointIdentifier.getMountPoint();
159             modules = this.controllerContext.getAllModules(mountPoint);
160         }
161         else {
162             throw new RestconfDocumentedException(
163                     "URI has bad format. If modules behind mount point should be showed, URI has to end with " +
164                     ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
165         }
166
167         final List<Node<?>> modulesAsData = new ArrayList<Node<?>>();
168         Module restconfModule = this.getRestconfModule();
169         final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
170                                          restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
171
172         for (final Module module : modules) {
173             modulesAsData.add(this.toModuleCompositeNode(module, moduleSchemaNode));
174         }
175
176         final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
177                                   restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
178         QName qName = modulesSchemaNode.getQName();
179         final CompositeNode modulesNode = NodeFactory.createImmutableCompositeNode(qName, null, modulesAsData);
180         return new StructuredData(modulesNode, modulesSchemaNode, mountPoint);
181     }
182
183     @Override
184     public StructuredData getModule(final String identifier) {
185         final QName moduleNameAndRevision = this.getModuleNameAndRevision(identifier);
186         Module module = null;
187         MountInstance mountPoint = null;
188         if (identifier.contains(ControllerContext.MOUNT)) {
189             InstanceIdWithSchemaNode mountPointIdentifier =
190                                             this.controllerContext.toMountPointIdentifier(identifier);
191             mountPoint = mountPointIdentifier.getMountPoint();
192             module = this.controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision);
193         }
194         else {
195             module = this.controllerContext.findModuleByNameAndRevision(moduleNameAndRevision);
196         }
197
198         if (module == null) {
199             throw new RestconfDocumentedException(
200                     "Module with name '" + moduleNameAndRevision.getLocalName() + "' and revision '" +
201                     moduleNameAndRevision.getRevision() + "' was not found.",
202                     ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
203         }
204
205         Module restconfModule = this.getRestconfModule();
206         final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
207                                           restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
208         final CompositeNode moduleNode = this.toModuleCompositeNode(module, moduleSchemaNode);
209         return new StructuredData(moduleNode, moduleSchemaNode, mountPoint);
210     }
211
212     @Override
213     public StructuredData getOperations() {
214         Set<Module> allModules = this.controllerContext.getAllModules();
215         return this.operationsFromModulesToStructuredData(allModules, null);
216     }
217
218     @Override
219     public StructuredData getOperations(final String identifier) {
220         Set<Module> modules = null;
221         MountInstance mountPoint = null;
222         if (identifier.contains(ControllerContext.MOUNT)) {
223             InstanceIdWithSchemaNode mountPointIdentifier =
224                                          this.controllerContext.toMountPointIdentifier(identifier);
225             mountPoint = mountPointIdentifier.getMountPoint();
226             modules = this.controllerContext.getAllModules(mountPoint);
227         }
228         else {
229             throw new RestconfDocumentedException(
230                     "URI has bad format. If operations behind mount point should be showed, URI has to end with " +
231                     ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
232         }
233
234         return this.operationsFromModulesToStructuredData(modules, mountPoint);
235     }
236
237     private StructuredData operationsFromModulesToStructuredData(final Set<Module> modules,
238                                                                  final MountInstance mountPoint) {
239         final List<Node<?>> operationsAsData = new ArrayList<Node<?>>();
240         Module restconfModule = this.getRestconfModule();
241         final DataSchemaNode operationsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
242                               restconfModule, Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE);
243         QName qName = operationsSchemaNode.getQName();
244         SchemaPath path = operationsSchemaNode.getPath();
245         ContainerSchemaNodeBuilder containerSchemaNodeBuilder =
246                              new ContainerSchemaNodeBuilder(Draft02.RestConfModule.NAME, 0, qName, path);
247         final ContainerSchemaNodeBuilder fakeOperationsSchemaNode = containerSchemaNodeBuilder;
248         for (final Module module : modules) {
249             Set<RpcDefinition> rpcs = module.getRpcs();
250             for (final RpcDefinition rpc : rpcs) {
251                 QName rpcQName = rpc.getQName();
252                 SimpleNode<Object> immutableSimpleNode =
253                                      NodeFactory.<Object>createImmutableSimpleNode(rpcQName, null, null);
254                 operationsAsData.add(immutableSimpleNode);
255
256                 String name = module.getName();
257                 LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName, null);
258                 final LeafSchemaNodeBuilder fakeRpcSchemaNode = leafSchemaNodeBuilder;
259                 fakeRpcSchemaNode.setAugmenting(true);
260
261                 EmptyType instance = EmptyType.getInstance();
262                 fakeRpcSchemaNode.setType(instance);
263                 fakeOperationsSchemaNode.addChildNode(fakeRpcSchemaNode.build());
264             }
265         }
266
267         final CompositeNode operationsNode =
268                                   NodeFactory.createImmutableCompositeNode(qName, null, operationsAsData);
269         ContainerSchemaNode schemaNode = fakeOperationsSchemaNode.build();
270         return new StructuredData(operationsNode, schemaNode, mountPoint);
271     }
272
273     private Module getRestconfModule() {
274         Module restconfModule = controllerContext.getRestconfModule();
275         if (restconfModule == null) {
276             throw new RestconfDocumentedException(
277                     "ietf-restconf module was not found.", ErrorType.APPLICATION,
278                     ErrorTag.OPERATION_NOT_SUPPORTED );
279         }
280
281         return restconfModule;
282     }
283
284     private QName getModuleNameAndRevision(final String identifier) {
285         final int mountIndex = identifier.indexOf(ControllerContext.MOUNT);
286         String moduleNameAndRevision = "";
287         if (mountIndex >= 0) {
288             moduleNameAndRevision = identifier.substring(mountIndex + ControllerContext.MOUNT.length());
289         }
290         else {
291             moduleNameAndRevision = identifier;
292         }
293
294         Splitter splitter = Splitter.on("/").omitEmptyStrings();
295         Iterable<String> split = splitter.split(moduleNameAndRevision);
296         final List<String> pathArgs = Lists.<String>newArrayList(split);
297         if (pathArgs.size() < 2) {
298             throw new RestconfDocumentedException(
299                     "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'",
300                     ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
301         }
302
303         try {
304             final String moduleName = pathArgs.get( 0 );
305             String revision = pathArgs.get(1);
306             final Date moduleRevision = REVISION_FORMAT.parse(revision);
307             return QName.create(null, moduleRevision, moduleName);
308         }
309         catch (ParseException e) {
310             throw new RestconfDocumentedException(
311                     "URI has bad format. It should be \'moduleName/yyyy-MM-dd\'",
312                     ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
313         }
314     }
315
316     private CompositeNode toStreamCompositeNode(final String streamName, final DataSchemaNode streamSchemaNode) {
317         final List<Node<?>> streamNodeValues = new ArrayList<Node<?>>();
318         List<DataSchemaNode> instanceDataChildrenByName =
319                 this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) streamSchemaNode),
320                                                                        "name");
321         final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
322         streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(nameSchemaNode.getQName(), null,
323                                                                            streamName));
324
325         instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
326                                                  ((DataNodeContainer) streamSchemaNode), "description");
327         final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
328         streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(descriptionSchemaNode.getQName(), null,
329                                                                            "DESCRIPTION_PLACEHOLDER"));
330
331         instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
332                                                ((DataNodeContainer) streamSchemaNode), "replay-support");
333         final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
334         streamNodeValues.add(NodeFactory.<Boolean>createImmutableSimpleNode(replaySupportSchemaNode.getQName(), null,
335                                                                             Boolean.valueOf(true)));
336
337         instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
338                                            ((DataNodeContainer) streamSchemaNode), "replay-log-creation-time");
339         final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
340         streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(replayLogCreationTimeSchemaNode.getQName(),
341                                                                            null, ""));
342
343         instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
344                                                         ((DataNodeContainer) streamSchemaNode), "events");
345         final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
346         streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(eventsSchemaNode.getQName(),
347                                                                            null, ""));
348
349         return NodeFactory.createImmutableCompositeNode(streamSchemaNode.getQName(), null, streamNodeValues);
350     }
351
352     private CompositeNode toModuleCompositeNode(final Module module, final DataSchemaNode moduleSchemaNode) {
353         final List<Node<?>> moduleNodeValues = new ArrayList<Node<?>>();
354         List<DataSchemaNode> instanceDataChildrenByName =
355             this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) moduleSchemaNode), "name");
356         final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
357         moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(nameSchemaNode.getQName(),
358                                                                            null, module.getName()));
359
360         instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
361                                                           ((DataNodeContainer) moduleSchemaNode), "revision");
362         final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
363         Date _revision = module.getRevision();
364         moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(revisionSchemaNode.getQName(), null,
365                                                                            REVISION_FORMAT.format(_revision)));
366
367         instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
368                                                         ((DataNodeContainer) moduleSchemaNode), "namespace");
369         final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
370         moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(namespaceSchemaNode.getQName(), null,
371                                                                            module.getNamespace().toString()));
372
373         instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
374                                                            ((DataNodeContainer) moduleSchemaNode), "feature");
375         final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
376         for (final FeatureDefinition feature : module.getFeatures()) {
377             moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(featureSchemaNode.getQName(), null,
378                                                                                feature.getQName().getLocalName()));
379         }
380
381         return NodeFactory.createImmutableCompositeNode(moduleSchemaNode.getQName(), null, moduleNodeValues);
382     }
383
384     @Override
385     public Object getRoot() {
386         return null;
387     }
388
389     @Override
390     public StructuredData invokeRpc(final String identifier, final CompositeNode payload) {
391         final RpcExecutor rpc = this.resolveIdentifierInInvokeRpc(identifier);
392         QName rpcName = rpc.getRpcDefinition().getQName();
393         URI rpcNamespace = rpcName.getNamespace();
394         if (Objects.equal(rpcNamespace.toString(), SAL_REMOTE_NAMESPACE) &&
395             Objects.equal(rpcName.getLocalName(), SAL_REMOTE_RPC_SUBSRCIBE)) {
396             return invokeSalRemoteRpcSubscribeRPC(payload, rpc.getRpcDefinition());
397         }
398
399         validateInput( rpc.getRpcDefinition().getInput(), payload );
400
401         return callRpc(rpc, payload);
402     }
403
404     private void validateInput(DataSchemaNode inputSchema, CompositeNode payload) {
405         if( inputSchema != null && payload == null )
406         {
407             //expected a non null payload
408             throw new RestconfDocumentedException( "Input is required.",
409                                                    ErrorType.PROTOCOL,
410                                                    ErrorTag.MALFORMED_MESSAGE );
411         }
412         else if( inputSchema == null && payload != null )
413         {
414             //did not expect any input
415             throw new RestconfDocumentedException( "No input expected.",
416                                                    ErrorType.PROTOCOL,
417                                                    ErrorTag.MALFORMED_MESSAGE );
418         }
419         //else
420         //{
421             //TODO: Validate "mandatory" and "config" values here??? Or should those be
422         // validate in a more central location inside MD-SAL core.
423         //}
424     }
425
426     private StructuredData invokeSalRemoteRpcSubscribeRPC(final CompositeNode payload,
427                                                           final RpcDefinition rpc) {
428         final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
429         final SimpleNode<? extends Object> pathNode = value == null ? null :
430                                value.getFirstSimpleByName( QName.create(rpc.getQName(), "path") );
431         final Object pathValue = pathNode == null ? null : pathNode.getValue();
432
433         if (!(pathValue instanceof InstanceIdentifier)) {
434             throw new RestconfDocumentedException(
435                     "Instance identifier was not normalized correctly.",
436                     ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED );
437         }
438
439         final InstanceIdentifier pathIdentifier = ((InstanceIdentifier) pathValue);
440         String streamName = null;
441         if (!Iterables.isEmpty(pathIdentifier.getPath())) {
442             String fullRestconfIdentifier = this.controllerContext.toFullRestconfIdentifier(pathIdentifier);
443             streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier);
444         }
445
446         if (Strings.isNullOrEmpty(streamName)) {
447             throw new RestconfDocumentedException(
448                     "Path is empty or contains data node which is not Container or List build-in type.",
449                     ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
450         }
451
452         final SimpleNode<String> streamNameNode = NodeFactory.<String>createImmutableSimpleNode(
453                              QName.create(rpc.getOutput().getQName(), "stream-name"), null, streamName);
454         final List<Node<?>> output = new ArrayList<Node<?>>();
455         output.add(streamNameNode);
456
457         final MutableCompositeNode responseData = NodeFactory.createMutableCompositeNode(
458                                               rpc.getOutput().getQName(), null, output, null, null);
459
460         if (!Notificator.existListenerFor(pathIdentifier)) {
461             Notificator.createListener(pathIdentifier, streamName);
462         }
463
464         return new StructuredData(responseData, rpc.getOutput(), null);
465     }
466
467     @Override
468     public StructuredData invokeRpc(final String identifier, final String noPayload) {
469         if (StringUtils.isNotBlank(noPayload)) {
470             throw new RestconfDocumentedException(
471                     "Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
472         }
473         return invokeRpc( identifier, (CompositeNode)null );
474     }
475
476     private RpcExecutor resolveIdentifierInInvokeRpc(final String identifier) {
477         String identifierEncoded = null;
478         MountInstance mountPoint = null;
479         if (identifier.contains(ControllerContext.MOUNT)) {
480             // mounted RPC call - look up mount instance.
481             InstanceIdWithSchemaNode mountPointId = controllerContext
482                     .toMountPointIdentifier(identifier);
483             mountPoint = mountPointId.getMountPoint();
484
485             int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
486                     + ControllerContext.MOUNT.length() + 1;
487             String remoteRpcName = identifier.substring(startOfRemoteRpcName);
488             identifierEncoded = remoteRpcName;
489
490         } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) {
491             final String slashErrorMsg = String
492                     .format("Identifier %n%s%ncan\'t contain slash "
493                             + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.",
494                             identifier);
495             throw new RestconfDocumentedException(
496                     slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
497         } else {
498             identifierEncoded = identifier;
499         }
500
501         final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
502         RpcDefinition rpc = controllerContext.getRpcDefinition(identifierDecoded);
503
504         if (rpc == null) {
505             throw new RestconfDocumentedException(
506                     "RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT );
507         }
508
509         if (mountPoint == null) {
510             return new BrokerRpcExecutor(rpc, broker);
511         } else {
512             return new MountPointRpcExecutor(rpc, mountPoint);
513         }
514
515     }
516
517     private StructuredData callRpc(final RpcExecutor rpcExecutor, final CompositeNode payload) {
518         if (rpcExecutor == null) {
519             throw new RestconfDocumentedException(
520                     "RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT );
521         }
522
523         CompositeNode rpcRequest = null;
524         RpcDefinition rpc = rpcExecutor.getRpcDefinition();
525         QName rpcName = rpc.getQName();
526
527         if (payload == null) {
528             rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, null, null, null);
529         } else {
530             final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
531             List<Node<?>> input = Collections.<Node<?>> singletonList(value);
532             rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, input, null, null);
533         }
534
535         RpcResult<CompositeNode> rpcResult = rpcExecutor.invokeRpc(rpcRequest);
536
537         checkRpcSuccessAndThrowException(rpcResult);
538
539         if (rpcResult.getResult() == null) {
540             return null;
541         }
542
543         if( rpc.getOutput() == null )
544         {
545             return null; //no output, nothing to send back.
546         }
547
548         return new StructuredData(rpcResult.getResult(), rpc.getOutput(), null);
549     }
550
551     private void checkRpcSuccessAndThrowException(RpcResult<CompositeNode> rpcResult) {
552         if (rpcResult.isSuccessful() == false) {
553
554             Collection<RpcError> rpcErrors = rpcResult.getErrors();
555             if( rpcErrors == null || rpcErrors.isEmpty() ) {
556                 throw new RestconfDocumentedException(
557                     "The operation was not successful and there were no RPC errors returned",
558                     ErrorType.RPC, ErrorTag.OPERATION_FAILED );
559             }
560
561             List<RestconfError> errorList = Lists.newArrayList();
562             for( RpcError rpcError: rpcErrors ) {
563                 errorList.add( new RestconfError( rpcError ) );
564             }
565
566             throw new RestconfDocumentedException( errorList );
567         }
568     }
569
570     @Override
571     public StructuredData readConfigurationData(final String identifier, UriInfo info) {
572         final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
573         CompositeNode data = null;
574         MountInstance mountPoint = iiWithData.getMountPoint();
575         if (mountPoint != null) {
576             data = broker.readConfigurationDataBehindMountPoint(mountPoint, iiWithData.getInstanceIdentifier());
577         }
578         else {
579             data = broker.readConfigurationData(iiWithData.getInstanceIdentifier());
580         }
581
582         data = pruneDataAtDepth( data, parseDepthParameter( info ) );
583         return new StructuredData(data, iiWithData.getSchemaNode(), iiWithData.getMountPoint());
584     }
585
586     @SuppressWarnings("unchecked")
587     private <T extends Node<?>> T pruneDataAtDepth( T node, Integer depth ) {
588         if( depth == null ) {
589             return node;
590         }
591
592         if( node instanceof CompositeNode ) {
593             ImmutableList.Builder<Node<?>> newChildNodes = ImmutableList.<Node<?>> builder();
594             if( depth > 1 ) {
595                 for( Node<?> childNode: ((CompositeNode)node).getValue() ) {
596                     newChildNodes.add( pruneDataAtDepth( childNode, depth - 1 ) );
597                 }
598             }
599
600             return (T) ImmutableCompositeNode.create( node.getNodeType(), newChildNodes.build() );
601         }
602         else { // SimpleNode
603             return node;
604         }
605     }
606
607     private Integer parseDepthParameter( UriInfo info ) {
608         String param = info.getQueryParameters( false ).getFirst( "depth" );
609         if( Strings.isNullOrEmpty( param ) || "unbounded".equals( param ) ) {
610             return null;
611         }
612
613         try {
614             Integer depth = Integer.valueOf( param );
615             if( depth < 1 ) {
616                 throw new RestconfDocumentedException( new RestconfError(
617                         ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, "Invalid depth parameter: " + depth,
618                         null, "The depth parameter must be an integer > 1 or \"unbounded\"" ) );
619             }
620
621             return depth;
622         }
623         catch( NumberFormatException e ) {
624             throw new RestconfDocumentedException( new RestconfError(
625                     ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
626                     "Invalid depth parameter: " + e.getMessage(),
627                     null, "The depth parameter must be an integer > 1 or \"unbounded\"" ) );
628         }
629     }
630
631     @Override
632     public StructuredData readOperationalData(final String identifier, UriInfo info) {
633         final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
634         CompositeNode data = null;
635         MountInstance mountPoint = iiWithData.getMountPoint();
636         if (mountPoint != null) {
637             data = broker.readOperationalDataBehindMountPoint(mountPoint, iiWithData.getInstanceIdentifier());
638         }
639         else {
640             data = broker.readOperationalData(iiWithData.getInstanceIdentifier());
641         }
642
643         data = pruneDataAtDepth( data, parseDepthParameter( info ) );
644         return new StructuredData(data, iiWithData.getSchemaNode(), mountPoint);
645     }
646
647     @Override
648     public Response updateConfigurationData(final String identifier, final CompositeNode payload) {
649         final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
650
651         validateInput(iiWithData.getSchemaNode(), payload);
652
653         MountInstance mountPoint = iiWithData.getMountPoint();
654         final CompositeNode value = this.normalizeNode(payload, iiWithData.getSchemaNode(), mountPoint);
655         RpcResult<TransactionStatus> status = null;
656
657         try {
658             if (mountPoint != null) {
659                 status = broker.commitConfigurationDataPutBehindMountPoint(
660                                                 mountPoint, iiWithData.getInstanceIdentifier(), value).get();
661             } else {
662                 status = broker.commitConfigurationDataPut(iiWithData.getInstanceIdentifier(), value).get();
663             }
664         }
665         catch( Exception e ) {
666             throw new RestconfDocumentedException( "Error updating data", e );
667         }
668
669         if( status.getResult() == TransactionStatus.COMMITED )
670             return Response.status(Status.OK).build();
671
672         return Response.status(Status.INTERNAL_SERVER_ERROR).build();
673     }
674
675     @Override
676     public Response createConfigurationData(final String identifier, final CompositeNode payload) {
677         if( payload == null ) {
678             throw new RestconfDocumentedException( "Input is required.",
679                     ErrorType.PROTOCOL,
680                     ErrorTag.MALFORMED_MESSAGE );
681         }
682
683         URI payloadNS = this.namespace(payload);
684         if (payloadNS == null) {
685             throw new RestconfDocumentedException(
686                  "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
687                  ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE );
688         }
689
690         InstanceIdWithSchemaNode iiWithData = null;
691         CompositeNode value = null;
692         if (this.representsMountPointRootData(payload)) {
693              // payload represents mount point data and URI represents path to the mount point
694
695             if (this.endsWithMountPoint(identifier)) {
696                 throw new RestconfDocumentedException(
697                         "URI has bad format. URI should be without \"" + ControllerContext.MOUNT +
698                         "\" for POST operation.",
699                         ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
700             }
701
702             final String completeIdentifier = this.addMountPointIdentifier(identifier);
703             iiWithData = this.controllerContext.toInstanceIdentifier(completeIdentifier);
704
705             value = this.normalizeNode(payload, iiWithData.getSchemaNode(), iiWithData.getMountPoint());
706         }
707         else {
708             final InstanceIdWithSchemaNode incompleteInstIdWithData =
709                                                this.controllerContext.toInstanceIdentifier(identifier);
710             final DataNodeContainer parentSchema = (DataNodeContainer) incompleteInstIdWithData.getSchemaNode();
711             MountInstance mountPoint = incompleteInstIdWithData.getMountPoint();
712             final Module module = this.findModule(mountPoint, payload);
713             if (module == null) {
714                 throw new RestconfDocumentedException(
715                         "Module was not found for \"" + payloadNS + "\"",
716                         ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
717             }
718
719             String payloadName = this.getName(payload);
720             final DataSchemaNode schemaNode = this.controllerContext.findInstanceDataChildByNameAndNamespace(
721                                                               parentSchema, payloadName, module.getNamespace());
722             value = this.normalizeNode(payload, schemaNode, mountPoint);
723
724             iiWithData = this.addLastIdentifierFromData(incompleteInstIdWithData, value, schemaNode);
725         }
726
727         RpcResult<TransactionStatus> status = null;
728         MountInstance mountPoint = iiWithData.getMountPoint();
729         try {
730             if (mountPoint != null) {
731                 Future<RpcResult<TransactionStatus>> future =
732                                           broker.commitConfigurationDataPostBehindMountPoint(
733                                                        mountPoint, iiWithData.getInstanceIdentifier(), value);
734                 status = future == null ? null : future.get();
735             }
736             else {
737                 Future<RpcResult<TransactionStatus>> future =
738                                broker.commitConfigurationDataPost(iiWithData.getInstanceIdentifier(), value);
739                 status = future == null ? null : future.get();
740             }
741         }
742         catch( Exception e ) {
743             throw new RestconfDocumentedException( "Error creating data", e );
744         }
745
746         if (status == null) {
747             return Response.status(Status.ACCEPTED).build();
748         }
749
750         if( status.getResult() == TransactionStatus.COMMITED )
751             return Response.status(Status.NO_CONTENT).build();
752
753         return Response.status(Status.INTERNAL_SERVER_ERROR).build();
754     }
755
756     @Override
757     public Response createConfigurationData(final CompositeNode payload) {
758         if( payload == null ) {
759             throw new RestconfDocumentedException( "Input is required.",
760                     ErrorType.PROTOCOL,
761                     ErrorTag.MALFORMED_MESSAGE );
762         }
763
764         URI payloadNS = this.namespace(payload);
765         if (payloadNS == null) {
766             throw new RestconfDocumentedException(
767                 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
768                 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE );
769         }
770
771         final Module module = this.findModule(null, payload);
772         if (module == null) {
773             throw new RestconfDocumentedException(
774                     "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)",
775                     ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE );
776         }
777
778         String payloadName = this.getName(payload);
779         final DataSchemaNode schemaNode = this.controllerContext.findInstanceDataChildByNameAndNamespace(
780                                                                    module, payloadName, module.getNamespace());
781         final CompositeNode value = this.normalizeNode(payload, schemaNode, null);
782         final InstanceIdWithSchemaNode iiWithData = this.addLastIdentifierFromData(null, value, schemaNode);
783         RpcResult<TransactionStatus> status = null;
784         MountInstance mountPoint = iiWithData.getMountPoint();
785
786         try {
787             if (mountPoint != null) {
788                 Future<RpcResult<TransactionStatus>> future =
789                                              broker.commitConfigurationDataPostBehindMountPoint(
790                                                           mountPoint, iiWithData.getInstanceIdentifier(), value);
791                 status = future == null ? null : future.get();
792             }
793             else {
794                 Future<RpcResult<TransactionStatus>> future =
795                                  broker.commitConfigurationDataPost(iiWithData.getInstanceIdentifier(), value);
796                 status = future == null ? null : future.get();
797             }
798         }
799         catch( Exception e ) {
800             throw new RestconfDocumentedException( "Error creating data", e );
801         }
802
803         if (status == null) {
804             return Response.status(Status.ACCEPTED).build();
805         }
806
807         if( status.getResult() == TransactionStatus.COMMITED )
808             return Response.status(Status.NO_CONTENT).build();
809
810         return Response.status(Status.INTERNAL_SERVER_ERROR).build();
811     }
812
813     @Override
814     public Response deleteConfigurationData(final String identifier) {
815         final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
816         RpcResult<TransactionStatus> status = null;
817         MountInstance mountPoint = iiWithData.getMountPoint();
818
819         try {
820             if (mountPoint != null) {
821                 status = broker.commitConfigurationDataDeleteBehindMountPoint(
822                                         mountPoint, iiWithData.getInstanceIdentifier()).get();
823             }
824             else {
825                 status = broker.commitConfigurationDataDelete(iiWithData.getInstanceIdentifier()).get();
826             }
827         }
828         catch( Exception e ) {
829             throw new RestconfDocumentedException( "Error creating data", e );
830         }
831
832         if( status.getResult() == TransactionStatus.COMMITED )
833             return Response.status(Status.OK).build();
834
835         return Response.status(Status.INTERNAL_SERVER_ERROR).build();
836     }
837
838     @Override
839     public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
840         final String streamName = Notificator.createStreamNameFromUri(identifier);
841         if (Strings.isNullOrEmpty(streamName)) {
842             throw new RestconfDocumentedException(
843                     "Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
844         }
845
846         final ListenerAdapter listener = Notificator.getListenerFor(streamName);
847         if (listener == null) {
848             throw new RestconfDocumentedException(
849                     "Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
850         }
851
852         broker.registerToListenDataChanges(listener);
853
854         final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
855         UriBuilder port = uriBuilder.port(WebSocketServer.PORT);
856         final URI uriToWebsocketServer = port.replacePath(streamName).build();
857
858         return Response.status(Status.OK).location(uriToWebsocketServer).build();
859     }
860
861     private Module findModule(final MountInstance mountPoint, final CompositeNode data) {
862         if (data instanceof CompositeNodeWrapper) {
863             return findModule(mountPoint, (CompositeNodeWrapper)data);
864         }
865         else if (data != null) {
866             URI namespace = data.getNodeType().getNamespace();
867             if (mountPoint != null) {
868                 return this.controllerContext.findModuleByNamespace(mountPoint, namespace);
869             }
870             else {
871                 return this.controllerContext.findModuleByNamespace(namespace);
872             }
873         }
874         else {
875             throw new IllegalArgumentException("Unhandled parameter types: " +
876                     Arrays.<Object>asList(mountPoint, data).toString());
877         }
878     }
879
880     private Module findModule(final MountInstance mountPoint, final CompositeNodeWrapper data) {
881         URI namespace = data.getNamespace();
882         Preconditions.<URI>checkNotNull(namespace);
883
884         Module module = null;
885         if (mountPoint != null) {
886             module = this.controllerContext.findModuleByNamespace(mountPoint, namespace);
887             if (module == null) {
888                 module = this.controllerContext.findModuleByName(mountPoint, namespace.toString());
889             }
890         }
891         else {
892             module = this.controllerContext.findModuleByNamespace(namespace);
893             if (module == null) {
894                 module = this.controllerContext.findModuleByName(namespace.toString());
895             }
896         }
897
898         return module;
899     }
900
901     private InstanceIdWithSchemaNode addLastIdentifierFromData(
902                                               final InstanceIdWithSchemaNode identifierWithSchemaNode,
903                                               final CompositeNode data, final DataSchemaNode schemaOfData) {
904         InstanceIdentifier instanceIdentifier = null;
905         if (identifierWithSchemaNode != null) {
906             instanceIdentifier = identifierWithSchemaNode.getInstanceIdentifier();
907         }
908
909         final InstanceIdentifier iiOriginal = instanceIdentifier;
910         InstanceIdentifierBuilder iiBuilder = null;
911         if (iiOriginal == null) {
912             iiBuilder = InstanceIdentifier.builder();
913         }
914         else {
915             iiBuilder = InstanceIdentifier.builder(iiOriginal);
916         }
917
918         if ((schemaOfData instanceof ListSchemaNode)) {
919             HashMap<QName,Object> keys = this.resolveKeysFromData(((ListSchemaNode) schemaOfData), data);
920             iiBuilder.nodeWithKey(schemaOfData.getQName(), keys);
921         }
922         else {
923             iiBuilder.node(schemaOfData.getQName());
924         }
925
926         InstanceIdentifier instance = iiBuilder.toInstance();
927         MountInstance mountPoint = null;
928         if (identifierWithSchemaNode != null) {
929             mountPoint=identifierWithSchemaNode.getMountPoint();
930         }
931
932         return new InstanceIdWithSchemaNode(instance, schemaOfData, mountPoint);
933     }
934
935     private HashMap<QName,Object> resolveKeysFromData(final ListSchemaNode listNode,
936                                                       final CompositeNode dataNode) {
937         final HashMap<QName,Object> keyValues = new HashMap<QName, Object>();
938         List<QName> _keyDefinition = listNode.getKeyDefinition();
939         for (final QName key : _keyDefinition) {
940             SimpleNode<? extends Object> head = null;
941             String localName = key.getLocalName();
942             List<SimpleNode<? extends Object>> simpleNodesByName = dataNode.getSimpleNodesByName(localName);
943             if (simpleNodesByName != null) {
944                 head = Iterables.getFirst(simpleNodesByName, null);
945             }
946
947             Object dataNodeKeyValueObject = null;
948             if (head != null) {
949                 dataNodeKeyValueObject = head.getValue();
950             }
951
952             if (dataNodeKeyValueObject == null) {
953                 throw new RestconfDocumentedException(
954                         "Data contains list \"" + dataNode.getNodeType().getLocalName() +
955                         "\" which does not contain key: \"" + key.getLocalName() + "\"",
956                         ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
957             }
958
959             keyValues.put(key, dataNodeKeyValueObject);
960         }
961
962         return keyValues;
963     }
964
965     private boolean endsWithMountPoint(final String identifier) {
966         return identifier.endsWith(ControllerContext.MOUNT) ||
967                identifier.endsWith(ControllerContext.MOUNT + "/");
968     }
969
970     private boolean representsMountPointRootData(final CompositeNode data) {
971         URI namespace = this.namespace(data);
972         return (SchemaContext.NAME.getNamespace().equals( namespace ) /* ||
973                 MOUNT_POINT_MODULE_NAME.equals( namespace.toString() )*/ ) &&
974                 SchemaContext.NAME.getLocalName().equals( this.localName(data) );
975     }
976
977     private String addMountPointIdentifier(final String identifier) {
978         boolean endsWith = identifier.endsWith("/");
979         if (endsWith) {
980             return (identifier + ControllerContext.MOUNT);
981         }
982
983         return identifier + "/" + ControllerContext.MOUNT;
984     }
985
986     private CompositeNode normalizeNode(final CompositeNode node, final DataSchemaNode schema,
987                                         final MountInstance mountPoint) {
988         if (schema == null) {
989             QName nodeType = node == null ? null : node.getNodeType();
990             String localName = nodeType == null ? null : nodeType.getLocalName();
991
992             throw new RestconfDocumentedException(
993                     "Data schema node was not found for " + localName,
994                     ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
995         }
996
997         if (!(schema instanceof DataNodeContainer)) {
998             throw new RestconfDocumentedException(
999                     "Root element has to be container or list yang datatype.",
1000                     ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1001         }
1002
1003         if ((node instanceof CompositeNodeWrapper)) {
1004             boolean isChangeAllowed = ((CompositeNodeWrapper) node).isChangeAllowed();
1005             if (isChangeAllowed) {
1006                 try {
1007                     this.normalizeNode(((CompositeNodeWrapper) node), schema, null, mountPoint);
1008                 }
1009                 catch (IllegalArgumentException e) {
1010                     throw new RestconfDocumentedException(
1011                                     e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1012                 }
1013             }
1014
1015             return ((CompositeNodeWrapper) node).unwrap();
1016         }
1017
1018         return node;
1019     }
1020
1021     private void normalizeNode(final NodeWrapper<? extends Object> nodeBuilder,
1022                                final DataSchemaNode schema, final QName previousAugment,
1023                                final MountInstance mountPoint) {
1024         if (schema == null) {
1025             throw new RestconfDocumentedException(
1026                     "Data has bad format.\n\"" + nodeBuilder.getLocalName() +
1027                     "\" does not exist in yang schema.",
1028                     ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1029         }
1030
1031         QName currentAugment = null;
1032         if (nodeBuilder.getQname() != null) {
1033             currentAugment = previousAugment;
1034         }
1035         else {
1036             currentAugment = this.normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint);
1037             if (nodeBuilder.getQname() == null) {
1038                 throw new RestconfDocumentedException(
1039                         "Data has bad format.\nIf data is in XML format then namespace for \"" +
1040                         nodeBuilder.getLocalName() +
1041                         "\" should be \"" + schema.getQName().getNamespace() + "\".\n" +
1042                         "If data is in JSON format then module name for \"" + nodeBuilder.getLocalName() +
1043                          "\" should be corresponding to namespace \"" +
1044                         schema.getQName().getNamespace() + "\".",
1045                         ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1046             }
1047         }
1048
1049         if ((nodeBuilder instanceof CompositeNodeWrapper)) {
1050             final List<NodeWrapper<?>> children = ((CompositeNodeWrapper) nodeBuilder).getValues();
1051             for (final NodeWrapper<? extends Object> child : children) {
1052                 final List<DataSchemaNode> potentialSchemaNodes =
1053                         this.controllerContext.findInstanceDataChildrenByName(
1054                                              ((DataNodeContainer) schema), child.getLocalName());
1055
1056                 if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) {
1057                     StringBuilder builder = new StringBuilder();
1058                     for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1059                         builder.append("   ").append(potentialSchemaNode.getQName().getNamespace().toString())
1060                                .append("\n");
1061                     }
1062
1063                     throw new RestconfDocumentedException(
1064                                  "Node \"" + child.getLocalName() +
1065                                  "\" is added as augment from more than one module. " +
1066                                  "Therefore node must have namespace (XML format) or module name (JSON format)." +
1067                                  "\nThe node is added as augment from modules with namespaces:\n" + builder,
1068                                  ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1069                 }
1070
1071                 boolean rightNodeSchemaFound = false;
1072                 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1073                     if (!rightNodeSchemaFound) {
1074                         final QName potentialCurrentAugment =
1075                                 this.normalizeNodeName(child, potentialSchemaNode, currentAugment, mountPoint);
1076                         if (child.getQname() != null ) {
1077                             this.normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint);
1078                             rightNodeSchemaFound = true;
1079                         }
1080                     }
1081                 }
1082
1083                 if (!rightNodeSchemaFound) {
1084                     throw new RestconfDocumentedException(
1085                                "Schema node \"" + child.getLocalName() + "\" was not found in module.",
1086                                ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT );
1087                 }
1088             }
1089
1090             if ((schema instanceof ListSchemaNode)) {
1091                 final List<QName> listKeys = ((ListSchemaNode) schema).getKeyDefinition();
1092                 for (final QName listKey : listKeys) {
1093                     boolean foundKey = false;
1094                     for (final NodeWrapper<? extends Object> child : children) {
1095                         if (Objects.equal(child.unwrap().getNodeType().getLocalName(), listKey.getLocalName())) {
1096                             foundKey = true;
1097                         }
1098                     }
1099
1100                     if (!foundKey) {
1101                         throw new RestconfDocumentedException(
1102                                        "Missing key in URI \"" + listKey.getLocalName() +
1103                                        "\" of list \"" + schema.getQName().getLocalName() + "\"",
1104                                        ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1105                     }
1106                 }
1107             }
1108         }
1109         else {
1110             if ((nodeBuilder instanceof SimpleNodeWrapper)) {
1111                 final SimpleNodeWrapper simpleNode = ((SimpleNodeWrapper) nodeBuilder);
1112                 final Object value = simpleNode.getValue();
1113                 Object inputValue = value;
1114                 TypeDefinition<? extends Object> typeDefinition = this.typeDefinition(schema);
1115                 if ((typeDefinition instanceof IdentityrefTypeDefinition)) {
1116                     if ((value instanceof String)) {
1117                         inputValue = new IdentityValuesDTO( nodeBuilder.getNamespace().toString(),
1118                                                             (String) value, null, (String) value );
1119                     } // else value is already instance of IdentityValuesDTO
1120                 }
1121
1122                 Codec<Object,Object> codec = RestCodec.from(typeDefinition, mountPoint);
1123                 Object outputValue = codec == null ? null : codec.deserialize(inputValue);
1124
1125                 simpleNode.setValue(outputValue);
1126             }
1127             else {
1128                 if ((nodeBuilder instanceof EmptyNodeWrapper)) {
1129                     final EmptyNodeWrapper emptyNodeBuilder = ((EmptyNodeWrapper) nodeBuilder);
1130                     if ((schema instanceof LeafSchemaNode)) {
1131                         emptyNodeBuilder.setComposite(false);
1132                     }
1133                     else {
1134                         if ((schema instanceof ContainerSchemaNode)) {
1135                             // FIXME: Add presence check
1136                             emptyNodeBuilder.setComposite(true);
1137                         }
1138                     }
1139                 }
1140             }
1141         }
1142     }
1143
1144     private QName normalizeNodeName(final NodeWrapper<? extends Object> nodeBuilder,
1145                                     final DataSchemaNode schema, final QName previousAugment,
1146                                     final MountInstance mountPoint) {
1147         QName validQName = schema.getQName();
1148         QName currentAugment = previousAugment;
1149         if (schema.isAugmenting()) {
1150             currentAugment = schema.getQName();
1151         }
1152         else if (previousAugment != null &&
1153                  !Objects.equal( schema.getQName().getNamespace(), previousAugment.getNamespace())) {
1154             validQName = QName.create(currentAugment, schema.getQName().getLocalName());
1155         }
1156
1157         String moduleName = null;
1158         if (mountPoint == null) {
1159             moduleName = controllerContext.findModuleNameByNamespace(validQName.getNamespace());
1160         }
1161         else {
1162             moduleName = controllerContext.findModuleNameByNamespace(mountPoint, validQName.getNamespace());
1163         }
1164
1165         if (nodeBuilder.getNamespace() == null ||
1166             Objects.equal(nodeBuilder.getNamespace(), validQName.getNamespace()) ||
1167             Objects.equal(nodeBuilder.getNamespace().toString(), moduleName) /*||
1168             Note: this check is wrong - can never be true as it compares a URI with a String
1169                   not sure what the intention is so commented out...
1170             Objects.equal(nodeBuilder.getNamespace(), MOUNT_POINT_MODULE_NAME)*/ ) {
1171
1172             nodeBuilder.setQname(validQName);
1173         }
1174
1175         return currentAugment;
1176     }
1177
1178     private URI namespace(final CompositeNode data) {
1179         if (data instanceof CompositeNodeWrapper) {
1180             return ((CompositeNodeWrapper)data).getNamespace();
1181         }
1182         else if (data != null) {
1183             return data.getNodeType().getNamespace();
1184         }
1185         else {
1186             throw new IllegalArgumentException("Unhandled parameter types: " +
1187                     Arrays.<Object>asList(data).toString());
1188         }
1189     }
1190
1191     private String localName(final CompositeNode data) {
1192         if (data instanceof CompositeNodeWrapper) {
1193             return ((CompositeNodeWrapper)data).getLocalName();
1194         }
1195         else if (data != null) {
1196             return data.getNodeType().getLocalName();
1197         }
1198         else {
1199             throw new IllegalArgumentException("Unhandled parameter types: " +
1200                     Arrays.<Object>asList(data).toString());
1201         }
1202     }
1203
1204     private String getName(final CompositeNode data) {
1205         if (data instanceof CompositeNodeWrapper) {
1206             return ((CompositeNodeWrapper)data).getLocalName();
1207         }
1208         else if (data != null) {
1209             return data.getNodeType().getLocalName();
1210         }
1211         else {
1212             throw new IllegalArgumentException("Unhandled parameter types: " +
1213                     Arrays.<Object>asList(data).toString());
1214         }
1215     }
1216
1217     private TypeDefinition<? extends Object> _typeDefinition(final LeafSchemaNode node) {
1218         TypeDefinition<?> baseType = node.getType();
1219         while (baseType.getBaseType() != null) {
1220             baseType = baseType.getBaseType();
1221         }
1222
1223         return baseType;
1224     }
1225
1226     private TypeDefinition<? extends Object> typeDefinition(final LeafListSchemaNode node) {
1227         TypeDefinition<?> baseType = node.getType();
1228         while (baseType.getBaseType() != null) {
1229             baseType = baseType.getBaseType();
1230         }
1231
1232         return baseType;
1233     }
1234
1235     private TypeDefinition<? extends Object> typeDefinition(final DataSchemaNode node) {
1236         if (node instanceof LeafListSchemaNode) {
1237             return typeDefinition((LeafListSchemaNode)node);
1238         }
1239         else if (node instanceof LeafSchemaNode) {
1240             return _typeDefinition((LeafSchemaNode)node);
1241         }
1242         else {
1243             throw new IllegalArgumentException("Unhandled parameter types: " +
1244                     Arrays.<Object>asList(node).toString());
1245         }
1246     }
1247 }