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

©2013 OpenDaylight, A Linux Foundation Collaborative Project. All Rights Reserved.
OpenDaylight is a registered trademark of The OpenDaylight Project, Inc.
Linux Foundation and OpenDaylight are registered trademarks of the Linux Foundation.
Linux is a registered trademark of Linus Torvalds.