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