X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fsal-rest-connector%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fsal%2Frestconf%2Fimpl%2FRestconfImpl.java;h=ae48ca7196904a135429bd9aae88626f947924d1;hp=ad682bc8291d50e52d674607e30142009bac459e;hb=3b721d88ed1083a463ecb73a6050de4bfedf1a78;hpb=26da3c2a206a753356b507b018052cbb9cccca7d diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java index ad682bc829..ae48ca7196 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java @@ -12,9 +12,9 @@ import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; - import java.net.URI; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -25,34 +25,23 @@ import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.Future; - import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; - import org.apache.commons.lang3.StringUtils; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.sal.core.api.mount.MountInstance; import org.opendaylight.controller.sal.rest.api.Draft02; import org.opendaylight.controller.sal.rest.api.RestconfService; +import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag; +import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType; import org.opendaylight.controller.sal.restconf.rpc.impl.BrokerRpcExecutor; import org.opendaylight.controller.sal.restconf.rpc.impl.MountPointRpcExecutor; import org.opendaylight.controller.sal.restconf.rpc.impl.RpcExecutor; -import org.opendaylight.controller.sal.restconf.impl.BrokerFacade; -import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper; -import org.opendaylight.controller.sal.restconf.impl.ControllerContext; -import org.opendaylight.controller.sal.restconf.impl.EmptyNodeWrapper; -import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO; -import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode; -import org.opendaylight.controller.sal.restconf.impl.NodeWrapper; -import org.opendaylight.controller.sal.restconf.impl.RestCodec; -import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper; -import org.opendaylight.controller.sal.restconf.impl.StructuredData; -import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag; -import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType; import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter; import org.opendaylight.controller.sal.streams.listeners.Notificator; import org.opendaylight.controller.sal.streams.websockets.WebSocketServer; @@ -63,10 +52,14 @@ import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode; import org.opendaylight.yangtools.yang.data.api.Node; import org.opendaylight.yangtools.yang.data.api.SimpleNode; +import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; import org.opendaylight.yangtools.yang.data.impl.NodeFactory; +import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; @@ -85,6 +78,21 @@ import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBu import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder; public class RestconfImpl implements RestconfService { + private enum UriParameters { + PRETTY_PRINT( "prettyPrint"), + DEPTH( "depth"); + + private String uriParameterName; + UriParameters(String uriParameterName) { + this.uriParameterName = uriParameterName; + } + + @Override + public String toString() { + return uriParameterName; + } + } + private final static RestconfImpl INSTANCE = new RestconfImpl(); private static final int CHAR_NOT_FOUND = -1; @@ -117,12 +125,12 @@ public class RestconfImpl implements RestconfService { } @Override - public StructuredData getModules() { + public StructuredData getModules(final UriInfo uriInfo) { final Module restconfModule = this.getRestconfModule(); final List> modulesAsData = new ArrayList>(); final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode( - restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE); + restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE); Set allModules = this.controllerContext.getAllModules(); for (final Module module : allModules) { @@ -131,71 +139,71 @@ public class RestconfImpl implements RestconfService { } final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode( - restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE); + restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE); QName qName = modulesSchemaNode.getQName(); final CompositeNode modulesNode = NodeFactory.createImmutableCompositeNode(qName, null, modulesAsData); - return new StructuredData(modulesNode, modulesSchemaNode, null); + return new StructuredData(modulesNode, modulesSchemaNode, null,parsePrettyPrintParameter( uriInfo )); } @Override - public StructuredData getAvailableStreams() { + public StructuredData getAvailableStreams(final UriInfo uriInfo) { Set availableStreams = Notificator.getStreamNames(); final List> streamsAsData = new ArrayList>(); Module restconfModule = this.getRestconfModule(); final DataSchemaNode streamSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode( - restconfModule, Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE); + restconfModule, Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE); for (final String streamName : availableStreams) { streamsAsData.add(this.toStreamCompositeNode(streamName, streamSchemaNode)); } final DataSchemaNode streamsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode( - restconfModule, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE); + restconfModule, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE); QName qName = streamsSchemaNode.getQName(); final CompositeNode streamsNode = NodeFactory.createImmutableCompositeNode(qName, null, streamsAsData); - return new StructuredData(streamsNode, streamsSchemaNode, null); + return new StructuredData(streamsNode, streamsSchemaNode, null,parsePrettyPrintParameter( uriInfo )); } @Override - public StructuredData getModules(final String identifier) { + public StructuredData getModules(final String identifier,final UriInfo uriInfo) { Set modules = null; MountInstance mountPoint = null; if (identifier.contains(ControllerContext.MOUNT)) { InstanceIdWithSchemaNode mountPointIdentifier = - this.controllerContext.toMountPointIdentifier(identifier); + this.controllerContext.toMountPointIdentifier(identifier); mountPoint = mountPointIdentifier.getMountPoint(); modules = this.controllerContext.getAllModules(mountPoint); } else { throw new RestconfDocumentedException( "URI has bad format. If modules behind mount point should be showed, URI has to end with " + - ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE ); + ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE ); } final List> modulesAsData = new ArrayList>(); Module restconfModule = this.getRestconfModule(); final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode( - restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE); + restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE); for (final Module module : modules) { modulesAsData.add(this.toModuleCompositeNode(module, moduleSchemaNode)); } final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode( - restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE); + restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE); QName qName = modulesSchemaNode.getQName(); final CompositeNode modulesNode = NodeFactory.createImmutableCompositeNode(qName, null, modulesAsData); - return new StructuredData(modulesNode, modulesSchemaNode, mountPoint); + return new StructuredData(modulesNode, modulesSchemaNode, mountPoint,parsePrettyPrintParameter( uriInfo )); } @Override - public StructuredData getModule(final String identifier) { + public StructuredData getModule(final String identifier,final UriInfo uriInfo) { final QName moduleNameAndRevision = this.getModuleNameAndRevision(identifier); Module module = null; MountInstance mountPoint = null; if (identifier.contains(ControllerContext.MOUNT)) { InstanceIdWithSchemaNode mountPointIdentifier = - this.controllerContext.toMountPointIdentifier(identifier); + this.controllerContext.toMountPointIdentifier(identifier); mountPoint = mountPointIdentifier.getMountPoint(); module = this.controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision); } @@ -206,63 +214,64 @@ public class RestconfImpl implements RestconfService { if (module == null) { throw new RestconfDocumentedException( "Module with name '" + moduleNameAndRevision.getLocalName() + "' and revision '" + - moduleNameAndRevision.getRevision() + "' was not found.", - ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT ); + moduleNameAndRevision.getRevision() + "' was not found.", + ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT ); } Module restconfModule = this.getRestconfModule(); final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode( - restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE); + restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE); final CompositeNode moduleNode = this.toModuleCompositeNode(module, moduleSchemaNode); - return new StructuredData(moduleNode, moduleSchemaNode, mountPoint); + return new StructuredData(moduleNode, moduleSchemaNode, mountPoint,parsePrettyPrintParameter( uriInfo )); } @Override - public StructuredData getOperations() { + public StructuredData getOperations(final UriInfo uriInfo) { Set allModules = this.controllerContext.getAllModules(); - return this.operationsFromModulesToStructuredData(allModules, null); + return this.operationsFromModulesToStructuredData(allModules, null,parsePrettyPrintParameter(uriInfo)); } @Override - public StructuredData getOperations(final String identifier) { + public StructuredData getOperations(final String identifier,final UriInfo uriInfo) { Set modules = null; MountInstance mountPoint = null; if (identifier.contains(ControllerContext.MOUNT)) { InstanceIdWithSchemaNode mountPointIdentifier = - this.controllerContext.toMountPointIdentifier(identifier); + this.controllerContext.toMountPointIdentifier(identifier); mountPoint = mountPointIdentifier.getMountPoint(); modules = this.controllerContext.getAllModules(mountPoint); } else { throw new RestconfDocumentedException( "URI has bad format. If operations behind mount point should be showed, URI has to end with " + - ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE ); + ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE ); } - return this.operationsFromModulesToStructuredData(modules, mountPoint); + return this.operationsFromModulesToStructuredData(modules, mountPoint,parsePrettyPrintParameter(uriInfo)); } private StructuredData operationsFromModulesToStructuredData(final Set modules, - final MountInstance mountPoint) { + final MountInstance mountPoint,boolean prettyPrint) { final List> operationsAsData = new ArrayList>(); Module restconfModule = this.getRestconfModule(); final DataSchemaNode operationsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode( - restconfModule, Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE); + restconfModule, Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE); QName qName = operationsSchemaNode.getQName(); SchemaPath path = operationsSchemaNode.getPath(); ContainerSchemaNodeBuilder containerSchemaNodeBuilder = - new ContainerSchemaNodeBuilder(Draft02.RestConfModule.NAME, 0, qName, path); + new ContainerSchemaNodeBuilder(Draft02.RestConfModule.NAME, 0, qName, path); final ContainerSchemaNodeBuilder fakeOperationsSchemaNode = containerSchemaNodeBuilder; for (final Module module : modules) { Set rpcs = module.getRpcs(); for (final RpcDefinition rpc : rpcs) { QName rpcQName = rpc.getQName(); SimpleNode immutableSimpleNode = - NodeFactory.createImmutableSimpleNode(rpcQName, null, null); + NodeFactory.createImmutableSimpleNode(rpcQName, null, null); operationsAsData.add(immutableSimpleNode); String name = module.getName(); - LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName, null); + LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName, + SchemaPath.create(true, QName.create("dummy"))); final LeafSchemaNodeBuilder fakeRpcSchemaNode = leafSchemaNodeBuilder; fakeRpcSchemaNode.setAugmenting(true); @@ -273,9 +282,9 @@ public class RestconfImpl implements RestconfService { } final CompositeNode operationsNode = - NodeFactory.createImmutableCompositeNode(qName, null, operationsAsData); + NodeFactory.createImmutableCompositeNode(qName, null, operationsAsData); ContainerSchemaNode schemaNode = fakeOperationsSchemaNode.build(); - return new StructuredData(operationsNode, schemaNode, mountPoint); + return new StructuredData(operationsNode, schemaNode, mountPoint,prettyPrint); } private Module getRestconfModule() { @@ -325,34 +334,34 @@ public class RestconfImpl implements RestconfService { final List> streamNodeValues = new ArrayList>(); List instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) streamSchemaNode), - "name"); + "name"); final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); streamNodeValues.add(NodeFactory.createImmutableSimpleNode(nameSchemaNode.getQName(), null, - streamName)); + streamName)); instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( - ((DataNodeContainer) streamSchemaNode), "description"); + ((DataNodeContainer) streamSchemaNode), "description"); final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); streamNodeValues.add(NodeFactory.createImmutableSimpleNode(descriptionSchemaNode.getQName(), null, - "DESCRIPTION_PLACEHOLDER")); + "DESCRIPTION_PLACEHOLDER")); instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( - ((DataNodeContainer) streamSchemaNode), "replay-support"); + ((DataNodeContainer) streamSchemaNode), "replay-support"); final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); streamNodeValues.add(NodeFactory.createImmutableSimpleNode(replaySupportSchemaNode.getQName(), null, - Boolean.valueOf(true))); + Boolean.valueOf(true))); instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( - ((DataNodeContainer) streamSchemaNode), "replay-log-creation-time"); + ((DataNodeContainer) streamSchemaNode), "replay-log-creation-time"); final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); streamNodeValues.add(NodeFactory.createImmutableSimpleNode(replayLogCreationTimeSchemaNode.getQName(), - null, "")); + null, "")); instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( - ((DataNodeContainer) streamSchemaNode), "events"); + ((DataNodeContainer) streamSchemaNode), "events"); final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); streamNodeValues.add(NodeFactory.createImmutableSimpleNode(eventsSchemaNode.getQName(), - null, "")); + null, "")); return NodeFactory.createImmutableCompositeNode(streamSchemaNode.getQName(), null, streamNodeValues); } @@ -360,30 +369,30 @@ public class RestconfImpl implements RestconfService { private CompositeNode toModuleCompositeNode(final Module module, final DataSchemaNode moduleSchemaNode) { final List> moduleNodeValues = new ArrayList>(); List instanceDataChildrenByName = - this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) moduleSchemaNode), "name"); + this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) moduleSchemaNode), "name"); final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(nameSchemaNode.getQName(), - null, module.getName())); + null, module.getName())); instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( - ((DataNodeContainer) moduleSchemaNode), "revision"); + ((DataNodeContainer) moduleSchemaNode), "revision"); final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); Date _revision = module.getRevision(); moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(revisionSchemaNode.getQName(), null, - REVISION_FORMAT.format(_revision))); + REVISION_FORMAT.format(_revision))); instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( - ((DataNodeContainer) moduleSchemaNode), "namespace"); + ((DataNodeContainer) moduleSchemaNode), "namespace"); final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(namespaceSchemaNode.getQName(), null, - module.getNamespace().toString())); + module.getNamespace().toString())); instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( - ((DataNodeContainer) moduleSchemaNode), "feature"); + ((DataNodeContainer) moduleSchemaNode), "feature"); final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); for (final FeatureDefinition feature : module.getFeatures()) { moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(featureSchemaNode.getQName(), null, - feature.getQName().getLocalName())); + feature.getQName().getLocalName())); } return NodeFactory.createImmutableCompositeNode(moduleSchemaNode.getQName(), null, moduleNodeValues); @@ -395,24 +404,47 @@ public class RestconfImpl implements RestconfService { } @Override - public StructuredData invokeRpc(final String identifier, final CompositeNode payload) { + public StructuredData invokeRpc(final String identifier, final CompositeNode payload,final UriInfo uriInfo) { final RpcExecutor rpc = this.resolveIdentifierInInvokeRpc(identifier); QName rpcName = rpc.getRpcDefinition().getQName(); URI rpcNamespace = rpcName.getNamespace(); if (Objects.equal(rpcNamespace.toString(), SAL_REMOTE_NAMESPACE) && - Objects.equal(rpcName.getLocalName(), SAL_REMOTE_RPC_SUBSRCIBE)) { - - return invokeSalRemoteRpcSubscribeRPC(payload, rpc.getRpcDefinition()); + Objects.equal(rpcName.getLocalName(), SAL_REMOTE_RPC_SUBSRCIBE)) { + return invokeSalRemoteRpcSubscribeRPC(payload, rpc.getRpcDefinition(),parsePrettyPrintParameter(uriInfo)); } - return callRpc(rpc, payload); + validateInput( rpc.getRpcDefinition().getInput(), payload ); + + return callRpc(rpc, payload,parsePrettyPrintParameter(uriInfo)); + } + + private void validateInput(final DataSchemaNode inputSchema, final CompositeNode payload) { + if( inputSchema != null && payload == null ) + { + //expected a non null payload + throw new RestconfDocumentedException( "Input is required.", + ErrorType.PROTOCOL, + ErrorTag.MALFORMED_MESSAGE ); + } + else if( inputSchema == null && payload != null ) + { + //did not expect any input + throw new RestconfDocumentedException( "No input expected.", + ErrorType.PROTOCOL, + ErrorTag.MALFORMED_MESSAGE ); + } + //else + //{ + //TODO: Validate "mandatory" and "config" values here??? Or should those be + // validate in a more central location inside MD-SAL core. + //} } private StructuredData invokeSalRemoteRpcSubscribeRPC(final CompositeNode payload, - final RpcDefinition rpc) { + final RpcDefinition rpc,final boolean prettyPrint) { final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null); final SimpleNode pathNode = value == null ? null : - value.getFirstSimpleByName( QName.create(rpc.getQName(), "path") ); + value.getFirstSimpleByName( QName.create(rpc.getQName(), "path") ); final Object pathValue = pathNode == null ? null : pathNode.getValue(); if (!(pathValue instanceof InstanceIdentifier)) { @@ -423,7 +455,7 @@ public class RestconfImpl implements RestconfService { final InstanceIdentifier pathIdentifier = ((InstanceIdentifier) pathValue); String streamName = null; - if (!Iterables.isEmpty(pathIdentifier.getPath())) { + if (!Iterables.isEmpty(pathIdentifier.getPathArguments())) { String fullRestconfIdentifier = this.controllerContext.toFullRestconfIdentifier(pathIdentifier); streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier); } @@ -435,28 +467,27 @@ public class RestconfImpl implements RestconfService { } final SimpleNode streamNameNode = NodeFactory.createImmutableSimpleNode( - QName.create(rpc.getOutput().getQName(), "stream-name"), null, streamName); + QName.create(rpc.getOutput().getQName(), "stream-name"), null, streamName); final List> output = new ArrayList>(); output.add(streamNameNode); final MutableCompositeNode responseData = NodeFactory.createMutableCompositeNode( - rpc.getOutput().getQName(), null, output, null, null); + rpc.getOutput().getQName(), null, output, null, null); if (!Notificator.existListenerFor(pathIdentifier)) { Notificator.createListener(pathIdentifier, streamName); } - return new StructuredData(responseData, rpc.getOutput(), null); + return new StructuredData(responseData, rpc.getOutput(), null,prettyPrint); } @Override - public StructuredData invokeRpc(final String identifier, final String noPayload) { + public StructuredData invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) { if (StringUtils.isNotBlank(noPayload)) { throw new RestconfDocumentedException( "Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE ); } - final RpcExecutor rpc = resolveIdentifierInInvokeRpc(identifier); - return callRpc(rpc, null); + return invokeRpc( identifier, (CompositeNode)null,uriInfo); } private RpcExecutor resolveIdentifierInInvokeRpc(final String identifier) { @@ -500,7 +531,7 @@ public class RestconfImpl implements RestconfService { } - private StructuredData callRpc(final RpcExecutor rpcExecutor, final CompositeNode payload) { + private StructuredData callRpc(final RpcExecutor rpcExecutor, final CompositeNode payload,boolean prettyPrint) { if (rpcExecutor == null) { throw new RestconfDocumentedException( "RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT ); @@ -531,17 +562,17 @@ public class RestconfImpl implements RestconfService { return null; //no output, nothing to send back. } - return new StructuredData(rpcResult.getResult(), rpc.getOutput(), null); + return new StructuredData(rpcResult.getResult(), rpc.getOutput(), null,prettyPrint); } - private void checkRpcSuccessAndThrowException(RpcResult rpcResult) { + private void checkRpcSuccessAndThrowException(final RpcResult rpcResult) { if (rpcResult.isSuccessful() == false) { Collection rpcErrors = rpcResult.getErrors(); if( rpcErrors == null || rpcErrors.isEmpty() ) { throw new RestconfDocumentedException( - "The operation was not successful and there were no RPC errors returned", - ErrorType.RPC, ErrorTag.OPERATION_FAILED ); + "The operation was not successful and there were no RPC errors returned", + ErrorType.RPC, ErrorTag.OPERATION_FAILED ); } List errorList = Lists.newArrayList(); @@ -554,7 +585,7 @@ public class RestconfImpl implements RestconfService { } @Override - public StructuredData readConfigurationData(final String identifier) { + public StructuredData readConfigurationData(final String identifier, final UriInfo uriInfo) { final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier); CompositeNode data = null; MountInstance mountPoint = iiWithData.getMountPoint(); @@ -565,11 +596,58 @@ public class RestconfImpl implements RestconfService { data = broker.readConfigurationData(iiWithData.getInstanceIdentifier()); } - return new StructuredData(data, iiWithData.getSchemaNode(), iiWithData.getMountPoint()); + data = pruneDataAtDepth( data, parseDepthParameter( uriInfo ) ); + boolean prettyPrintMode = parsePrettyPrintParameter( uriInfo ); + return new StructuredData(data, iiWithData.getSchemaNode(), iiWithData.getMountPoint(),prettyPrintMode); + } + + @SuppressWarnings("unchecked") + private > T pruneDataAtDepth( final T node, final Integer depth ) { + if( depth == null ) { + return node; + } + + if( node instanceof CompositeNode ) { + ImmutableList.Builder> newChildNodes = ImmutableList.> builder(); + if( depth > 1 ) { + for( Node childNode: ((CompositeNode)node).getValue() ) { + newChildNodes.add( pruneDataAtDepth( childNode, depth - 1 ) ); + } + } + + return (T) ImmutableCompositeNode.create( node.getNodeType(), newChildNodes.build() ); + } + else { // SimpleNode + return node; + } + } + + private Integer parseDepthParameter( final UriInfo info ) { + String param = info.getQueryParameters( false ).getFirst( UriParameters.DEPTH.toString() ); + if( Strings.isNullOrEmpty( param ) || "unbounded".equals( param ) ) { + return null; + } + + try { + Integer depth = Integer.valueOf( param ); + if( depth < 1 ) { + throw new RestconfDocumentedException( new RestconfError( + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, "Invalid depth parameter: " + depth, + null, "The depth parameter must be an integer > 1 or \"unbounded\"" ) ); + } + + return depth; + } + catch( NumberFormatException e ) { + throw new RestconfDocumentedException( new RestconfError( + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, + "Invalid depth parameter: " + e.getMessage(), + null, "The depth parameter must be an integer > 1 or \"unbounded\"" ) ); + } } @Override - public StructuredData readOperationalData(final String identifier) { + public StructuredData readOperationalData(final String identifier, final UriInfo info) { final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier); CompositeNode data = null; MountInstance mountPoint = iiWithData.getMountPoint(); @@ -580,20 +658,31 @@ public class RestconfImpl implements RestconfService { data = broker.readOperationalData(iiWithData.getInstanceIdentifier()); } - return new StructuredData(data, iiWithData.getSchemaNode(), mountPoint); + data = pruneDataAtDepth( data, parseDepthParameter( info ) ); + boolean prettyPrintMode = parsePrettyPrintParameter( info ); + return new StructuredData(data, iiWithData.getSchemaNode(), mountPoint,prettyPrintMode); + } + + private boolean parsePrettyPrintParameter(UriInfo info) { + String param = info.getQueryParameters(false).getFirst(UriParameters.PRETTY_PRINT.toString()); + return Boolean.parseBoolean(param); } @Override public Response updateConfigurationData(final String identifier, final CompositeNode payload) { final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier); + + validateInput(iiWithData.getSchemaNode(), payload); + MountInstance mountPoint = iiWithData.getMountPoint(); final CompositeNode value = this.normalizeNode(payload, iiWithData.getSchemaNode(), mountPoint); + validateListKeysEqualityInPayloadAndUri(iiWithData, payload); RpcResult status = null; try { if (mountPoint != null) { status = broker.commitConfigurationDataPutBehindMountPoint( - mountPoint, iiWithData.getInstanceIdentifier(), value).get(); + mountPoint, iiWithData.getInstanceIdentifier(), value).get(); } else { status = broker.commitConfigurationDataPut(iiWithData.getInstanceIdentifier(), value).get(); } @@ -602,25 +691,78 @@ public class RestconfImpl implements RestconfService { throw new RestconfDocumentedException( "Error updating data", e ); } - if( status.getResult() == TransactionStatus.COMMITED ) + if( status.getResult() == TransactionStatus.COMMITED ) { return Response.status(Status.OK).build(); + } return Response.status(Status.INTERNAL_SERVER_ERROR).build(); } + /** + * Validates whether keys in {@code payload} are equal to values of keys in + * {@code iiWithData} for list schema node + * + * @throws RestconfDocumentedException + * if key values or key count in payload and URI isn't equal + * + */ + private void validateListKeysEqualityInPayloadAndUri(final InstanceIdWithSchemaNode iiWithData, + final CompositeNode payload) { + if (iiWithData.getSchemaNode() instanceof ListSchemaNode) { + final List keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition(); + final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument(); + if (lastPathArgument instanceof NodeIdentifierWithPredicates) { + final Map uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument) + .getKeyValues(); + isEqualUriAndPayloadKeyValues(uriKeyValues, payload, keyDefinitions); + } + } + } + + private void isEqualUriAndPayloadKeyValues(final Map uriKeyValues, final CompositeNode payload, + final List keyDefinitions) { + for (QName keyDefinition : keyDefinitions) { + final Object uriKeyValue = uriKeyValues.get(keyDefinition); + // should be caught during parsing URI to InstanceIdentifier + if (uriKeyValue == null) { + throw new RestconfDocumentedException("Missing key " + keyDefinition + " in URI.", + ErrorType.PROTOCOL, ErrorTag.DATA_MISSING); + } + final List> payloadKeyValues = payload.getSimpleNodesByName(keyDefinition.getLocalName()); + if (payloadKeyValues.isEmpty()) { + throw new RestconfDocumentedException("Missing key " + keyDefinition.getLocalName() + + " in the message body.", ErrorType.PROTOCOL, + ErrorTag.DATA_MISSING); + } + + Object payloadKeyValue = payloadKeyValues.iterator().next().getValue(); + if (!uriKeyValue.equals(payloadKeyValue)) { + throw new RestconfDocumentedException("The value '"+uriKeyValue+ "' for key '" + keyDefinition.getLocalName() + + "' specified in the URI doesn't match the value '" + payloadKeyValue + "' specified in the message body. ", + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + } + } + } + @Override public Response createConfigurationData(final String identifier, final CompositeNode payload) { + if( payload == null ) { + throw new RestconfDocumentedException( "Input is required.", + ErrorType.PROTOCOL, + ErrorTag.MALFORMED_MESSAGE ); + } + URI payloadNS = this.namespace(payload); if (payloadNS == null) { throw new RestconfDocumentedException( - "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)", - ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE ); + "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)", + ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE ); } InstanceIdWithSchemaNode iiWithData = null; CompositeNode value = null; if (this.representsMountPointRootData(payload)) { - // payload represents mount point data and URI represents path to the mount point + // payload represents mount point data and URI represents path to the mount point if (this.endsWithMountPoint(identifier)) { throw new RestconfDocumentedException( @@ -636,7 +778,7 @@ public class RestconfImpl implements RestconfService { } else { final InstanceIdWithSchemaNode incompleteInstIdWithData = - this.controllerContext.toInstanceIdentifier(identifier); + this.controllerContext.toInstanceIdentifier(identifier); final DataNodeContainer parentSchema = (DataNodeContainer) incompleteInstIdWithData.getSchemaNode(); MountInstance mountPoint = incompleteInstIdWithData.getMountPoint(); final Module module = this.findModule(mountPoint, payload); @@ -648,7 +790,7 @@ public class RestconfImpl implements RestconfService { String payloadName = this.getName(payload); final DataSchemaNode schemaNode = this.controllerContext.findInstanceDataChildByNameAndNamespace( - parentSchema, payloadName, module.getNamespace()); + parentSchema, payloadName, module.getNamespace()); value = this.normalizeNode(payload, schemaNode, mountPoint); iiWithData = this.addLastIdentifierFromData(incompleteInstIdWithData, value, schemaNode); @@ -659,13 +801,13 @@ public class RestconfImpl implements RestconfService { try { if (mountPoint != null) { Future> future = - broker.commitConfigurationDataPostBehindMountPoint( - mountPoint, iiWithData.getInstanceIdentifier(), value); + broker.commitConfigurationDataPostBehindMountPoint( + mountPoint, iiWithData.getInstanceIdentifier(), value); status = future == null ? null : future.get(); } else { Future> future = - broker.commitConfigurationDataPost(iiWithData.getInstanceIdentifier(), value); + broker.commitConfigurationDataPost(iiWithData.getInstanceIdentifier(), value); status = future == null ? null : future.get(); } } @@ -677,19 +819,26 @@ public class RestconfImpl implements RestconfService { return Response.status(Status.ACCEPTED).build(); } - if( status.getResult() == TransactionStatus.COMMITED ) + if( status.getResult() == TransactionStatus.COMMITED ) { return Response.status(Status.NO_CONTENT).build(); + } return Response.status(Status.INTERNAL_SERVER_ERROR).build(); } @Override public Response createConfigurationData(final CompositeNode payload) { + if( payload == null ) { + throw new RestconfDocumentedException( "Input is required.", + ErrorType.PROTOCOL, + ErrorTag.MALFORMED_MESSAGE ); + } + URI payloadNS = this.namespace(payload); if (payloadNS == null) { throw new RestconfDocumentedException( - "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)", - ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE ); + "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)", + ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE ); } final Module module = this.findModule(null, payload); @@ -701,7 +850,7 @@ public class RestconfImpl implements RestconfService { String payloadName = this.getName(payload); final DataSchemaNode schemaNode = this.controllerContext.findInstanceDataChildByNameAndNamespace( - module, payloadName, module.getNamespace()); + module, payloadName, module.getNamespace()); final CompositeNode value = this.normalizeNode(payload, schemaNode, null); final InstanceIdWithSchemaNode iiWithData = this.addLastIdentifierFromData(null, value, schemaNode); RpcResult status = null; @@ -710,13 +859,13 @@ public class RestconfImpl implements RestconfService { try { if (mountPoint != null) { Future> future = - broker.commitConfigurationDataPostBehindMountPoint( - mountPoint, iiWithData.getInstanceIdentifier(), value); + broker.commitConfigurationDataPostBehindMountPoint( + mountPoint, iiWithData.getInstanceIdentifier(), value); status = future == null ? null : future.get(); } else { Future> future = - broker.commitConfigurationDataPost(iiWithData.getInstanceIdentifier(), value); + broker.commitConfigurationDataPost(iiWithData.getInstanceIdentifier(), value); status = future == null ? null : future.get(); } } @@ -728,8 +877,9 @@ public class RestconfImpl implements RestconfService { return Response.status(Status.ACCEPTED).build(); } - if( status.getResult() == TransactionStatus.COMMITED ) + if( status.getResult() == TransactionStatus.COMMITED ) { return Response.status(Status.NO_CONTENT).build(); + } return Response.status(Status.INTERNAL_SERVER_ERROR).build(); } @@ -743,7 +893,7 @@ public class RestconfImpl implements RestconfService { try { if (mountPoint != null) { status = broker.commitConfigurationDataDeleteBehindMountPoint( - mountPoint, iiWithData.getInstanceIdentifier()).get(); + mountPoint, iiWithData.getInstanceIdentifier()).get(); } else { status = broker.commitConfigurationDataDelete(iiWithData.getInstanceIdentifier()).get(); @@ -753,8 +903,9 @@ public class RestconfImpl implements RestconfService { throw new RestconfDocumentedException( "Error creating data", e ); } - if( status.getResult() == TransactionStatus.COMMITED ) + if( status.getResult() == TransactionStatus.COMMITED ) { return Response.status(Status.OK).build(); + } return Response.status(Status.INTERNAL_SERVER_ERROR).build(); } @@ -776,7 +927,7 @@ public class RestconfImpl implements RestconfService { broker.registerToListenDataChanges(listener); final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder(); - UriBuilder port = uriBuilder.port(WebSocketServer.PORT); + UriBuilder port = uriBuilder.port(WebSocketServer.getInstance().getPort()); final URI uriToWebsocketServer = port.replacePath(streamName).build(); return Response.status(Status.OK).location(uriToWebsocketServer).build(); @@ -823,8 +974,8 @@ public class RestconfImpl implements RestconfService { } private InstanceIdWithSchemaNode addLastIdentifierFromData( - final InstanceIdWithSchemaNode identifierWithSchemaNode, - final CompositeNode data, final DataSchemaNode schemaOfData) { + final InstanceIdWithSchemaNode identifierWithSchemaNode, + final CompositeNode data, final DataSchemaNode schemaOfData) { InstanceIdentifier instanceIdentifier = null; if (identifierWithSchemaNode != null) { instanceIdentifier = identifierWithSchemaNode.getInstanceIdentifier(); @@ -857,7 +1008,7 @@ public class RestconfImpl implements RestconfService { } private HashMap resolveKeysFromData(final ListSchemaNode listNode, - final CompositeNode dataNode) { + final CompositeNode dataNode) { final HashMap keyValues = new HashMap(); List _keyDefinition = listNode.getKeyDefinition(); for (final QName key : _keyDefinition) { @@ -888,7 +1039,7 @@ public class RestconfImpl implements RestconfService { private boolean endsWithMountPoint(final String identifier) { return identifier.endsWith(ControllerContext.MOUNT) || - identifier.endsWith(ControllerContext.MOUNT + "/"); + identifier.endsWith(ControllerContext.MOUNT + "/"); } private boolean representsMountPointRootData(final CompositeNode data) { @@ -908,7 +1059,7 @@ public class RestconfImpl implements RestconfService { } private CompositeNode normalizeNode(final CompositeNode node, final DataSchemaNode schema, - final MountInstance mountPoint) { + final MountInstance mountPoint) { if (schema == null) { QName nodeType = node == null ? null : node.getNodeType(); String localName = nodeType == null ? null : nodeType.getLocalName(); @@ -932,7 +1083,7 @@ public class RestconfImpl implements RestconfService { } catch (IllegalArgumentException e) { throw new RestconfDocumentedException( - e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE ); + e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE ); } } @@ -943,8 +1094,8 @@ public class RestconfImpl implements RestconfService { } private void normalizeNode(final NodeWrapper nodeBuilder, - final DataSchemaNode schema, final QName previousAugment, - final MountInstance mountPoint) { + final DataSchemaNode schema, final QName previousAugment, + final MountInstance mountPoint) { if (schema == null) { throw new RestconfDocumentedException( "Data has bad format.\n\"" + nodeBuilder.getLocalName() + @@ -961,120 +1112,174 @@ public class RestconfImpl implements RestconfService { if (nodeBuilder.getQname() == null) { throw new RestconfDocumentedException( "Data has bad format.\nIf data is in XML format then namespace for \"" + - nodeBuilder.getLocalName() + - "\" should be \"" + schema.getQName().getNamespace() + "\".\n" + - "If data is in JSON format then module name for \"" + nodeBuilder.getLocalName() + - "\" should be corresponding to namespace \"" + - schema.getQName().getNamespace() + "\".", - ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE ); + nodeBuilder.getLocalName() + + "\" should be \"" + schema.getQName().getNamespace() + "\".\n" + + "If data is in JSON format then module name for \"" + nodeBuilder.getLocalName() + + "\" should be corresponding to namespace \"" + + schema.getQName().getNamespace() + "\".", + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE ); } } - if ((nodeBuilder instanceof CompositeNodeWrapper)) { - final List> children = ((CompositeNodeWrapper) nodeBuilder).getValues(); - for (final NodeWrapper child : children) { - final List potentialSchemaNodes = - this.controllerContext.findInstanceDataChildrenByName( - ((DataNodeContainer) schema), child.getLocalName()); + if ( nodeBuilder instanceof CompositeNodeWrapper ) { + if( schema instanceof DataNodeContainer ) { + normalizeCompositeNode( (CompositeNodeWrapper)nodeBuilder, (DataNodeContainer)schema, + mountPoint, currentAugment ); + } + else if( schema instanceof AnyXmlSchemaNode ) { + normalizeAnyXmlNode( (CompositeNodeWrapper)nodeBuilder, (AnyXmlSchemaNode)schema ); + } + } + else if ( nodeBuilder instanceof SimpleNodeWrapper ) { + normalizeSimpleNode( (SimpleNodeWrapper) nodeBuilder, schema, mountPoint ); + } + else if ((nodeBuilder instanceof EmptyNodeWrapper)) { + normalizeEmptyNode( (EmptyNodeWrapper) nodeBuilder, schema ); + } + } - if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) { - StringBuilder builder = new StringBuilder(); - for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) { - builder.append(" ").append(potentialSchemaNode.getQName().getNamespace().toString()) - .append("\n"); - } + private void normalizeAnyXmlNode( final CompositeNodeWrapper compositeNode, final AnyXmlSchemaNode schema ) { + List> children = compositeNode.getValues(); + for( NodeWrapper child : children ) { + child.setNamespace( schema.getQName().getNamespace() ); + if( child instanceof CompositeNodeWrapper ) { + normalizeAnyXmlNode( (CompositeNodeWrapper)child, schema ); + } + } + } - throw new RestconfDocumentedException( - "Node \"" + child.getLocalName() + - "\" is added as augment from more than one module. " + - "Therefore node must have namespace (XML format) or module name (JSON format)." + - "\nThe node is added as augment from modules with namespaces:\n" + builder, - ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE ); - } + private void normalizeEmptyNode( final EmptyNodeWrapper emptyNodeBuilder, final DataSchemaNode schema ) { + if ((schema instanceof LeafSchemaNode)) { + emptyNodeBuilder.setComposite(false); + } + else { + if ((schema instanceof ContainerSchemaNode)) { + // FIXME: Add presence check + emptyNodeBuilder.setComposite(true); + } + } + } - boolean rightNodeSchemaFound = false; + private void normalizeSimpleNode( final SimpleNodeWrapper simpleNode, final DataSchemaNode schema, + final MountInstance mountPoint ) { + final Object value = simpleNode.getValue(); + Object inputValue = value; + TypeDefinition typeDefinition = this.typeDefinition(schema); + if ((typeDefinition instanceof IdentityrefTypeDefinition)) { + if ((value instanceof String)) { + inputValue = new IdentityValuesDTO( simpleNode.getNamespace().toString(), + (String) value, null, (String) value ); + } // else value is already instance of IdentityValuesDTO + } + + Object outputValue = inputValue; + + if( typeDefinition != null ) { + Codec codec = RestCodec.from(typeDefinition, mountPoint); + outputValue = codec == null ? null : codec.deserialize(inputValue); + } + + simpleNode.setValue(outputValue); + } + + private void normalizeCompositeNode( final CompositeNodeWrapper compositeNodeBuilder, + final DataNodeContainer schema, final MountInstance mountPoint, + final QName currentAugment ) { + final List> children = compositeNodeBuilder.getValues(); + checkNodeMultiplicityAccordingToSchema(schema,children); + for (final NodeWrapper child : children) { + final List potentialSchemaNodes = + this.controllerContext.findInstanceDataChildrenByName( + schema, child.getLocalName()); + + if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) { + StringBuilder builder = new StringBuilder(); for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) { - if (!rightNodeSchemaFound) { - final QName potentialCurrentAugment = - this.normalizeNodeName(child, potentialSchemaNode, currentAugment, mountPoint); - if (child.getQname() != null ) { - this.normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint); - rightNodeSchemaFound = true; - } - } + builder.append(" ").append(potentialSchemaNode.getQName().getNamespace().toString()) + .append("\n"); } + throw new RestconfDocumentedException( + "Node \"" + child.getLocalName() + + "\" is added as augment from more than one module. " + + "Therefore node must have namespace (XML format) or module name (JSON format)." + + "\nThe node is added as augment from modules with namespaces:\n" + builder, + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE ); + } + + boolean rightNodeSchemaFound = false; + for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) { if (!rightNodeSchemaFound) { - throw new RestconfDocumentedException( - "Schema node \"" + child.getLocalName() + "\" was not found in module.", - ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT ); + final QName potentialCurrentAugment = + this.normalizeNodeName(child, potentialSchemaNode, currentAugment, mountPoint); + if (child.getQname() != null ) { + this.normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint); + rightNodeSchemaFound = true; + } } } - if ((schema instanceof ListSchemaNode)) { - final List listKeys = ((ListSchemaNode) schema).getKeyDefinition(); - for (final QName listKey : listKeys) { - boolean foundKey = false; - for (final NodeWrapper child : children) { - if (Objects.equal(child.unwrap().getNodeType().getLocalName(), listKey.getLocalName())) { - foundKey = true; - } - } + if (!rightNodeSchemaFound) { + throw new RestconfDocumentedException( + "Schema node \"" + child.getLocalName() + "\" was not found in module.", + ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT ); + } + } - if (!foundKey) { - throw new RestconfDocumentedException( - "Missing key in URI \"" + listKey.getLocalName() + - "\" of list \"" + schema.getQName().getLocalName() + "\"", - ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE ); + if ((schema instanceof ListSchemaNode)) { + ListSchemaNode listSchemaNode = (ListSchemaNode)schema; + final List listKeys = listSchemaNode.getKeyDefinition(); + for (final QName listKey : listKeys) { + boolean foundKey = false; + for (final NodeWrapper child : children) { + if (Objects.equal(child.unwrap().getNodeType().getLocalName(), listKey.getLocalName())) { + foundKey = true; } } + + if (!foundKey) { + throw new RestconfDocumentedException( + "Missing key '" + listKey.getLocalName() + + "' for list '" + listSchemaNode.getQName().getLocalName() + "' in the message body", + ErrorType.PROTOCOL, ErrorTag.DATA_MISSING ); + } } } - else { - if ((nodeBuilder instanceof SimpleNodeWrapper)) { - final SimpleNodeWrapper simpleNode = ((SimpleNodeWrapper) nodeBuilder); - final Object value = simpleNode.getValue(); - Object inputValue = value; - TypeDefinition typeDefinition = this.typeDefinition(schema); - if ((typeDefinition instanceof IdentityrefTypeDefinition)) { - if ((value instanceof String)) { - inputValue = new IdentityValuesDTO( nodeBuilder.getNamespace().toString(), - (String) value, null, (String) value ); - } // else value is already instance of IdentityValuesDTO - } + } - Codec codec = RestCodec.from(typeDefinition, mountPoint); - Object outputValue = codec == null ? null : codec.deserialize(inputValue); + private void checkNodeMultiplicityAccordingToSchema(final DataNodeContainer dataNodeContainer, + final List> nodes) { + Map equalNodeNamesToCounts = new HashMap(); + for (NodeWrapper child : nodes) { + Integer count = equalNodeNamesToCounts.get(child.getLocalName()); + equalNodeNamesToCounts.put(child.getLocalName(), count == null ? 1 : ++count); + } - simpleNode.setValue(outputValue); - } - else { - if ((nodeBuilder instanceof EmptyNodeWrapper)) { - final EmptyNodeWrapper emptyNodeBuilder = ((EmptyNodeWrapper) nodeBuilder); - if ((schema instanceof LeafSchemaNode)) { - emptyNodeBuilder.setComposite(false); - } - else { - if ((schema instanceof ContainerSchemaNode)) { - // FIXME: Add presence check - emptyNodeBuilder.setComposite(true); - } - } + for (DataSchemaNode childSchemaNode : dataNodeContainer.getChildNodes()) { + if (childSchemaNode instanceof ContainerSchemaNode || childSchemaNode instanceof LeafSchemaNode) { + String localName = childSchemaNode.getQName().getLocalName(); + Integer count = equalNodeNamesToCounts.get(localName); + if (count != null && count > 1) { + throw new RestconfDocumentedException( + "Multiple input data elements were specified for '" + + childSchemaNode.getQName().getLocalName() + + "'. The data for this element type can only be specified once.", + ErrorType.APPLICATION, ErrorTag.BAD_ELEMENT); } } } } private QName normalizeNodeName(final NodeWrapper nodeBuilder, - final DataSchemaNode schema, final QName previousAugment, - final MountInstance mountPoint) { + final DataSchemaNode schema, final QName previousAugment, + final MountInstance mountPoint) { QName validQName = schema.getQName(); QName currentAugment = previousAugment; if (schema.isAugmenting()) { currentAugment = schema.getQName(); } else if (previousAugment != null && - !Objects.equal( schema.getQName().getNamespace(), previousAugment.getNamespace())) { + !Objects.equal( schema.getQName().getNamespace(), previousAugment.getNamespace())) { validQName = QName.create(currentAugment, schema.getQName().getLocalName()); } @@ -1087,8 +1292,8 @@ public class RestconfImpl implements RestconfService { } if (nodeBuilder.getNamespace() == null || - Objects.equal(nodeBuilder.getNamespace(), validQName.getNamespace()) || - Objects.equal(nodeBuilder.getNamespace().toString(), moduleName) /*|| + Objects.equal(nodeBuilder.getNamespace(), validQName.getNamespace()) || + Objects.equal(nodeBuilder.getNamespace().toString(), moduleName) /*|| Note: this check is wrong - can never be true as it compares a URI with a String not sure what the intention is so commented out... Objects.equal(nodeBuilder.getNamespace(), MOUNT_POINT_MODULE_NAME)*/ ) { @@ -1163,6 +1368,9 @@ public class RestconfImpl implements RestconfService { else if (node instanceof LeafSchemaNode) { return _typeDefinition((LeafSchemaNode)node); } + else if (node instanceof AnyXmlSchemaNode) { + return null; + } else { throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.asList(node).toString());