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;
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.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.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;
}
@Override
- public StructuredData getModules() {
+ public StructuredData getModules(final UriInfo uriInfo) {
final Module restconfModule = this.getRestconfModule();
final List<Node<?>> modulesAsData = new ArrayList<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<String> availableStreams = Notificator.getStreamNames();
final List<Node<?>> streamsAsData = new ArrayList<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<Module> modules = null;
MountInstance mountPoint = null;
if (identifier.contains(ControllerContext.MOUNT)) {
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;
final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
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<Module> 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<Module> modules = null;
MountInstance mountPoint = null;
if (identifier.contains(ControllerContext.MOUNT)) {
ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
}
- return this.operationsFromModulesToStructuredData(modules, mountPoint);
+ return this.operationsFromModulesToStructuredData(modules, mountPoint,parsePrettyPrintParameter(uriInfo));
}
private StructuredData operationsFromModulesToStructuredData(final Set<Module> modules,
- final MountInstance mountPoint) {
+ final MountInstance mountPoint,boolean prettyPrint) {
final List<Node<?>> operationsAsData = new ArrayList<Node<?>>();
Module restconfModule = this.getRestconfModule();
final DataSchemaNode operationsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
final CompositeNode operationsNode =
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() {
}
@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());
+ 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(final DataSchemaNode inputSchema, final CompositeNode payload) {
}
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<? extends Object> pathNode = value == null ? null :
value.getFirstSimpleByName( QName.create(rpc.getQName(), "path") );
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) {
}
- 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 );
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(final RpcResult<CompositeNode> rpcResult) {
}
@Override
- public StructuredData readConfigurationData(final String identifier, final 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();
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 Integer parseDepthParameter( final UriInfo info ) {
- String param = info.getQueryParameters( false ).getFirst( "depth" );
+ String param = info.getQueryParameters( false ).getFirst( UriParameters.DEPTH.toString() );
if( Strings.isNullOrEmpty( param ) || "unbounded".equals( param ) ) {
return null;
}
}
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
MountInstance mountPoint = iiWithData.getMountPoint();
final CompositeNode value = this.normalizeNode(payload, iiWithData.getSchemaNode(), mountPoint);
+ validateListKeysEqualityInPayloadAndUri(iiWithData, payload);
RpcResult<TransactionStatus> status = null;
try {
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<QName> keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition();
+ final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
+ if (lastPathArgument instanceof NodeIdentifierWithPredicates) {
+ final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument)
+ .getKeyValues();
+ isEqualUriAndPayloadKeyValues(uriKeyValues, payload, keyDefinitions);
+ }
+ }
+ }
+
+ private void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final CompositeNode payload,
+ final List<QName> 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<SimpleNode<?>> 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 ) {
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();
final DataNodeContainer schema, final MountInstance mountPoint,
final QName currentAugment ) {
final List<NodeWrapper<?>> children = compositeNodeBuilder.getValues();
+ checkNodeMultiplicityAccordingToSchema(schema,children);
for (final NodeWrapper<? extends Object> child : children) {
final List<DataSchemaNode> potentialSchemaNodes =
this.controllerContext.findInstanceDataChildrenByName(
if (!foundKey) {
throw new RestconfDocumentedException(
- "Missing key in URI \"" + listKey.getLocalName() +
- "\" of list \"" + listSchemaNode.getQName().getLocalName() + "\"",
- ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
+ "Missing key '" + listKey.getLocalName() +
+ "' for list '" + listSchemaNode.getQName().getLocalName() + "' in the message body",
+ ErrorType.PROTOCOL, ErrorTag.DATA_MISSING );
+ }
+ }
+ }
+ }
+
+ private void checkNodeMultiplicityAccordingToSchema(final DataNodeContainer dataNodeContainer,
+ final List<NodeWrapper<?>> nodes) {
+ Map<String, Integer> equalNodeNamesToCounts = new HashMap<String, Integer>();
+ 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);
}
}
}