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