cfbce736fb70041ad2ee03eb26fdbc47d4c86797
[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 = identifier.rpcDefinition
234         if (rpc === null) {
235             throw new ResponseException(NOT_FOUND, "RPC does not exist.");
236         }
237         if (rpc.QName.namespace.toString == SAL_REMOTE_NAMESPACE && rpc.QName.localName == SAL_REMOTE_RPC_SUBSRCIBE) {
238             val value = normalizeNode(payload, rpc.input, null)
239             val pathNode = value?.getFirstSimpleByName(QName.create(rpc.QName, "path"))
240             val pathValue = pathNode?.value
241             if (pathValue === null && !(pathValue instanceof InstanceIdentifier)) {
242                 throw new ResponseException(INTERNAL_SERVER_ERROR, "Instance identifier was not normalized correctly.");
243             }
244             val pathIdentifier = (pathValue as InstanceIdentifier)
245             var String streamName = null
246             if (!pathIdentifier.path.nullOrEmpty) {
247                 streamName = Notificator.createStreamNameFromUri(pathIdentifier.toFullRestconfIdentifier)
248             }
249             if (streamName.nullOrEmpty) {
250                 throw new ResponseException(BAD_REQUEST, "Path is empty or contains data node which is not Container or List build-in type.");
251             }
252             val streamNameNode = NodeFactory.createImmutableSimpleNode(QName.create(rpc.output.QName, "stream-name"), null, streamName)
253             val List<Node<?>> output = new ArrayList
254             output.add(streamNameNode)
255             val responseData = NodeFactory.createMutableCompositeNode(rpc.output.QName, null, output, null, null)
256
257             if (!Notificator.existListenerFor(pathIdentifier)) {
258                 Notificator.createListener(pathIdentifier, streamName)
259             }
260
261             return new StructuredData(responseData, rpc.output, null)
262         }
263         return callRpc(identifier.rpcDefinition, payload)
264     }
265
266     override invokeRpc(String identifier, String noPayload) {
267         if (!noPayload.nullOrEmpty) {
268             throw new ResponseException(UNSUPPORTED_MEDIA_TYPE, "Content-Type contains unsupported Media Type.");
269         }
270         return callRpc(identifier.rpcDefinition, null)
271     }
272
273     private def StructuredData callRpc(RpcDefinition rpc, CompositeNode payload) {
274         if (rpc === null) {
275             throw new ResponseException(NOT_FOUND, "RPC does not exist.");
276         }
277         var CompositeNode rpcRequest;
278         if (payload === null) {
279             rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, null, null, null)
280         } else {
281             val value = normalizeNode(payload, rpc.input, null)
282             val List<Node<?>> input = new ArrayList
283             input.add(value)
284             rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, input, null, null)
285         }
286         val rpcResult = broker.invokeRpc(rpc.QName, rpcRequest);
287         if (!rpcResult.successful) {
288             throw new ResponseException(INTERNAL_SERVER_ERROR, "Operation failed")
289         }
290         if (rpcResult.result === null) {
291             return null
292         }
293         return new StructuredData(rpcResult.result, rpc.output, null)
294     }
295
296     override readConfigurationData(String identifier) {
297         val iiWithData = identifier.toInstanceIdentifier
298         var CompositeNode data = null;
299         if (iiWithData.mountPoint !== null) {
300             data = broker.readConfigurationDataBehindMountPoint(iiWithData.mountPoint, iiWithData.getInstanceIdentifier)
301         } else {
302             data = broker.readConfigurationData(iiWithData.getInstanceIdentifier);
303         }
304         return new StructuredData(data, iiWithData.schemaNode, iiWithData.mountPoint)
305     }
306
307     override readOperationalData(String identifier) {
308         val iiWithData = identifier.toInstanceIdentifier
309         var CompositeNode data = null;
310         if (iiWithData.mountPoint !== null) {
311             data = broker.readOperationalDataBehindMountPoint(iiWithData.mountPoint, iiWithData.getInstanceIdentifier)
312         } else {
313             data = broker.readOperationalData(iiWithData.getInstanceIdentifier);
314         }
315         return new StructuredData(data, iiWithData.schemaNode, iiWithData.mountPoint)
316     }
317
318     override updateConfigurationData(String identifier, CompositeNode payload) {
319         val iiWithData = identifier.toInstanceIdentifier
320         val value = normalizeNode(payload, iiWithData.schemaNode, iiWithData.mountPoint)
321         var RpcResult<TransactionStatus> status = null
322         if (iiWithData.mountPoint !== null) {
323             status = broker.commitConfigurationDataPutBehindMountPoint(iiWithData.mountPoint,
324                 iiWithData.instanceIdentifier, value).get()
325         } else {
326             status = broker.commitConfigurationDataPut(iiWithData.instanceIdentifier, value).get();
327         }
328         switch status.result {
329             case TransactionStatus.COMMITED: Response.status(OK).build
330             default: Response.status(INTERNAL_SERVER_ERROR).build
331         }
332     }
333
334     override createConfigurationData(String identifier, CompositeNode payload) {
335         if (payload.namespace === null) {
336             throw new ResponseException(BAD_REQUEST,
337                 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
338         }
339         var InstanceIdWithSchemaNode iiWithData;
340         var CompositeNode value;
341         if (payload.representsMountPointRootData) { // payload represents mount point data and URI represents path to the mount point
342             if (identifier.endsWithMountPoint) {
343                 throw new ResponseException(BAD_REQUEST,
344                     "URI has bad format. URI should be without \"" + ControllerContext.MOUNT + "\" for POST operation.");
345             }
346             val completIdentifier = identifier.addMountPointIdentifier
347             iiWithData = completIdentifier.toInstanceIdentifier
348             value = normalizeNode(payload, iiWithData.schemaNode, iiWithData.mountPoint)
349         } else {
350             val uncompleteInstIdWithData = identifier.toInstanceIdentifier
351             val parentSchema = uncompleteInstIdWithData.schemaNode as DataNodeContainer
352             val module = uncompleteInstIdWithData.mountPoint.findModule(payload)
353             if (module === null) {
354                 throw new ResponseException(BAD_REQUEST, "Module was not found for \"" + payload.namespace + "\"")
355             }
356             val schemaNode = parentSchema.findInstanceDataChildByNameAndNamespace(payload.name, module.namespace)
357             value = normalizeNode(payload, schemaNode, uncompleteInstIdWithData.mountPoint)
358             iiWithData = uncompleteInstIdWithData.addLastIdentifierFromData(value, schemaNode)
359         }
360         var RpcResult<TransactionStatus> status = null
361         if (iiWithData.mountPoint !== null) {
362             status = broker.commitConfigurationDataPostBehindMountPoint(iiWithData.mountPoint,
363                 iiWithData.instanceIdentifier, value)?.get();
364         } else {
365             status = broker.commitConfigurationDataPost(iiWithData.instanceIdentifier, value)?.get();
366         }
367         if (status === null) {
368             return Response.status(ACCEPTED).build
369         }
370         switch status.result {
371             case TransactionStatus.COMMITED: Response.status(NO_CONTENT).build
372             default: Response.status(INTERNAL_SERVER_ERROR).build
373         }
374     }
375
376     override createConfigurationData(CompositeNode payload) {
377         if (payload.namespace === null) {
378             throw new ResponseException(BAD_REQUEST,
379                 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
380         }
381         val module = findModule(null, payload)
382         if (module === null) {
383             throw new ResponseException(BAD_REQUEST,
384                 "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)");
385         }
386         val schemaNode = module.findInstanceDataChildByNameAndNamespace(payload.name, module.namespace)
387         val value = normalizeNode(payload, schemaNode, null)
388         val iiWithData = addLastIdentifierFromData(null, value, schemaNode)
389         var RpcResult<TransactionStatus> status = null
390         if (iiWithData.mountPoint !== null) {
391             status = broker.commitConfigurationDataPostBehindMountPoint(iiWithData.mountPoint,
392                 iiWithData.instanceIdentifier, value)?.get();
393         } else {
394             status = broker.commitConfigurationDataPost(iiWithData.instanceIdentifier, value)?.get();
395         }
396         if (status === null) {
397             return Response.status(ACCEPTED).build
398         }
399         switch status.result {
400             case TransactionStatus.COMMITED: Response.status(NO_CONTENT).build
401             default: Response.status(INTERNAL_SERVER_ERROR).build
402         }
403     }
404
405     override deleteConfigurationData(String identifier) {
406         val iiWithData = identifier.toInstanceIdentifier
407         var RpcResult<TransactionStatus> status = null
408         if (iiWithData.mountPoint !== null) {
409             status = broker.commitConfigurationDataDeleteBehindMountPoint(iiWithData.mountPoint,
410                 iiWithData.getInstanceIdentifier).get;
411         } else {
412             status = broker.commitConfigurationDataDelete(iiWithData.getInstanceIdentifier).get;
413         }
414         switch status.result {
415             case TransactionStatus.COMMITED: Response.status(OK).build
416             default: Response.status(INTERNAL_SERVER_ERROR).build
417         }
418     }
419
420     override subscribeToStream(String identifier, UriInfo uriInfo) {
421         val streamName = Notificator.createStreamNameFromUri(identifier)
422         if (streamName.nullOrEmpty) {
423             throw new ResponseException(BAD_REQUEST, "Stream name is empty.")
424         }
425         val listener = Notificator.getListenerFor(streamName);
426         if (listener === null) {
427             throw new ResponseException(BAD_REQUEST, "Stream was not found.")
428         }
429         broker.registerToListenDataChanges(listener)
430         val uriBuilder = uriInfo.getAbsolutePathBuilder()
431         val uriToWebsocketServer = uriBuilder.port(WebSocketServer.PORT).replacePath(streamName).build()
432         return Response.status(OK).location(uriToWebsocketServer).build
433     }
434
435     private def dispatch URI namespace(CompositeNode data) {
436         return data.nodeType.namespace
437     }
438
439     private def dispatch URI namespace(CompositeNodeWrapper data) {
440         return data.namespace
441     }
442
443     private def dispatch String localName(CompositeNode data) {
444         return data.nodeType.localName
445     }
446
447     private def dispatch String localName(CompositeNodeWrapper data) {
448         return data.localName
449     }
450
451     private def dispatch Module findModule(MountInstance mountPoint, CompositeNode data) {
452         if (mountPoint !== null) {
453             return mountPoint.findModuleByNamespace(data.nodeType.namespace)
454         } else {
455             return findModuleByNamespace(data.nodeType.namespace)
456         }
457     }
458
459     private def dispatch Module findModule(MountInstance mountPoint, CompositeNodeWrapper data) {
460         Preconditions.checkNotNull(data.namespace)
461         var Module module = null;
462         if (mountPoint !== null) {
463             module = mountPoint.findModuleByNamespace(data.namespace) // namespace from XML
464             if (module === null) {
465                 module = mountPoint.findModuleByName(data.namespace.toString) // namespace (module name) from JSON
466             }
467         } else {
468             module = data.namespace.findModuleByNamespace // namespace from XML
469             if (module === null) {
470                 module = data.namespace.toString.findModuleByName // namespace (module name) from JSON
471             }
472         }
473         return module
474     }
475
476     private def dispatch getName(CompositeNode data) {
477         return data.nodeType.localName
478     }
479
480     private def dispatch getName(CompositeNodeWrapper data) {
481         return data.localName
482     }
483
484     private def InstanceIdWithSchemaNode addLastIdentifierFromData(InstanceIdWithSchemaNode identifierWithSchemaNode,
485         CompositeNode data, DataSchemaNode schemaOfData) {
486         val iiOriginal = identifierWithSchemaNode?.instanceIdentifier
487         var InstanceIdentifierBuilder iiBuilder = null
488         if (iiOriginal === null) {
489             iiBuilder = InstanceIdentifier.builder
490         } else {
491             iiBuilder = InstanceIdentifier.builder(iiOriginal)
492         }
493
494         if (schemaOfData instanceof ListSchemaNode) {
495             iiBuilder.nodeWithKey(schemaOfData.QName, (schemaOfData as ListSchemaNode).resolveKeysFromData(data))
496         } else {
497             iiBuilder.node(schemaOfData.QName)
498         }
499         return new InstanceIdWithSchemaNode(iiBuilder.toInstance, schemaOfData, identifierWithSchemaNode?.mountPoint)
500     }
501
502     private def resolveKeysFromData(ListSchemaNode listNode, CompositeNode dataNode) {
503         val keyValues = new HashMap<QName, Object>();
504         for (key : listNode.keyDefinition) {
505             val dataNodeKeyValueObject = dataNode.getSimpleNodesByName(key.localName)?.head?.value
506             if (dataNodeKeyValueObject === null) {
507                 throw new ResponseException(BAD_REQUEST,
508                     "Data contains list \"" + dataNode.nodeType.localName + "\" which does not contain key: \"" +
509                         key.localName + "\"")
510             }
511             keyValues.put(key, dataNodeKeyValueObject);
512         }
513         return keyValues
514     }
515
516     private def endsWithMountPoint(String identifier) {
517         return (identifier.endsWith(ControllerContext.MOUNT) || identifier.endsWith(ControllerContext.MOUNT + "/"))
518     }
519
520     private def representsMountPointRootData(CompositeNode data) {
521         return ((data.namespace == SchemaContext.NAME.namespace || data.namespace == MOUNT_POINT_MODULE_NAME) &&
522             data.localName == SchemaContext.NAME.localName)
523     }
524
525     private def addMountPointIdentifier(String identifier) {
526         if (identifier.endsWith("/")) {
527             return identifier + ControllerContext.MOUNT
528         }
529         return identifier + "/" + ControllerContext.MOUNT
530     }
531
532     private def CompositeNode normalizeNode(CompositeNode node, DataSchemaNode schema, MountInstance mountPoint) {
533         if (schema === null) {
534             throw new ResponseException(INTERNAL_SERVER_ERROR,
535                 "Data schema node was not found for " + node?.nodeType?.localName)
536         }
537         if (!(schema instanceof DataNodeContainer)) {
538             throw new ResponseException(BAD_REQUEST, "Root element has to be container or list yang datatype.");
539         }
540         if (node instanceof CompositeNodeWrapper) {
541             if ((node  as CompositeNodeWrapper).changeAllowed) {
542                 normalizeNode(node as CompositeNodeWrapper, schema, null, mountPoint)
543             }
544             return (node as CompositeNodeWrapper).unwrap()
545         }
546         return node
547     }
548
549     private def void normalizeNode(NodeWrapper<?> nodeBuilder, DataSchemaNode schema, QName previousAugment,
550         MountInstance mountPoint) {
551         if (schema === null) {
552             throw new ResponseException(BAD_REQUEST,
553                 "Data has bad format.\n\"" + nodeBuilder.localName + "\" does not exist in yang schema.");
554         }
555
556         var QName currentAugment;
557         if (nodeBuilder.qname !== null) {
558             currentAugment = previousAugment
559         } else {
560             currentAugment = normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint)
561             if (nodeBuilder.qname === null) {
562                 throw new ResponseException(BAD_REQUEST,
563                     "Data has bad format.\nIf data is in XML format then namespace for \"" + nodeBuilder.localName +
564                         "\" should be \"" + schema.QName.namespace + "\".\n" +
565                         "If data is in JSON format then module name for \"" + nodeBuilder.localName +
566                          "\" should be corresponding to namespace \"" + schema.QName.namespace + "\".");
567             }
568         }
569
570         if (nodeBuilder instanceof CompositeNodeWrapper) {
571             val List<NodeWrapper<?>> children = (nodeBuilder as CompositeNodeWrapper).getValues
572             for (child : children) {
573                 val potentialSchemaNodes = (schema as DataNodeContainer).findInstanceDataChildrenByName(child.localName)
574                 if (potentialSchemaNodes.size > 1 && child.namespace === null) {
575                     val StringBuilder namespacesOfPotentialModules = new StringBuilder;
576                     for (potentialSchemaNode : potentialSchemaNodes) {
577                         namespacesOfPotentialModules.append("   ").append(potentialSchemaNode.QName.namespace.toString).append("\n")
578                     }
579                     throw new ResponseException(BAD_REQUEST,
580                         "Node \"" + child.localName + "\" is added as augment from more than one module. " 
581                         + "Therefore node must have namespace (XML format) or module name (JSON format)."
582                         + "\nThe node is added as augment from modules with namespaces:\n" + namespacesOfPotentialModules)
583                 }
584                 var rightNodeSchemaFound = false
585                 for (potentialSchemaNode : potentialSchemaNodes) {
586                     if (!rightNodeSchemaFound) {
587                         val potentialCurrentAugment = normalizeNodeName(child, potentialSchemaNode, currentAugment,
588                             mountPoint)
589                         if (child.qname !== null) {
590                             normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint)
591                             rightNodeSchemaFound = true
592                         }
593                     }
594                 }
595                 if (!rightNodeSchemaFound) {
596                     throw new ResponseException(BAD_REQUEST,
597                         "Schema node \"" + child.localName + "\" was not found in module.")
598                 }
599             }
600             if (schema instanceof ListSchemaNode) {
601                 val listKeys = (schema as ListSchemaNode).keyDefinition
602                 for (listKey : listKeys) {
603                     var foundKey = false
604                     for (child : children) {
605                         if (child.unwrap.nodeType.localName == listKey.localName) {
606                             foundKey = true;
607                         }
608                     }
609                     if (!foundKey) {
610                         throw new ResponseException(BAD_REQUEST,
611                             "Missing key in URI \"" + listKey.localName + "\" of list \"" + schema.QName.localName +
612                                 "\"")
613                     }
614                 }
615             }
616         } else if (nodeBuilder instanceof SimpleNodeWrapper) {
617             val simpleNode = (nodeBuilder as SimpleNodeWrapper)
618             val value = simpleNode.value
619             var inputValue = value;
620
621             if (schema.typeDefinition instanceof IdentityrefTypeDefinition) {
622                 if (value instanceof String) {
623                     inputValue = new IdentityValuesDTO(nodeBuilder.namespace.toString, value as String, null)
624                 } // else value is already instance of IdentityValuesDTO
625             }
626             
627             val outputValue = RestCodec.from(schema.typeDefinition, mountPoint)?.deserialize(inputValue);
628             simpleNode.setValue(outputValue)
629         } else if (nodeBuilder instanceof EmptyNodeWrapper) {
630             val emptyNodeBuilder = nodeBuilder as EmptyNodeWrapper
631             if (schema instanceof LeafSchemaNode) {
632                 emptyNodeBuilder.setComposite(false);
633             } else if (schema instanceof ContainerSchemaNode) {
634
635                 // FIXME: Add presence check
636                 emptyNodeBuilder.setComposite(true);
637             }
638         }
639     }
640
641     private def dispatch TypeDefinition<?> typeDefinition(LeafSchemaNode node) {
642         var baseType = node.type
643         while (baseType.baseType !== null) {
644             baseType = baseType.baseType;
645         }
646         baseType
647     }
648
649     private def dispatch TypeDefinition<?> typeDefinition(LeafListSchemaNode node) {
650         var TypeDefinition<?> baseType = node.type
651         while (baseType.baseType !== null) {
652             baseType = baseType.baseType;
653         }
654         baseType
655     }
656     
657     private def QName normalizeNodeName(NodeWrapper<?> nodeBuilder, DataSchemaNode schema, QName previousAugment,
658         MountInstance mountPoint) {
659         var validQName = schema.QName
660         var currentAugment = previousAugment;
661         if (schema.augmenting) {
662             currentAugment = schema.QName
663         } else if (previousAugment !== null && schema.QName.namespace !== previousAugment.namespace) {
664             validQName = QName.create(currentAugment, schema.QName.localName);
665         }
666         var String moduleName = null;
667         if (mountPoint === null) {
668             moduleName = controllerContext.findModuleNameByNamespace(validQName.namespace);
669         } else {
670             moduleName = controllerContext.findModuleNameByNamespace(mountPoint, validQName.namespace)
671         }
672         if (nodeBuilder.namespace === null || nodeBuilder.namespace == validQName.namespace ||
673             nodeBuilder.namespace.toString == moduleName || nodeBuilder.namespace == MOUNT_POINT_MODULE_NAME) {
674             nodeBuilder.qname = validQName
675         }
676         return currentAugment
677     }
678
679 }

©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.