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