2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.sal.restconf.impl
10 import com.google.common.base.Preconditions
12 import java.text.ParseException
13 import java.text.SimpleDateFormat
14 import java.util.ArrayList
15 import java.util.HashMap
18 import javax.ws.rs.core.Response
19 import org.opendaylight.controller.md.sal.common.api.TransactionStatus
20 import org.opendaylight.controller.sal.core.api.mount.MountInstance
21 import org.opendaylight.controller.sal.rest.api.RestconfService
22 import org.opendaylight.yangtools.yang.common.QName
23 import org.opendaylight.yangtools.yang.common.RpcResult
24 import org.opendaylight.yangtools.yang.data.api.CompositeNode
25 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
26 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder
27 import org.opendaylight.yangtools.yang.data.api.Node
28 import org.opendaylight.yangtools.yang.data.impl.NodeFactory
29 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
30 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
31 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
32 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
33 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
34 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
35 import org.opendaylight.yangtools.yang.model.api.Module
36 import org.opendaylight.yangtools.yang.model.api.RpcDefinition
37 import org.opendaylight.yangtools.yang.model.api.SchemaContext
38 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
39 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition
41 import static javax.ws.rs.core.Response.Status.*
42 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder
43 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder
44 import org.opendaylight.yangtools.yang.model.util.EmptyType
45 import com.google.common.base.Splitter
46 import com.google.common.collect.Lists
48 class RestconfImpl implements RestconfService {
50 val static RestconfImpl INSTANCE = new RestconfImpl
51 val static MOUNT_POINT_MODULE_NAME = "ietf-netconf"
52 val static REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd")
53 val static RESTCONF_MODULE_DRAFT02_REVISION = "2013-10-19"
54 val static RESTCONF_MODULE_DRAFT02_NAME = "ietf-restconf"
55 val static RESTCONF_MODULE_DRAFT02_NAMESPACE = "urn:ietf:params:xml:ns:yang:ietf-restconf"
56 val static RESTCONF_MODULE_DRAFT02_RESTCONF_GROUPING_SCHEMA_NODE = "restconf"
57 val static RESTCONF_MODULE_DRAFT02_RESTCONF_CONTAINER_SCHEMA_NODE = "restconf"
58 val static RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE = "modules"
59 val static RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE = "module"
60 val static RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE = "operations"
66 extension ControllerContext controllerContext
69 if (INSTANCE !== null) {
70 throw new IllegalStateException("Already instantiated");
74 static def getInstance() {
78 override getModules() {
79 val restconfModule = getRestconfModule()
80 val List<Node<?>> modulesAsData = new ArrayList
81 val moduleSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE)
82 for (module : allModules) {
83 modulesAsData.add(module.toModuleCompositeNode(moduleSchemaNode))
85 val modulesSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE)
86 val modulesNode = NodeFactory.createImmutableCompositeNode(modulesSchemaNode.QName, null, modulesAsData)
87 return new StructuredData(modulesNode, modulesSchemaNode, null)
90 override getModules(String identifier) {
91 var Set<Module> modules = null
92 var MountInstance mountPoint = null
93 if (identifier.contains(ControllerContext.MOUNT)) {
94 mountPoint = identifier.toMountPointIdentifier.mountPoint
95 modules = mountPoint.allModules
97 throw new ResponseException(BAD_REQUEST, "URI has bad format. If modules behind mount point should be showed, URI has to end with " + ControllerContext.MOUNT)
99 val List<Node<?>> modulesAsData = new ArrayList
100 val moduleSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE)
101 for (module : modules) {
102 modulesAsData.add(module.toModuleCompositeNode(moduleSchemaNode))
104 val modulesSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE)
105 val modulesNode = NodeFactory.createImmutableCompositeNode(modulesSchemaNode.QName, null, modulesAsData)
106 return new StructuredData(modulesNode, modulesSchemaNode, mountPoint)
109 override getModule(String identifier) {
110 val moduleNameAndRevision = identifier.moduleNameAndRevision
111 var Module module = null
112 var MountInstance mountPoint = null
113 if (identifier.contains(ControllerContext.MOUNT)) {
114 mountPoint = identifier.toMountPointIdentifier.mountPoint
115 module = mountPoint.findModuleByNameAndRevision(moduleNameAndRevision)
117 module = findModuleByNameAndRevision(moduleNameAndRevision)
119 if (module === null) {
120 throw new ResponseException(BAD_REQUEST,
121 "Module with name '" + moduleNameAndRevision.localName + "' and revision '" +
122 moduleNameAndRevision.revision + "' was not found.")
124 val moduleSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE)
125 val moduleNode = module.toModuleCompositeNode(moduleSchemaNode)
126 return new StructuredData(moduleNode, moduleSchemaNode, mountPoint)
129 override getOperations() {
130 return operationsFromModulesToStructuredData(allModules,null)
133 override getOperations(String identifier) {
134 var Set<Module> modules = null
135 var MountInstance mountPoint = null
136 if (identifier.contains(ControllerContext.MOUNT)) {
137 mountPoint = identifier.toMountPointIdentifier.mountPoint
138 modules = mountPoint.allModules
140 throw new ResponseException(BAD_REQUEST, "URI has bad format. If operations behind mount point should be showed, URI has to end with " + ControllerContext.MOUNT)
142 return operationsFromModulesToStructuredData(modules,mountPoint)
145 private def StructuredData operationsFromModulesToStructuredData(Set<Module> modules,MountInstance mountPoint) {
146 val List<Node<?>> operationsAsData = new ArrayList
147 val operationsSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE)
148 val fakeOperationsSchemaNode = new ContainerSchemaNodeBuilder(RESTCONF_MODULE_DRAFT02_NAME, 0, operationsSchemaNode.QName, operationsSchemaNode.path)
149 for (module : modules) {
150 for (rpc : module.rpcs) {
151 operationsAsData.add(NodeFactory.createImmutableSimpleNode(rpc.QName, null, null))
152 val fakeRpcSchemaNode = new LeafSchemaNodeBuilder(module.name, 0, rpc.QName, null)
153 fakeRpcSchemaNode.setAugmenting(true)
154 fakeRpcSchemaNode.setType(EmptyType.instance)
155 fakeOperationsSchemaNode.addChildNode(fakeRpcSchemaNode.build)
158 val operationsNode = NodeFactory.createImmutableCompositeNode(operationsSchemaNode.QName, null, operationsAsData)
159 return new StructuredData(operationsNode, fakeOperationsSchemaNode.build, mountPoint)
162 private def Module getRestconfModule() {
163 val restconfModule = findModuleByNameAndRevision(
164 QName.create(RESTCONF_MODULE_DRAFT02_NAMESPACE, RESTCONF_MODULE_DRAFT02_REVISION,
165 RESTCONF_MODULE_DRAFT02_NAME))
166 if (restconfModule === null) {
167 throw new ResponseException(INTERNAL_SERVER_ERROR, "Restconf module was not found.")
169 return restconfModule
172 private def QName getModuleNameAndRevision(String identifier) {
173 val indexOfMountPointFirstLetter = identifier.indexOf(ControllerContext.MOUNT)
174 var moduleNameAndRevision = "";
175 if (indexOfMountPointFirstLetter !== -1) { // module and revision is behind mount point string
176 moduleNameAndRevision = identifier.substring(indexOfMountPointFirstLetter + ControllerContext.MOUNT.length)
178 moduleNameAndRevision = identifier
180 val pathArgs = Lists.newArrayList(Splitter.on("/").omitEmptyStrings.split(moduleNameAndRevision))
181 if (pathArgs.length < 2) {
182 throw new ResponseException(BAD_REQUEST,
183 "URI has bad format. End of URI should be in format 'moduleName/yyyy-MM-dd'")
186 val moduleName = pathArgs.head
187 val moduleRevision = REVISION_FORMAT.parse(pathArgs.get(1))
188 return QName.create(null, moduleRevision, moduleName)
189 } catch(ParseException e) {
190 throw new ResponseException(BAD_REQUEST, "URI has bad format. It should be 'moduleName/yyyy-MM-dd'")
194 private def CompositeNode toModuleCompositeNode(Module module, DataSchemaNode moduleSchemaNode) {
195 val List<Node<?>> moduleNodeValues = new ArrayList
196 val nameSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("name").head
197 moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(nameSchemaNode.QName, null, module.name))
198 val revisionSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("revision").head
199 moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(revisionSchemaNode.QName, null, REVISION_FORMAT.format(module.revision)))
200 val namespaceSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("namespace").head
201 moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(namespaceSchemaNode.QName, null, module.namespace.toString))
202 val featureSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("feature").head
203 for (feature : module.features) {
204 moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(featureSchemaNode.QName, null, feature.QName.localName))
206 return NodeFactory.createImmutableCompositeNode(moduleSchemaNode.QName, null, moduleNodeValues)
209 private def DataSchemaNode getSchemaNode(Module restconfModule, String schemaNodeName) {
210 val restconfGrouping = restconfModule.groupings.filter[g|g.QName.localName == RESTCONF_MODULE_DRAFT02_RESTCONF_GROUPING_SCHEMA_NODE].head
211 val restconfContainer = restconfGrouping.findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_RESTCONF_CONTAINER_SCHEMA_NODE).head
212 if (schemaNodeName == RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE) {
213 return (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE).head
214 } else if (schemaNodeName == RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE) {
215 return (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE).head
216 } else if (schemaNodeName == RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE) {
217 val modules = (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE).head
218 return (modules as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE).head
227 override invokeRpc(String identifier, CompositeNode payload) {
228 return callRpc(identifier.rpcDefinition, payload)
231 override invokeRpc(String identifier, String noPayload) {
232 if (!noPayload.nullOrEmpty) {
233 throw new ResponseException(UNSUPPORTED_MEDIA_TYPE, "Content-Type contains unsupported Media Type.");
235 return callRpc(identifier.rpcDefinition, null)
238 private def StructuredData callRpc(RpcDefinition rpc, CompositeNode payload) {
240 throw new ResponseException(NOT_FOUND, "RPC does not exist.");
242 var CompositeNode rpcRequest;
243 if (payload === null) {
244 rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, null, null, null)
246 val value = normalizeNode(payload, rpc.input, null)
247 val List<Node<?>> input = new ArrayList
249 rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, input, null, null)
251 val rpcResult = broker.invokeRpc(rpc.QName, rpcRequest);
252 if (!rpcResult.successful) {
253 throw new ResponseException(INTERNAL_SERVER_ERROR, "Operation failed")
255 if (rpcResult.result === null) {
258 return new StructuredData(rpcResult.result, rpc.output, null)
261 override readConfigurationData(String identifier) {
262 val iiWithData = identifier.toInstanceIdentifier
263 var CompositeNode data = null;
264 if (iiWithData.mountPoint !== null) {
265 data = broker.readConfigurationDataBehindMountPoint(iiWithData.mountPoint, iiWithData.getInstanceIdentifier)
267 data = broker.readConfigurationData(iiWithData.getInstanceIdentifier);
269 return new StructuredData(data, iiWithData.schemaNode, iiWithData.mountPoint)
272 override readOperationalData(String identifier) {
273 val iiWithData = identifier.toInstanceIdentifier
274 var CompositeNode data = null;
275 if (iiWithData.mountPoint !== null) {
276 data = broker.readOperationalDataBehindMountPoint(iiWithData.mountPoint, iiWithData.getInstanceIdentifier)
278 data = broker.readOperationalData(iiWithData.getInstanceIdentifier);
280 return new StructuredData(data, iiWithData.schemaNode, iiWithData.mountPoint)
283 override updateConfigurationData(String identifier, CompositeNode payload) {
284 val iiWithData = identifier.toInstanceIdentifier
285 val value = normalizeNode(payload, iiWithData.schemaNode, iiWithData.mountPoint)
286 var RpcResult<TransactionStatus> status = null
287 if (iiWithData.mountPoint !== null) {
288 status = broker.commitConfigurationDataPutBehindMountPoint(iiWithData.mountPoint,
289 iiWithData.instanceIdentifier, value).get()
291 status = broker.commitConfigurationDataPut(iiWithData.instanceIdentifier, value).get();
293 switch status.result {
294 case TransactionStatus.COMMITED: Response.status(OK).build
295 default: Response.status(INTERNAL_SERVER_ERROR).build
299 override createConfigurationData(String identifier, CompositeNode payload) {
300 if (payload.namespace === null) {
301 throw new ResponseException(BAD_REQUEST,
302 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
304 var InstanceIdWithSchemaNode iiWithData;
305 var CompositeNode value;
306 if (payload.representsMountPointRootData) { // payload represents mount point data and URI represents path to the mount point
307 if (identifier.endsWithMountPoint) {
308 throw new ResponseException(BAD_REQUEST,
309 "URI has bad format. URI should be without \"" + ControllerContext.MOUNT + "\" for POST operation.");
311 val completIdentifier = identifier.addMountPointIdentifier
312 iiWithData = completIdentifier.toInstanceIdentifier
313 value = normalizeNode(payload, iiWithData.schemaNode, iiWithData.mountPoint)
315 val uncompleteInstIdWithData = identifier.toInstanceIdentifier
316 val parentSchema = uncompleteInstIdWithData.schemaNode as DataNodeContainer
317 val module = uncompleteInstIdWithData.mountPoint.findModule(payload)
318 if (module === null) {
319 throw new ResponseException(BAD_REQUEST, "Module was not found for \"" + payload.namespace + "\"")
321 val schemaNode = parentSchema.findInstanceDataChildByNameAndNamespace(payload.name, module.namespace)
322 value = normalizeNode(payload, schemaNode, uncompleteInstIdWithData.mountPoint)
323 iiWithData = uncompleteInstIdWithData.addLastIdentifierFromData(value, schemaNode)
325 var RpcResult<TransactionStatus> status = null
326 if (iiWithData.mountPoint !== null) {
327 status = broker.commitConfigurationDataPostBehindMountPoint(iiWithData.mountPoint,
328 iiWithData.instanceIdentifier, value)?.get();
330 status = broker.commitConfigurationDataPost(iiWithData.instanceIdentifier, value)?.get();
332 if (status === null) {
333 return Response.status(ACCEPTED).build
335 switch status.result {
336 case TransactionStatus.COMMITED: Response.status(NO_CONTENT).build
337 default: Response.status(INTERNAL_SERVER_ERROR).build
341 override createConfigurationData(CompositeNode payload) {
342 if (payload.namespace === null) {
343 throw new ResponseException(BAD_REQUEST,
344 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
346 val module = findModule(null, payload)
347 if (module === null) {
348 throw new ResponseException(BAD_REQUEST,
349 "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)");
351 val schemaNode = module.findInstanceDataChildByNameAndNamespace(payload.name, module.namespace)
352 val value = normalizeNode(payload, schemaNode, null)
353 val iiWithData = addLastIdentifierFromData(null, value, schemaNode)
354 var RpcResult<TransactionStatus> status = null
355 if (iiWithData.mountPoint !== null) {
356 status = broker.commitConfigurationDataPostBehindMountPoint(iiWithData.mountPoint,
357 iiWithData.instanceIdentifier, value)?.get();
359 status = broker.commitConfigurationDataPost(iiWithData.instanceIdentifier, value)?.get();
361 if (status === null) {
362 return Response.status(ACCEPTED).build
364 switch status.result {
365 case TransactionStatus.COMMITED: Response.status(NO_CONTENT).build
366 default: Response.status(INTERNAL_SERVER_ERROR).build
370 override deleteConfigurationData(String identifier) {
371 val iiWithData = identifier.toInstanceIdentifier
372 var RpcResult<TransactionStatus> status = null
373 if (iiWithData.mountPoint !== null) {
374 status = broker.commitConfigurationDataDeleteBehindMountPoint(iiWithData.mountPoint,
375 iiWithData.getInstanceIdentifier).get;
377 status = broker.commitConfigurationDataDelete(iiWithData.getInstanceIdentifier).get;
379 switch status.result {
380 case TransactionStatus.COMMITED: Response.status(OK).build
381 default: Response.status(INTERNAL_SERVER_ERROR).build
385 private def dispatch URI namespace(CompositeNode data) {
386 return data.nodeType.namespace
389 private def dispatch URI namespace(CompositeNodeWrapper data) {
390 return data.namespace
393 private def dispatch String localName(CompositeNode data) {
394 return data.nodeType.localName
397 private def dispatch String localName(CompositeNodeWrapper data) {
398 return data.localName
401 private def dispatch Module findModule(MountInstance mountPoint, CompositeNode data) {
402 if (mountPoint !== null) {
403 return mountPoint.findModuleByNamespace(data.nodeType.namespace)
405 return findModuleByNamespace(data.nodeType.namespace)
409 private def dispatch Module findModule(MountInstance mountPoint, CompositeNodeWrapper data) {
410 Preconditions.checkNotNull(data.namespace)
411 var Module module = null;
412 if (mountPoint !== null) {
413 module = mountPoint.findModuleByNamespace(data.namespace) // namespace from XML
414 if (module === null) {
415 module = mountPoint.findModuleByName(data.namespace.toString) // namespace (module name) from JSON
418 module = data.namespace.findModuleByNamespace // namespace from XML
419 if (module === null) {
420 module = data.namespace.toString.findModuleByName // namespace (module name) from JSON
426 private def dispatch getName(CompositeNode data) {
427 return data.nodeType.localName
430 private def dispatch getName(CompositeNodeWrapper data) {
431 return data.localName
434 private def InstanceIdWithSchemaNode addLastIdentifierFromData(InstanceIdWithSchemaNode identifierWithSchemaNode,
435 CompositeNode data, DataSchemaNode schemaOfData) {
436 val iiOriginal = identifierWithSchemaNode?.instanceIdentifier
437 var InstanceIdentifierBuilder iiBuilder = null
438 if (iiOriginal === null) {
439 iiBuilder = InstanceIdentifier.builder
441 iiBuilder = InstanceIdentifier.builder(iiOriginal)
444 if (schemaOfData instanceof ListSchemaNode) {
445 iiBuilder.nodeWithKey(schemaOfData.QName, (schemaOfData as ListSchemaNode).resolveKeysFromData(data))
447 iiBuilder.node(schemaOfData.QName)
449 return new InstanceIdWithSchemaNode(iiBuilder.toInstance, schemaOfData, identifierWithSchemaNode?.mountPoint)
452 private def resolveKeysFromData(ListSchemaNode listNode, CompositeNode dataNode) {
453 val keyValues = new HashMap<QName, Object>();
454 for (key : listNode.keyDefinition) {
455 val dataNodeKeyValueObject = dataNode.getSimpleNodesByName(key.localName)?.head?.value
456 if (dataNodeKeyValueObject === null) {
457 throw new ResponseException(BAD_REQUEST,
458 "Data contains list \"" + dataNode.nodeType.localName + "\" which does not contain key: \"" +
459 key.localName + "\"")
461 keyValues.put(key, dataNodeKeyValueObject);
466 private def endsWithMountPoint(String identifier) {
467 return (identifier.endsWith(ControllerContext.MOUNT) || identifier.endsWith(ControllerContext.MOUNT + "/"))
470 private def representsMountPointRootData(CompositeNode data) {
471 return ((data.namespace == SchemaContext.NAME.namespace || data.namespace == MOUNT_POINT_MODULE_NAME) &&
472 data.localName == SchemaContext.NAME.localName)
475 private def addMountPointIdentifier(String identifier) {
476 if (identifier.endsWith("/")) {
477 return identifier + ControllerContext.MOUNT
479 return identifier + "/" + ControllerContext.MOUNT
482 private def CompositeNode normalizeNode(CompositeNode node, DataSchemaNode schema, MountInstance mountPoint) {
483 if (schema === null) {
484 throw new ResponseException(INTERNAL_SERVER_ERROR,
485 "Data schema node was not found for " + node?.nodeType?.localName)
487 if (!(schema instanceof DataNodeContainer)) {
488 throw new ResponseException(BAD_REQUEST, "Root element has to be container or list yang datatype.");
490 if (node instanceof CompositeNodeWrapper) {
491 if ((node as CompositeNodeWrapper).changeAllowed) {
492 normalizeNode(node as CompositeNodeWrapper, schema, null, mountPoint)
494 return (node as CompositeNodeWrapper).unwrap()
499 private def void normalizeNode(NodeWrapper<?> nodeBuilder, DataSchemaNode schema, QName previousAugment,
500 MountInstance mountPoint) {
501 if (schema === null) {
502 throw new ResponseException(BAD_REQUEST,
503 "Data has bad format.\n\"" + nodeBuilder.localName + "\" does not exist in yang schema.");
506 var QName currentAugment;
507 if (nodeBuilder.qname !== null) {
508 currentAugment = previousAugment
510 currentAugment = normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint)
511 if (nodeBuilder.qname === null) {
512 throw new ResponseException(BAD_REQUEST,
513 "Data has bad format.\nIf data is in XML format then namespace for \"" + nodeBuilder.localName +
514 "\" should be \"" + schema.QName.namespace + "\".\n" +
515 "If data is in JSON format then module name for \"" + nodeBuilder.localName +
516 "\" should be corresponding to namespace \"" + schema.QName.namespace + "\".");
520 if (nodeBuilder instanceof CompositeNodeWrapper) {
521 val List<NodeWrapper<?>> children = (nodeBuilder as CompositeNodeWrapper).getValues
522 for (child : children) {
523 val potentialSchemaNodes = (schema as DataNodeContainer).findInstanceDataChildrenByName(child.localName)
524 if (potentialSchemaNodes.size > 1 && child.namespace === null) {
525 val StringBuilder namespacesOfPotentialModules = new StringBuilder;
526 for (potentialSchemaNode : potentialSchemaNodes) {
527 namespacesOfPotentialModules.append(" ").append(potentialSchemaNode.QName.namespace.toString).append("\n")
529 throw new ResponseException(BAD_REQUEST,
530 "Node \"" + child.localName + "\" is added as augment from more than one module. "
531 + "Therefore node must have namespace (XML format) or module name (JSON format)."
532 + "\nThe node is added as augment from modules with namespaces:\n" + namespacesOfPotentialModules)
534 var rightNodeSchemaFound = false
535 for (potentialSchemaNode : potentialSchemaNodes) {
536 if (!rightNodeSchemaFound) {
537 val potentialCurrentAugment = normalizeNodeName(child, potentialSchemaNode, currentAugment,
539 if (child.qname !== null) {
540 normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint)
541 rightNodeSchemaFound = true
545 if (!rightNodeSchemaFound) {
546 throw new ResponseException(BAD_REQUEST,
547 "Schema node \"" + child.localName + "\" was not found in module.")
550 if (schema instanceof ListSchemaNode) {
551 val listKeys = (schema as ListSchemaNode).keyDefinition
552 for (listKey : listKeys) {
554 for (child : children) {
555 if (child.unwrap.nodeType.localName == listKey.localName) {
560 throw new ResponseException(BAD_REQUEST,
561 "Missing key in URI \"" + listKey.localName + "\" of list \"" + schema.QName.localName +
566 } else if (nodeBuilder instanceof SimpleNodeWrapper) {
567 val simpleNode = (nodeBuilder as SimpleNodeWrapper)
568 val value = simpleNode.value
569 var inputValue = value;
571 if (schema.typeDefinition instanceof IdentityrefTypeDefinition) {
572 if (value instanceof String) {
573 inputValue = new IdentityValuesDTO(nodeBuilder.namespace.toString, value as String, null)
574 } // else value is already instance of IdentityValuesDTO
577 val outputValue = RestCodec.from(schema.typeDefinition, mountPoint)?.deserialize(inputValue);
578 simpleNode.setValue(outputValue)
579 } else if (nodeBuilder instanceof EmptyNodeWrapper) {
580 val emptyNodeBuilder = nodeBuilder as EmptyNodeWrapper
581 if (schema instanceof LeafSchemaNode) {
582 emptyNodeBuilder.setComposite(false);
583 } else if (schema instanceof ContainerSchemaNode) {
585 // FIXME: Add presence check
586 emptyNodeBuilder.setComposite(true);
591 private def dispatch TypeDefinition<?> typeDefinition(LeafSchemaNode node) {
592 var baseType = node.type
593 while (baseType.baseType !== null) {
594 baseType = baseType.baseType;
599 private def dispatch TypeDefinition<?> typeDefinition(LeafListSchemaNode node) {
600 var TypeDefinition<?> baseType = node.type
601 while (baseType.baseType !== null) {
602 baseType = baseType.baseType;
607 private def QName normalizeNodeName(NodeWrapper<?> nodeBuilder, DataSchemaNode schema, QName previousAugment,
608 MountInstance mountPoint) {
609 var validQName = schema.QName
610 var currentAugment = previousAugment;
611 if (schema.augmenting) {
612 currentAugment = schema.QName
613 } else if (previousAugment !== null && schema.QName.namespace !== previousAugment.namespace) {
614 validQName = QName.create(currentAugment, schema.QName.localName);
616 var String moduleName = null;
617 if (mountPoint === null) {
618 moduleName = controllerContext.findModuleNameByNamespace(validQName.namespace);
620 moduleName = controllerContext.findModuleNameByNamespace(mountPoint, validQName.namespace)
622 if (nodeBuilder.namespace === null || nodeBuilder.namespace == validQName.namespace ||
623 nodeBuilder.namespace.toString == moduleName || nodeBuilder.namespace == MOUNT_POINT_MODULE_NAME) {
624 nodeBuilder.qname = validQName
626 return currentAugment