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=4e1adbc598be2807bccf1f0e8bd76926e141d55d;hp=4716a02be2f26230721be7e08eb697eaeb0224cc;hb=d80c9663968b16cac94ae50f49f573358a4da6c5;hpb=5135f2d97f0886632f3ad3b7160a3be54909810f 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 4716a02be2..4e1adbc598 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 @@ -25,6 +25,7 @@ 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; @@ -75,6 +76,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; @@ -107,12 +123,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) { @@ -121,71 +137,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); } @@ -196,59 +212,59 @@ 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(); @@ -264,9 +280,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() { @@ -316,34 +332,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); } @@ -351,30 +367,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); @@ -386,47 +402,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)); } validateInput( rpc.getRpcDefinition().getInput(), payload ); - return callRpc(rpc, payload); + return callRpc(rpc, payload,parsePrettyPrintParameter(uriInfo)); } - private void validateInput(DataSchemaNode inputSchema, CompositeNode payload) { + 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 ); + 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 ); + ErrorType.PROTOCOL, + ErrorTag.MALFORMED_MESSAGE ); } //else //{ - //TODO: Validate "mandatory" and "config" values here??? Or should those be + //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)) { @@ -437,7 +453,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); } @@ -449,27 +465,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 ); } - return invokeRpc( identifier, (CompositeNode)null ); + return invokeRpc( identifier, (CompositeNode)null,uriInfo); } private RpcExecutor resolveIdentifierInInvokeRpc(final String identifier) { @@ -513,7 +529,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 ); @@ -544,17 +560,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(); @@ -567,7 +583,7 @@ public class RestconfImpl implements RestconfService { } @Override - public StructuredData readConfigurationData(final String identifier, UriInfo info) { + public StructuredData readConfigurationData(final String identifier, final UriInfo uriInfo) { final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier); CompositeNode data = null; MountInstance mountPoint = iiWithData.getMountPoint(); @@ -578,12 +594,13 @@ public class RestconfImpl implements RestconfService { data = broker.readConfigurationData(iiWithData.getInstanceIdentifier()); } - data = pruneDataAtDepth( data, parseDepthParameter( info ) ); - 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( T node, Integer depth ) { + private > T pruneDataAtDepth( final T node, final Integer depth ) { if( depth == null ) { return node; } @@ -603,8 +620,8 @@ public class RestconfImpl implements RestconfService { } } - private Integer parseDepthParameter( UriInfo info ) { - String param = info.getQueryParameters( false ).getFirst( "depth" ); + private Integer parseDepthParameter( final UriInfo info ) { + String param = info.getQueryParameters( false ).getFirst( UriParameters.DEPTH.toString() ); if( Strings.isNullOrEmpty( param ) || "unbounded".equals( param ) ) { return null; } @@ -628,7 +645,7 @@ public class RestconfImpl implements RestconfService { } @Override - public StructuredData readOperationalData(final String identifier, UriInfo info) { + public StructuredData readOperationalData(final String identifier, final UriInfo info) { final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier); CompositeNode data = null; MountInstance mountPoint = iiWithData.getMountPoint(); @@ -640,7 +657,13 @@ public class RestconfImpl implements RestconfService { } data = pruneDataAtDepth( data, parseDepthParameter( info ) ); - return new StructuredData(data, iiWithData.getSchemaNode(), mountPoint); + 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 @@ -656,7 +679,7 @@ public class RestconfImpl implements RestconfService { 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(); } @@ -665,8 +688,9 @@ 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(); } @@ -682,14 +706,14 @@ public class RestconfImpl implements RestconfService { 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( @@ -705,7 +729,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); @@ -717,7 +741,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); @@ -728,13 +752,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(); } } @@ -746,8 +770,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(); } @@ -763,8 +788,8 @@ public class RestconfImpl implements RestconfService { 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); @@ -776,7 +801,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; @@ -785,13 +810,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(); } } @@ -803,8 +828,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(); } @@ -818,7 +844,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(); @@ -828,8 +854,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(); } @@ -898,8 +925,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(); @@ -932,7 +959,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) { @@ -963,7 +990,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) { @@ -983,7 +1010,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(); @@ -1007,7 +1034,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 ); } } @@ -1018,8 +1045,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() + @@ -1036,19 +1063,19 @@ 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 ) { if( schema instanceof DataNodeContainer ) { normalizeCompositeNode( (CompositeNodeWrapper)nodeBuilder, (DataNodeContainer)schema, - mountPoint, currentAugment ); + mountPoint, currentAugment ); } else if( schema instanceof AnyXmlSchemaNode ) { normalizeAnyXmlNode( (CompositeNodeWrapper)nodeBuilder, (AnyXmlSchemaNode)schema ); @@ -1062,7 +1089,7 @@ public class RestconfImpl implements RestconfService { } } - private void normalizeAnyXmlNode( CompositeNodeWrapper compositeNode, AnyXmlSchemaNode schema ) { + private void normalizeAnyXmlNode( final CompositeNodeWrapper compositeNode, final AnyXmlSchemaNode schema ) { List> children = compositeNode.getValues(); for( NodeWrapper child : children ) { child.setNamespace( schema.getQName().getNamespace() ); @@ -1072,7 +1099,7 @@ public class RestconfImpl implements RestconfService { } } - private void normalizeEmptyNode( EmptyNodeWrapper emptyNodeBuilder, DataSchemaNode schema ) { + private void normalizeEmptyNode( final EmptyNodeWrapper emptyNodeBuilder, final DataSchemaNode schema ) { if ((schema instanceof LeafSchemaNode)) { emptyNodeBuilder.setComposite(false); } @@ -1084,8 +1111,8 @@ public class RestconfImpl implements RestconfService { } } - private void normalizeSimpleNode( SimpleNodeWrapper simpleNode, DataSchemaNode schema, - MountInstance mountPoint ) { + 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); @@ -1106,28 +1133,29 @@ public class RestconfImpl implements RestconfService { simpleNode.setValue(outputValue); } - private void normalizeCompositeNode( CompositeNodeWrapper compositeNodeBuilder, - DataNodeContainer schema, MountInstance mountPoint, - QName currentAugment ) { + 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()); + schema, child.getLocalName()); 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"); + .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 ); + "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; @@ -1144,8 +1172,8 @@ public class RestconfImpl implements RestconfService { if (!rightNodeSchemaFound) { throw new RestconfDocumentedException( - "Schema node \"" + child.getLocalName() + "\" was not found in module.", - ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT ); + "Schema node \"" + child.getLocalName() + "\" was not found in module.", + ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT ); } } @@ -1162,24 +1190,47 @@ public class RestconfImpl implements RestconfService { if (!foundKey) { throw new RestconfDocumentedException( - "Missing key in URI \"" + listKey.getLocalName() + - "\" of list \"" + listSchemaNode.getQName().getLocalName() + "\"", - ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE ); + "Missing key in URI \"" + listKey.getLocalName() + + "\" of list \"" + listSchemaNode.getQName().getLocalName() + "\"", + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE ); + } + } + } + } + + 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); + } + + 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()); } @@ -1192,8 +1243,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)*/ ) {