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