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