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