Merge "Add filtering capability to config.ini in order to reference logging bridge...
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / main / java / org / opendaylight / controller / sal / restconf / impl / RestconfImpl.xtend
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.sal.restconf.impl
9
10 import com.google.common.base.Preconditions
11 import com.google.common.base.Splitter
12 import com.google.common.collect.Lists
13 import java.net.URI
14 import java.text.ParseException
15 import java.text.SimpleDateFormat
16 import java.util.ArrayList
17 import java.util.HashMap
18 import java.util.List
19 import java.util.Set
20 import javax.ws.rs.core.Response
21 import javax.ws.rs.core.UriInfo
22 import org.opendaylight.controller.md.sal.common.api.TransactionStatus
23 import org.opendaylight.controller.sal.core.api.mount.MountInstance
24 import org.opendaylight.controller.sal.rest.api.RestconfService
25 import org.opendaylight.controller.sal.streams.listeners.Notificator
26 import org.opendaylight.controller.sal.streams.websockets.WebSocketServer
27 import org.opendaylight.yangtools.yang.common.QName
28 import org.opendaylight.yangtools.yang.common.RpcResult
29 import org.opendaylight.yangtools.yang.data.api.CompositeNode
30 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
31 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder
32 import org.opendaylight.yangtools.yang.data.api.Node
33 import org.opendaylight.yangtools.yang.data.impl.NodeFactory
34 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
35 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
36 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
37 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
38 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
39 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
40 import org.opendaylight.yangtools.yang.model.api.Module
41 import org.opendaylight.yangtools.yang.model.api.RpcDefinition
42 import org.opendaylight.yangtools.yang.model.api.SchemaContext
43 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
44 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition
45 import org.opendaylight.yangtools.yang.model.util.EmptyType
46 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder
47 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder
48
49 import static javax.ws.rs.core.Response.Status.*
50
51 class RestconfImpl implements RestconfService {
52
53     val static RestconfImpl INSTANCE = new RestconfImpl
54     val static MOUNT_POINT_MODULE_NAME = "ietf-netconf"
55     val static REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd")
56     val static RESTCONF_MODULE_DRAFT02_REVISION = "2013-10-19"
57     val static RESTCONF_MODULE_DRAFT02_NAME = "ietf-restconf"
58     val static RESTCONF_MODULE_DRAFT02_NAMESPACE = "urn:ietf:params:xml:ns:yang:ietf-restconf"
59     val static RESTCONF_MODULE_DRAFT02_RESTCONF_GROUPING_SCHEMA_NODE = "restconf"
60     val static RESTCONF_MODULE_DRAFT02_RESTCONF_CONTAINER_SCHEMA_NODE = "restconf"
61     val static RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE = "modules"
62     val static RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE = "module"
63     val static RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE = "streams"
64     val static RESTCONF_MODULE_DRAFT02_STREAM_LIST_SCHEMA_NODE = "stream"
65     val static RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE = "operations"
66     val static SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote"
67     val static SAL_REMOTE_RPC_SUBSRCIBE = "create-data-change-event-subscription"
68
69     @Property
70     BrokerFacade broker
71
72     @Property
73     extension ControllerContext controllerContext
74
75     private new() {
76         if (INSTANCE !== null) {
77             throw new IllegalStateException("Already instantiated");
78         }
79     }
80
81     static def getInstance() {
82         return INSTANCE
83     }
84
85     override getModules() {
86         val restconfModule = getRestconfModule()
87         val List<Node<?>> modulesAsData = new ArrayList
88         val moduleSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE)
89         for (module : allModules) {
90             modulesAsData.add(module.toModuleCompositeNode(moduleSchemaNode))
91         }
92         val modulesSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE)
93         val modulesNode = NodeFactory.createImmutableCompositeNode(modulesSchemaNode.QName, null, modulesAsData)
94         return new StructuredData(modulesNode, modulesSchemaNode, null)
95     }
96
97     override getAvailableStreams(){
98         var Set<String> availableStreams = Notificator.getStreamNames();
99         val List<Node<?>> streamsAsData = new ArrayList
100         val streamSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_STREAM_LIST_SCHEMA_NODE)
101         for (String streamName:availableStreams){
102             streamsAsData.add(streamName.toStreamCompositeNode(streamSchemaNode))
103         }
104         val streamsSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE)
105         val streamsNode = NodeFactory.createImmutableCompositeNode(streamsSchemaNode.QName, null, streamsAsData)
106         return new StructuredData(streamsNode, streamsSchemaNode, null)
107     }
108     override getModules(String identifier) {
109         var Set<Module> modules = null
110         var MountInstance mountPoint = null
111         if (identifier.contains(ControllerContext.MOUNT)) {
112             mountPoint = identifier.toMountPointIdentifier.mountPoint
113             modules = mountPoint.allModules
114         } else {
115             throw new ResponseException(BAD_REQUEST, "URI has bad format. If modules behind mount point should be showed, URI has to end with " + ControllerContext.MOUNT)
116         }
117         val List<Node<?>> modulesAsData = new ArrayList
118         val moduleSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE)
119         for (module : modules) {
120             modulesAsData.add(module.toModuleCompositeNode(moduleSchemaNode))
121         }
122         val modulesSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE)
123         val modulesNode = NodeFactory.createImmutableCompositeNode(modulesSchemaNode.QName, null, modulesAsData)
124         return new StructuredData(modulesNode, modulesSchemaNode, mountPoint)
125     }
126
127     override getModule(String identifier) {
128         val moduleNameAndRevision = identifier.moduleNameAndRevision
129         var Module module = null
130         var MountInstance mountPoint = null
131         if (identifier.contains(ControllerContext.MOUNT)) {
132             mountPoint = identifier.toMountPointIdentifier.mountPoint
133             module = mountPoint.findModuleByNameAndRevision(moduleNameAndRevision)
134         } else {
135             module = findModuleByNameAndRevision(moduleNameAndRevision)
136         }
137         if (module === null) {
138             throw new ResponseException(BAD_REQUEST,
139                 "Module with name '" + moduleNameAndRevision.localName + "' and revision '" +
140                     moduleNameAndRevision.revision + "' was not found.")
141         }
142         val moduleSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE)
143         val moduleNode = module.toModuleCompositeNode(moduleSchemaNode)
144         return new StructuredData(moduleNode, moduleSchemaNode, mountPoint)
145     }
146
147     override getOperations() {
148         return operationsFromModulesToStructuredData(allModules,null)
149     }
150     
151     override getOperations(String identifier) {
152         var Set<Module> modules = null
153         var MountInstance mountPoint = null
154         if (identifier.contains(ControllerContext.MOUNT)) {
155             mountPoint = identifier.toMountPointIdentifier.mountPoint
156             modules = mountPoint.allModules
157         } else {
158             throw new ResponseException(BAD_REQUEST, "URI has bad format. If operations behind mount point should be showed, URI has to end with " + ControllerContext.MOUNT)
159         }
160         return operationsFromModulesToStructuredData(modules,mountPoint)
161     }
162     
163     private def StructuredData operationsFromModulesToStructuredData(Set<Module> modules,MountInstance mountPoint) {
164         val List<Node<?>> operationsAsData = new ArrayList
165         val operationsSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE)        
166         val fakeOperationsSchemaNode = new ContainerSchemaNodeBuilder(RESTCONF_MODULE_DRAFT02_NAME, 0, operationsSchemaNode.QName, operationsSchemaNode.path)
167         for (module : modules) {
168             for (rpc : module.rpcs) {
169                 operationsAsData.add(NodeFactory.createImmutableSimpleNode(rpc.QName, null, null))
170                 val fakeRpcSchemaNode = new LeafSchemaNodeBuilder(module.name, 0, rpc.QName, null)
171                 fakeRpcSchemaNode.setAugmenting(true)
172                 fakeRpcSchemaNode.setType(EmptyType.instance)
173                 fakeOperationsSchemaNode.addChildNode(fakeRpcSchemaNode.build)                
174             }
175         }
176         val operationsNode = NodeFactory.createImmutableCompositeNode(operationsSchemaNode.QName, null, operationsAsData)
177         return new StructuredData(operationsNode, fakeOperationsSchemaNode.build, mountPoint)        
178     }
179
180     private def Module getRestconfModule() {
181         val restconfModule = findModuleByNameAndRevision(
182             QName.create(RESTCONF_MODULE_DRAFT02_NAMESPACE, RESTCONF_MODULE_DRAFT02_REVISION,
183                 RESTCONF_MODULE_DRAFT02_NAME))
184         if (restconfModule === null) {
185             throw new ResponseException(INTERNAL_SERVER_ERROR, "Restconf module was not found.")
186         }
187         return restconfModule
188     }
189
190     private def QName getModuleNameAndRevision(String identifier) {
191         val indexOfMountPointFirstLetter = identifier.indexOf(ControllerContext.MOUNT)
192         var moduleNameAndRevision = "";
193         if (indexOfMountPointFirstLetter !== -1) { // module and revision is behind mount point string
194             moduleNameAndRevision = identifier.substring(indexOfMountPointFirstLetter + ControllerContext.MOUNT.length)
195         } else (
196             moduleNameAndRevision = identifier
197         )
198         val pathArgs = Lists.newArrayList(Splitter.on("/").omitEmptyStrings.split(moduleNameAndRevision))
199         if (pathArgs.length < 2) {
200             throw new ResponseException(BAD_REQUEST,
201                 "URI has bad format. End of URI should be in format 'moduleName/yyyy-MM-dd'")
202         }
203         try {
204             val moduleName = pathArgs.head
205             val moduleRevision = REVISION_FORMAT.parse(pathArgs.get(1))
206             return QName.create(null, moduleRevision, moduleName)
207         } catch(ParseException e) {
208             throw new ResponseException(BAD_REQUEST, "URI has bad format. It should be 'moduleName/yyyy-MM-dd'")
209         }
210     }
211
212     private def CompositeNode toStreamCompositeNode(String streamName, DataSchemaNode streamSchemaNode) {
213         val List<Node<?>> streamNodeValues = new ArrayList
214         val nameSchemaNode = (streamSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("name").head
215         streamNodeValues.add(NodeFactory.createImmutableSimpleNode(nameSchemaNode.QName, null, streamName))
216
217         val descriptionSchemaNode = (streamSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("description").head
218         streamNodeValues.add(NodeFactory.createImmutableSimpleNode(descriptionSchemaNode.QName, null, "DESCRIPTION_PLACEHOLDER"))
219
220         val replaySupportSchemaNode = (streamSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("replay-support").head
221         streamNodeValues.add(NodeFactory.createImmutableSimpleNode(replaySupportSchemaNode.QName, null, true))
222
223         val replayLogCreationTimeSchemaNode = (streamSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("replay-log-creation-time").head
224         streamNodeValues.add(NodeFactory.createImmutableSimpleNode(replayLogCreationTimeSchemaNode.QName, null, ""))
225
226         val eventsSchemaNode = (streamSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("events").head
227         streamNodeValues.add(NodeFactory.createImmutableSimpleNode(eventsSchemaNode.QName, null, ""))
228
229         return NodeFactory.createImmutableCompositeNode(streamSchemaNode.QName, null, streamNodeValues)
230     }
231     private def CompositeNode toModuleCompositeNode(Module module, DataSchemaNode moduleSchemaNode) {
232         val List<Node<?>> moduleNodeValues = new ArrayList
233         val nameSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("name").head
234         moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(nameSchemaNode.QName, null, module.name))
235         val revisionSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("revision").head
236         moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(revisionSchemaNode.QName, null, REVISION_FORMAT.format(module.revision)))
237         val namespaceSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("namespace").head
238         moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(namespaceSchemaNode.QName, null, module.namespace.toString))
239         val featureSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("feature").head
240         for (feature : module.features) {
241             moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(featureSchemaNode.QName, null, feature.QName.localName))
242         }
243         return NodeFactory.createImmutableCompositeNode(moduleSchemaNode.QName, null, moduleNodeValues)
244     }
245
246     private def DataSchemaNode getSchemaNode(Module restconfModule, String schemaNodeName) {
247         val restconfGrouping = restconfModule.groupings.filter[g|g.QName.localName == RESTCONF_MODULE_DRAFT02_RESTCONF_GROUPING_SCHEMA_NODE].head
248         val restconfContainer = restconfGrouping.findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_RESTCONF_CONTAINER_SCHEMA_NODE).head
249         if (schemaNodeName == RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE) {
250             return (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE).head
251         } else if (schemaNodeName == RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE) {
252            return (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE).head
253         } else if (schemaNodeName == RESTCONF_MODULE_DRAFT02_STREAM_LIST_SCHEMA_NODE) {
254            val modules = (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE).head
255            return (modules as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_STREAM_LIST_SCHEMA_NODE).head
256         }else if (schemaNodeName == RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE) {
257             return (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE).head
258         } else if (schemaNodeName == RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE) {
259             val modules = (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE).head
260             return (modules as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE).head
261         }
262         return null
263     }
264
265     override getRoot() {
266         return null;
267     }
268
269     override invokeRpc(String identifier, CompositeNode payload) {
270         val rpc = resolveIdentifierInInvokeRpc(identifier)
271         if (rpc.QName.namespace.toString == SAL_REMOTE_NAMESPACE && rpc.QName.localName == SAL_REMOTE_RPC_SUBSRCIBE) {
272             val value = normalizeNode(payload, rpc.input, null)
273             val pathNode = value?.getFirstSimpleByName(QName.create(rpc.QName, "path"))
274             val pathValue = pathNode?.value
275             if (pathValue === null && !(pathValue instanceof InstanceIdentifier)) {
276                 throw new ResponseException(INTERNAL_SERVER_ERROR, "Instance identifier was not normalized correctly.");
277             }
278             val pathIdentifier = (pathValue as InstanceIdentifier)
279             var String streamName = null
280             if (!pathIdentifier.path.nullOrEmpty) {
281                 streamName = Notificator.createStreamNameFromUri(pathIdentifier.toFullRestconfIdentifier)
282             }
283             if (streamName.nullOrEmpty) {
284                 throw new ResponseException(BAD_REQUEST, "Path is empty or contains data node which is not Container or List build-in type.");
285             }
286             val streamNameNode = NodeFactory.createImmutableSimpleNode(QName.create(rpc.output.QName, "stream-name"), null, streamName)
287             val List<Node<?>> output = new ArrayList
288             output.add(streamNameNode)
289             val responseData = NodeFactory.createMutableCompositeNode(rpc.output.QName, null, output, null, null)
290
291             if (!Notificator.existListenerFor(pathIdentifier)) {
292                 Notificator.createListener(pathIdentifier, streamName)
293             }
294
295             return new StructuredData(responseData, rpc.output, null)
296         }
297         return callRpc(identifier.rpcDefinition, payload)
298     }
299
300     override invokeRpc(String identifier, String noPayload) {
301         if (!noPayload.nullOrEmpty) {
302             throw new ResponseException(UNSUPPORTED_MEDIA_TYPE, "Content-Type contains unsupported Media Type.");
303         }
304         val rpc = resolveIdentifierInInvokeRpc(identifier)
305         return callRpc(rpc, null)
306     }
307
308     private def resolveIdentifierInInvokeRpc(String identifier) {
309         if (identifier.indexOf("/") === -1) {
310             val identifierDecoded = identifier.urlPathArgDecode
311             val rpc = identifierDecoded.rpcDefinition
312             if (rpc !== null) {
313                 return rpc
314             }
315             throw new ResponseException(NOT_FOUND, "RPC does not exist.");
316         }
317         val slashErrorMsg = String.format(
318             "Identifier %n%s%ncan't contain slash character (/).%nIf slash is part of identifier name then use %%2F placeholder.", identifier)
319         throw new ResponseException(NOT_FOUND, slashErrorMsg);
320     }
321
322     private def StructuredData callRpc(RpcDefinition rpc, CompositeNode payload) {
323         if (rpc === null) {
324             throw new ResponseException(NOT_FOUND, "RPC does not exist.");
325         }
326         var CompositeNode rpcRequest;
327         if (payload === null) {
328             rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, null, null, null)
329         } else {
330             val value = normalizeNode(payload, rpc.input, null)
331             val List<Node<?>> input = new ArrayList
332             input.add(value)
333             rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, input, null, null)
334         }
335         val rpcResult = broker.invokeRpc(rpc.QName, rpcRequest);
336         if (!rpcResult.successful) {
337             throw new ResponseException(INTERNAL_SERVER_ERROR, "Operation failed")
338         }
339         if (rpcResult.result === null) {
340             return null
341         }
342         return new StructuredData(rpcResult.result, rpc.output, null)
343     }
344
345     override readConfigurationData(String identifier) {
346         val iiWithData = identifier.toInstanceIdentifier
347         var CompositeNode data = null;
348         if (iiWithData.mountPoint !== null) {
349             data = broker.readConfigurationDataBehindMountPoint(iiWithData.mountPoint, iiWithData.getInstanceIdentifier)
350         } else {
351             data = broker.readConfigurationData(iiWithData.getInstanceIdentifier);
352         }
353         return new StructuredData(data, iiWithData.schemaNode, iiWithData.mountPoint)
354     }
355
356     override readOperationalData(String identifier) {
357         val iiWithData = identifier.toInstanceIdentifier
358         var CompositeNode data = null;
359         if (iiWithData.mountPoint !== null) {
360             data = broker.readOperationalDataBehindMountPoint(iiWithData.mountPoint, iiWithData.getInstanceIdentifier)
361         } else {
362             data = broker.readOperationalData(iiWithData.getInstanceIdentifier);
363         }
364         return new StructuredData(data, iiWithData.schemaNode, iiWithData.mountPoint)
365     }
366
367     override updateConfigurationData(String identifier, CompositeNode payload) {
368         val iiWithData = identifier.toInstanceIdentifier
369         val value = normalizeNode(payload, iiWithData.schemaNode, iiWithData.mountPoint)
370         var RpcResult<TransactionStatus> status = null
371         if (iiWithData.mountPoint !== null) {
372             status = broker.commitConfigurationDataPutBehindMountPoint(iiWithData.mountPoint,
373                 iiWithData.instanceIdentifier, value).get()
374         } else {
375             status = broker.commitConfigurationDataPut(iiWithData.instanceIdentifier, value).get();
376         }
377         switch status.result {
378             case TransactionStatus.COMMITED: Response.status(OK).build
379             default: Response.status(INTERNAL_SERVER_ERROR).build
380         }
381     }
382
383     override createConfigurationData(String identifier, CompositeNode payload) {
384         if (payload.namespace === null) {
385             throw new ResponseException(BAD_REQUEST,
386                 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
387         }
388         var InstanceIdWithSchemaNode iiWithData;
389         var CompositeNode value;
390         if (payload.representsMountPointRootData) { // payload represents mount point data and URI represents path to the mount point
391             if (identifier.endsWithMountPoint) {
392                 throw new ResponseException(BAD_REQUEST,
393                     "URI has bad format. URI should be without \"" + ControllerContext.MOUNT + "\" for POST operation.");
394             }
395             val completIdentifier = identifier.addMountPointIdentifier
396             iiWithData = completIdentifier.toInstanceIdentifier
397             value = normalizeNode(payload, iiWithData.schemaNode, iiWithData.mountPoint)
398         } else {
399             val uncompleteInstIdWithData = identifier.toInstanceIdentifier
400             val parentSchema = uncompleteInstIdWithData.schemaNode as DataNodeContainer
401             val module = uncompleteInstIdWithData.mountPoint.findModule(payload)
402             if (module === null) {
403                 throw new ResponseException(BAD_REQUEST, "Module was not found for \"" + payload.namespace + "\"")
404             }
405             val schemaNode = parentSchema.findInstanceDataChildByNameAndNamespace(payload.name, module.namespace)
406             value = normalizeNode(payload, schemaNode, uncompleteInstIdWithData.mountPoint)
407             iiWithData = uncompleteInstIdWithData.addLastIdentifierFromData(value, schemaNode)
408         }
409         var RpcResult<TransactionStatus> status = null
410         if (iiWithData.mountPoint !== null) {
411             status = broker.commitConfigurationDataPostBehindMountPoint(iiWithData.mountPoint,
412                 iiWithData.instanceIdentifier, value)?.get();
413         } else {
414             status = broker.commitConfigurationDataPost(iiWithData.instanceIdentifier, value)?.get();
415         }
416         if (status === null) {
417             return Response.status(ACCEPTED).build
418         }
419         switch status.result {
420             case TransactionStatus.COMMITED: Response.status(NO_CONTENT).build
421             default: Response.status(INTERNAL_SERVER_ERROR).build
422         }
423     }
424
425     override createConfigurationData(CompositeNode payload) {
426         if (payload.namespace === null) {
427             throw new ResponseException(BAD_REQUEST,
428                 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
429         }
430         val module = findModule(null, payload)
431         if (module === null) {
432             throw new ResponseException(BAD_REQUEST,
433                 "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)");
434         }
435         val schemaNode = module.findInstanceDataChildByNameAndNamespace(payload.name, module.namespace)
436         val value = normalizeNode(payload, schemaNode, null)
437         val iiWithData = addLastIdentifierFromData(null, value, schemaNode)
438         var RpcResult<TransactionStatus> status = null
439         if (iiWithData.mountPoint !== null) {
440             status = broker.commitConfigurationDataPostBehindMountPoint(iiWithData.mountPoint,
441                 iiWithData.instanceIdentifier, value)?.get();
442         } else {
443             status = broker.commitConfigurationDataPost(iiWithData.instanceIdentifier, value)?.get();
444         }
445         if (status === null) {
446             return Response.status(ACCEPTED).build
447         }
448         switch status.result {
449             case TransactionStatus.COMMITED: Response.status(NO_CONTENT).build
450             default: Response.status(INTERNAL_SERVER_ERROR).build
451         }
452     }
453
454     override deleteConfigurationData(String identifier) {
455         val iiWithData = identifier.toInstanceIdentifier
456         var RpcResult<TransactionStatus> status = null
457         if (iiWithData.mountPoint !== null) {
458             status = broker.commitConfigurationDataDeleteBehindMountPoint(iiWithData.mountPoint,
459                 iiWithData.getInstanceIdentifier).get;
460         } else {
461             status = broker.commitConfigurationDataDelete(iiWithData.getInstanceIdentifier).get;
462         }
463         switch status.result {
464             case TransactionStatus.COMMITED: Response.status(OK).build
465             default: Response.status(INTERNAL_SERVER_ERROR).build
466         }
467     }
468
469     override subscribeToStream(String identifier, UriInfo uriInfo) {
470         val streamName = Notificator.createStreamNameFromUri(identifier)
471         if (streamName.nullOrEmpty) {
472             throw new ResponseException(BAD_REQUEST, "Stream name is empty.")
473         }
474         val listener = Notificator.getListenerFor(streamName);
475         if (listener === null) {
476             throw new ResponseException(BAD_REQUEST, "Stream was not found.")
477         }
478         broker.registerToListenDataChanges(listener)
479         val uriBuilder = uriInfo.getAbsolutePathBuilder()
480         val uriToWebsocketServer = uriBuilder.port(WebSocketServer.PORT).replacePath(streamName).build()
481         return Response.status(OK).location(uriToWebsocketServer).build
482     }
483
484     private def dispatch URI namespace(CompositeNode data) {
485         return data.nodeType.namespace
486     }
487
488     private def dispatch URI namespace(CompositeNodeWrapper data) {
489         return data.namespace
490     }
491
492     private def dispatch String localName(CompositeNode data) {
493         return data.nodeType.localName
494     }
495
496     private def dispatch String localName(CompositeNodeWrapper data) {
497         return data.localName
498     }
499
500     private def dispatch Module findModule(MountInstance mountPoint, CompositeNode data) {
501         if (mountPoint !== null) {
502             return mountPoint.findModuleByNamespace(data.nodeType.namespace)
503         } else {
504             return findModuleByNamespace(data.nodeType.namespace)
505         }
506     }
507
508     private def dispatch Module findModule(MountInstance mountPoint, CompositeNodeWrapper data) {
509         Preconditions.checkNotNull(data.namespace)
510         var Module module = null;
511         if (mountPoint !== null) {
512             module = mountPoint.findModuleByNamespace(data.namespace) // namespace from XML
513             if (module === null) {
514                 module = mountPoint.findModuleByName(data.namespace.toString) // namespace (module name) from JSON
515             }
516         } else {
517             module = data.namespace.findModuleByNamespace // namespace from XML
518             if (module === null) {
519                 module = data.namespace.toString.findModuleByName // namespace (module name) from JSON
520             }
521         }
522         return module
523     }
524
525     private def dispatch getName(CompositeNode data) {
526         return data.nodeType.localName
527     }
528
529     private def dispatch getName(CompositeNodeWrapper data) {
530         return data.localName
531     }
532
533     private def InstanceIdWithSchemaNode addLastIdentifierFromData(InstanceIdWithSchemaNode identifierWithSchemaNode,
534         CompositeNode data, DataSchemaNode schemaOfData) {
535         val iiOriginal = identifierWithSchemaNode?.instanceIdentifier
536         var InstanceIdentifierBuilder iiBuilder = null
537         if (iiOriginal === null) {
538             iiBuilder = InstanceIdentifier.builder
539         } else {
540             iiBuilder = InstanceIdentifier.builder(iiOriginal)
541         }
542
543         if (schemaOfData instanceof ListSchemaNode) {
544             iiBuilder.nodeWithKey(schemaOfData.QName, (schemaOfData as ListSchemaNode).resolveKeysFromData(data))
545         } else {
546             iiBuilder.node(schemaOfData.QName)
547         }
548         return new InstanceIdWithSchemaNode(iiBuilder.toInstance, schemaOfData, identifierWithSchemaNode?.mountPoint)
549     }
550
551     private def resolveKeysFromData(ListSchemaNode listNode, CompositeNode dataNode) {
552         val keyValues = new HashMap<QName, Object>();
553         for (key : listNode.keyDefinition) {
554             val dataNodeKeyValueObject = dataNode.getSimpleNodesByName(key.localName)?.head?.value
555             if (dataNodeKeyValueObject === null) {
556                 throw new ResponseException(BAD_REQUEST,
557                     "Data contains list \"" + dataNode.nodeType.localName + "\" which does not contain key: \"" +
558                         key.localName + "\"")
559             }
560             keyValues.put(key, dataNodeKeyValueObject);
561         }
562         return keyValues
563     }
564
565     private def endsWithMountPoint(String identifier) {
566         return (identifier.endsWith(ControllerContext.MOUNT) || identifier.endsWith(ControllerContext.MOUNT + "/"))
567     }
568
569     private def representsMountPointRootData(CompositeNode data) {
570         return ((data.namespace == SchemaContext.NAME.namespace || data.namespace == MOUNT_POINT_MODULE_NAME) &&
571             data.localName == SchemaContext.NAME.localName)
572     }
573
574     private def addMountPointIdentifier(String identifier) {
575         if (identifier.endsWith("/")) {
576             return identifier + ControllerContext.MOUNT
577         }
578         return identifier + "/" + ControllerContext.MOUNT
579     }
580
581     private def CompositeNode normalizeNode(CompositeNode node, DataSchemaNode schema, MountInstance mountPoint) {
582         if (schema === null) {
583             throw new ResponseException(INTERNAL_SERVER_ERROR,
584                 "Data schema node was not found for " + node?.nodeType?.localName)
585         }
586         if (!(schema instanceof DataNodeContainer)) {
587             throw new ResponseException(BAD_REQUEST, "Root element has to be container or list yang datatype.");
588         }
589         if (node instanceof CompositeNodeWrapper) {
590             if ((node  as CompositeNodeWrapper).changeAllowed) {
591                 normalizeNode(node as CompositeNodeWrapper, schema, null, mountPoint)
592             }
593             return (node as CompositeNodeWrapper).unwrap()
594         }
595         return node
596     }
597
598     private def void normalizeNode(NodeWrapper<?> nodeBuilder, DataSchemaNode schema, QName previousAugment,
599         MountInstance mountPoint) {
600         if (schema === null) {
601             throw new ResponseException(BAD_REQUEST,
602                 "Data has bad format.\n\"" + nodeBuilder.localName + "\" does not exist in yang schema.");
603         }
604
605         var QName currentAugment;
606         if (nodeBuilder.qname !== null) {
607             currentAugment = previousAugment
608         } else {
609             currentAugment = normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint)
610             if (nodeBuilder.qname === null) {
611                 throw new ResponseException(BAD_REQUEST,
612                     "Data has bad format.\nIf data is in XML format then namespace for \"" + nodeBuilder.localName +
613                         "\" should be \"" + schema.QName.namespace + "\".\n" +
614                         "If data is in JSON format then module name for \"" + nodeBuilder.localName +
615                          "\" should be corresponding to namespace \"" + schema.QName.namespace + "\".");
616             }
617         }
618
619         if (nodeBuilder instanceof CompositeNodeWrapper) {
620             val List<NodeWrapper<?>> children = (nodeBuilder as CompositeNodeWrapper).getValues
621             for (child : children) {
622                 val potentialSchemaNodes = (schema as DataNodeContainer).findInstanceDataChildrenByName(child.localName)
623                 if (potentialSchemaNodes.size > 1 && child.namespace === null) {
624                     val StringBuilder namespacesOfPotentialModules = new StringBuilder;
625                     for (potentialSchemaNode : potentialSchemaNodes) {
626                         namespacesOfPotentialModules.append("   ").append(potentialSchemaNode.QName.namespace.toString).append("\n")
627                     }
628                     throw new ResponseException(BAD_REQUEST,
629                         "Node \"" + child.localName + "\" is added as augment from more than one module. " 
630                         + "Therefore node must have namespace (XML format) or module name (JSON format)."
631                         + "\nThe node is added as augment from modules with namespaces:\n" + namespacesOfPotentialModules)
632                 }
633                 var rightNodeSchemaFound = false
634                 for (potentialSchemaNode : potentialSchemaNodes) {
635                     if (!rightNodeSchemaFound) {
636                         val potentialCurrentAugment = normalizeNodeName(child, potentialSchemaNode, currentAugment,
637                             mountPoint)
638                         if (child.qname !== null) {
639                             normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint)
640                             rightNodeSchemaFound = true
641                         }
642                     }
643                 }
644                 if (!rightNodeSchemaFound) {
645                     throw new ResponseException(BAD_REQUEST,
646                         "Schema node \"" + child.localName + "\" was not found in module.")
647                 }
648             }
649             if (schema instanceof ListSchemaNode) {
650                 val listKeys = (schema as ListSchemaNode).keyDefinition
651                 for (listKey : listKeys) {
652                     var foundKey = false
653                     for (child : children) {
654                         if (child.unwrap.nodeType.localName == listKey.localName) {
655                             foundKey = true;
656                         }
657                     }
658                     if (!foundKey) {
659                         throw new ResponseException(BAD_REQUEST,
660                             "Missing key in URI \"" + listKey.localName + "\" of list \"" + schema.QName.localName +
661                                 "\"")
662                     }
663                 }
664             }
665         } else if (nodeBuilder instanceof SimpleNodeWrapper) {
666             val simpleNode = (nodeBuilder as SimpleNodeWrapper)
667             val value = simpleNode.value
668             var inputValue = value;
669
670             if (schema.typeDefinition instanceof IdentityrefTypeDefinition) {
671                 if (value instanceof String) {
672                     inputValue = new IdentityValuesDTO(nodeBuilder.namespace.toString, value as String, null)
673                 } // else value is already instance of IdentityValuesDTO
674             }
675             
676             val outputValue = RestCodec.from(schema.typeDefinition, mountPoint)?.deserialize(inputValue);
677             simpleNode.setValue(outputValue)
678         } else if (nodeBuilder instanceof EmptyNodeWrapper) {
679             val emptyNodeBuilder = nodeBuilder as EmptyNodeWrapper
680             if (schema instanceof LeafSchemaNode) {
681                 emptyNodeBuilder.setComposite(false);
682             } else if (schema instanceof ContainerSchemaNode) {
683
684                 // FIXME: Add presence check
685                 emptyNodeBuilder.setComposite(true);
686             }
687         }
688     }
689
690     private def dispatch TypeDefinition<?> typeDefinition(LeafSchemaNode node) {
691         var baseType = node.type
692         while (baseType.baseType !== null) {
693             baseType = baseType.baseType;
694         }
695         baseType
696     }
697
698     private def dispatch TypeDefinition<?> typeDefinition(LeafListSchemaNode node) {
699         var TypeDefinition<?> baseType = node.type
700         while (baseType.baseType !== null) {
701             baseType = baseType.baseType;
702         }
703         baseType
704     }
705     
706     private def QName normalizeNodeName(NodeWrapper<?> nodeBuilder, DataSchemaNode schema, QName previousAugment,
707         MountInstance mountPoint) {
708         var validQName = schema.QName
709         var currentAugment = previousAugment;
710         if (schema.augmenting) {
711             currentAugment = schema.QName
712         } else if (previousAugment !== null && schema.QName.namespace !== previousAugment.namespace) {
713             validQName = QName.create(currentAugment, schema.QName.localName);
714         }
715         var String moduleName = null;
716         if (mountPoint === null) {
717             moduleName = controllerContext.findModuleNameByNamespace(validQName.namespace);
718         } else {
719             moduleName = controllerContext.findModuleNameByNamespace(mountPoint, validQName.namespace)
720         }
721         if (nodeBuilder.namespace === null || nodeBuilder.namespace == validQName.namespace ||
722             nodeBuilder.namespace.toString == moduleName || nodeBuilder.namespace == MOUNT_POINT_MODULE_NAME) {
723             nodeBuilder.qname = validQName
724         }
725         return currentAugment
726     }
727
728 }