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