* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-
package org.opendaylight.netconf.sal.restconf.impl;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.CharMatcher;
-import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
-import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FluentFuture;
import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
import java.net.URI;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
-import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
-import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
-import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
-import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException;
-import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
-import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
-import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
+import org.opendaylight.mdsal.common.api.CommitInfo;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.common.api.OptimisticLockFailedException;
+import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
+import org.opendaylight.mdsal.dom.api.DOMMountPoint;
+import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException;
+import org.opendaylight.mdsal.dom.api.DOMRpcResult;
+import org.opendaylight.mdsal.dom.api.DOMRpcService;
+import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
import org.opendaylight.netconf.sal.rest.api.Draft02;
import org.opendaylight.netconf.sal.rest.api.RestconfService;
import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+@Singleton
public final class RestconfImpl implements RestconfService {
/**
* Notifications are served on port 8181.
private final ControllerContext controllerContext;
- private RestconfImpl(final BrokerFacade broker, final ControllerContext controllerContext) {
+ @Inject
+ public RestconfImpl(final BrokerFacade broker, final ControllerContext controllerContext) {
this.broker = broker;
this.controllerContext = controllerContext;
}
+ /**
+ * Factory method.
+ *
+ * @deprecated Just use {@link #RestconfImpl(BrokerFacade, ControllerContext)} constructor instead.
+ */
+ @Deprecated
public static RestconfImpl newInstance(final BrokerFacade broker, final ControllerContext controllerContext) {
return new RestconfImpl(broker, controllerContext);
}
restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode);
- final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> moduleContainerBuilder =
+ final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> moduleContainerBuilder =
Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode);
moduleContainerBuilder.withChild(allModuleMap);
restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode);
- final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> moduleContainerBuilder =
+ final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> moduleContainerBuilder =
Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode);
moduleContainerBuilder.withChild(mountPointModulesMap);
restconfModule, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
Preconditions.checkState(streamsContainerSchemaNode instanceof ContainerSchemaNode);
- final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> streamsContainerBuilder =
+ final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> streamsContainerBuilder =
Builders.containerBuilder((ContainerSchemaNode) streamsContainerSchemaNode);
streamsContainerBuilder.withChild(listStreamsBuilder.build());
}
final ContainerSchemaNode fakeCont = new FakeContainerSchemaNode(fakeRpcSchema);
- final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerBuilder =
+ final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> containerBuilder =
Builders.containerBuilder(fakeCont);
for (final LeafSchemaNode leaf : fakeRpcSchema) {
@Override
public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload,
final UriInfo uriInfo) {
+ if (payload == null) {
+ // no payload specified, reroute this to no payload invokeRpc implementation
+ return invokeRpc(identifier, uriInfo);
+ }
+
final SchemaPath type = payload.getInstanceIdentifierContext().getSchemaNode().getPath();
final URI namespace = payload.getInstanceIdentifierContext().getSchemaNode().getQName().getNamespace();
- final CheckedFuture<DOMRpcResult, DOMRpcException> response;
+ final ListenableFuture<DOMRpcResult> response;
final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
final SchemaContext schemaContext;
resultData = null;
}
- return new NormalizedNodeContext(
- new InstanceIdentifierContext<>(null, resultNodeSchema, mountPoint, schemaContext),
- resultData, QueryParametersParser.parseWriterParameters(uriInfo));
+ if (resultData != null && ((ContainerNode) resultData).getValue().isEmpty()) {
+ throw new WebApplicationException(Response.Status.NO_CONTENT);
+ } else {
+ return new NormalizedNodeContext(
+ new InstanceIdentifierContext<>(null, resultNodeSchema, mountPoint, schemaContext),
+ resultData, QueryParametersParser.parseWriterParameters(uriInfo));
+ }
}
- @Override
- public NormalizedNodeContext invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) {
- if (noPayload != null && !CharMatcher.whitespace().matchesAllOf(noPayload)) {
- throw new RestconfDocumentedException("Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
- }
+ private NormalizedNodeContext invokeRpc(final String identifier, final UriInfo uriInfo) {
- String identifierEncoded = null;
DOMMountPoint mountPoint = null;
+ final String identifierEncoded;
final SchemaContext schemaContext;
if (identifier.contains(ControllerContext.MOUNT)) {
// mounted RPC call - look up mount instance.
final String identifierDecoded = this.controllerContext.urlPathArgDecode(identifierEncoded);
- RpcDefinition rpc = null;
+ RpcDefinition rpc;
if (mountPoint == null) {
rpc = this.controllerContext.getRpcDefinition(identifierDecoded);
} else {
}
if (!rpc.getInput().getChildNodes().isEmpty()) {
- LOG.debug("RPC {} does not need input value.", rpc);
- throw new RestconfDocumentedException("RPC " + rpc + " does not take any input value.",
- ErrorType.RPC, ErrorTag.INVALID_VALUE);
+ LOG.debug("No input specified for RPC {} with an input section", rpc);
+ throw new RestconfDocumentedException("No input specified for RPC " + rpc
+ + " with an input section defined", ErrorType.RPC, ErrorTag.MISSING_ELEMENT);
}
- final CheckedFuture<DOMRpcResult, DOMRpcException> response;
+ final ListenableFuture<DOMRpcResult> response;
if (mountPoint != null) {
final Optional<DOMRpcService> mountRpcServices = mountPoint.getService(DOMRpcService.class);
if (!mountRpcServices.isPresent()) {
final DOMRpcResult result = checkRpcResponse(response);
- return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, rpc, mountPoint, schemaContext),
- result.getResult(), QueryParametersParser.parseWriterParameters(uriInfo));
+ if (result.getResult() != null && ((ContainerNode) result.getResult()).getValue().isEmpty()) {
+ throw new WebApplicationException(Response.Status.NO_CONTENT);
+ } else {
+ return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, rpc, mountPoint, schemaContext),
+ result.getResult(), QueryParametersParser.parseWriterParameters(uriInfo));
+ }
}
@SuppressWarnings("checkstyle:avoidHidingCauseException")
- private static DOMRpcResult checkRpcResponse(final CheckedFuture<DOMRpcResult, DOMRpcException> response) {
+ private static DOMRpcResult checkRpcResponse(final ListenableFuture<DOMRpcResult> response) {
if (response == null) {
return null;
}
}
}
- private CheckedFuture<DOMRpcResult, DOMRpcException>
- invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) {
+ private ListenableFuture<DOMRpcResult> invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) {
final ContainerNode value = (ContainerNode) payload.getData();
final QName rpcQName = payload.getInstanceIdentifierContext().getSchemaNode().getQName();
- final java.util.Optional<DataContainerChild<? extends PathArgument, ?>> path = value.getChild(
+ final Optional<DataContainerChild<? extends PathArgument, ?>> path = value.getChild(
new NodeIdentifier(QName.create(payload.getInstanceIdentifierContext().getSchemaNode().getQName(),
"path")));
final Object pathValue = path.isPresent() ? path.get().getValue() : null;
Notificator.createListener(pathIdentifier, streamName, outputType, controllerContext);
}
- final DOMRpcResult defaultDOMRpcResult = new DefaultDOMRpcResult(output);
-
- return Futures.immediateCheckedFuture(defaultDOMRpcResult);
+ return Futures.immediateFuture(new DefaultDOMRpcResult(output));
}
private static RpcDefinition findRpc(final SchemaContext schemaContext, final String identifierDecoded) {
}
try {
- result.getFutureOfPutData().checkedGet();
- return Response.status(result.getStatus()).build();
- } catch (final TransactionCommitFailedException e) {
- if (e instanceof OptimisticLockFailedException) {
+ result.getFutureOfPutData().get();
+ } catch (final InterruptedException e) {
+ LOG.debug("Update failed for {}", identifier, e);
+ throw new RestconfDocumentedException(e.getMessage(), e);
+ } catch (final ExecutionException e) {
+ final TransactionCommitFailedException failure = Throwables.getCauseAs(e,
+ TransactionCommitFailedException.class);
+ if (failure instanceof OptimisticLockFailedException) {
if (--tries <= 0) {
LOG.debug("Got OptimisticLockFailedException on last try - failing {}", identifier);
- throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
+ throw new RestconfDocumentedException(e.getMessage(), e, failure.getErrorList());
}
LOG.debug("Got OptimisticLockFailedException - trying again {}", identifier);
- } else {
- LOG.debug("Update failed for {}", identifier, e);
- throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
+ continue;
}
+
+ LOG.debug("Update failed for {}", identifier, e);
+ throw RestconfDocumentedException.decodeAndThrow(e.getMessage(), failure);
}
+
+ return Response.status(result.getStatus()).build();
}
}
"Point parameter can be used only with 'after' or 'before' values of Insert parameter.");
}
- CheckedFuture<Void, TransactionCommitFailedException> future;
+ FluentFuture<? extends CommitInfo> future;
if (mountPoint != null) {
future = this.broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData(), insert,
point);
}
try {
- future.checkedGet();
- } catch (final RestconfDocumentedException e) {
- throw e;
- } catch (final TransactionCommitFailedException e) {
+ future.get();
+ } catch (final InterruptedException e) {
+ LOG.info("Error creating data {}", uriInfo != null ? uriInfo.getPath() : "", e);
+ throw new RestconfDocumentedException(e.getMessage(), e);
+ } catch (final ExecutionException e) {
LOG.info("Error creating data {}", uriInfo != null ? uriInfo.getPath() : "", e);
- throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
+ throw RestconfDocumentedException.decodeAndThrow(e.getMessage(), Throwables.getCauseAs(e,
+ TransactionCommitFailedException.class));
}
LOG.trace("Successfuly created data.");
final DOMMountPoint mountPoint = iiWithData.getMountPoint();
final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
- final CheckedFuture<Void, TransactionCommitFailedException> future;
+ final FluentFuture<? extends CommitInfo> future;
if (mountPoint != null) {
future = this.broker.commitConfigurationDataDelete(mountPoint, normalizedII);
} else {
}
try {
- future.checkedGet();
- } catch (final TransactionCommitFailedException e) {
+ future.get();
+ } catch (final InterruptedException e) {
+ throw new RestconfDocumentedException(e.getMessage(), e);
+ } catch (final ExecutionException e) {
final Optional<Throwable> searchedException = Iterables.tryFind(Throwables.getCausalChain(e),
- Predicates.instanceOf(ModifiedNodeDoesNotExistException.class));
+ Predicates.instanceOf(ModifiedNodeDoesNotExistException.class)).toJavaUtil();
if (searchedException.isPresent()) {
- throw new RestconfDocumentedException("Data specified for delete doesn't exist.",
- ErrorType.APPLICATION, ErrorTag.DATA_MISSING, e);
+ throw new RestconfDocumentedException("Data specified for delete doesn't exist.", ErrorType.APPLICATION,
+ ErrorTag.DATA_MISSING, e);
}
- final String errMsg = "Error while deleting data";
- LOG.info(errMsg, e);
- throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
+ throw RestconfDocumentedException.decodeAndThrow(e.getMessage(), Throwables.getCauseAs(e,
+ TransactionCommitFailedException.class));
}
return Response.status(Status.OK).build();
if (response != null) {
// prepare node with value of location
final InstanceIdentifierContext<?> iid = prepareIIDSubsStreamOutput();
- final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> builder =
+ final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> builder =
ImmutableLeafNodeBuilder.create().withValue(response.toString());
builder.withNodeIdentifier(
NodeIdentifier.create(QName.create("subscribe:to:notification", "2016-10-28", "location")));
for (final NotificationListenerAdapter listener : listeners) {
this.broker.registerToListenNotification(listener);
- listener.setQueryParams(start,
- java.util.Optional.ofNullable(stop), java.util.Optional.ofNullable(filter), false);
+ listener.setQueryParams(start, Optional.ofNullable(stop), Optional.ofNullable(filter), false);
}
final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
return uriToWebsocketServerBuilder.replacePath(streamName).build();
}
- private String getWsScheme(final UriInfo uriInfo) {
+ private static String getWsScheme(final UriInfo uriInfo) {
URI uri = uriInfo.getAbsolutePath();
if (uri == null) {
return "ws";
throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL,
ErrorTag.UNKNOWN_ELEMENT);
}
- listener.setQueryParams(start, java.util.Optional.ofNullable(stop),
- java.util.Optional.ofNullable(filter), leafNodesOnly);
+ listener.setQueryParams(start, Optional.ofNullable(stop), Optional.ofNullable(filter), leafNodesOnly);
final Map<String, String> paramToValues = resolveValuesFromUri(identifier);
final LogicalDatastoreType datastore =
*/
private static <T> T parseEnumTypeParameter(final ContainerNode value, final Class<T> classDescriptor,
final String paramName) {
- final java.util.Optional<DataContainerChild<? extends PathArgument, ?>> optAugNode = value.getChild(
+ final Optional<DataContainerChild<? extends PathArgument, ?>> optAugNode = value.getChild(
SAL_REMOTE_AUG_IDENTIFIER);
if (!optAugNode.isPresent()) {
return null;
if (!(augNode instanceof AugmentationNode)) {
return null;
}
- final java.util.Optional<DataContainerChild<? extends PathArgument, ?>> enumNode = ((AugmentationNode) augNode)
- .getChild(new NodeIdentifier(QName.create(SAL_REMOTE_AUGMENT, paramName)));
+ final Optional<DataContainerChild<? extends PathArgument, ?>> enumNode = ((AugmentationNode) augNode).getChild(
+ new NodeIdentifier(QName.create(SAL_REMOTE_AUGMENT, paramName)));
if (!enumNode.isPresent()) {
return null;
}
return listModuleBuilder.build();
}
- protected MapEntryNode toModuleEntryNode(final Module module, final DataSchemaNode moduleSchemaNode) {
+ private MapEntryNode toModuleEntryNode(final Module module, final DataSchemaNode moduleSchemaNode) {
Preconditions.checkArgument(moduleSchemaNode instanceof ListSchemaNode,
"moduleSchemaNode has to be of type ListSchemaNode");
final ListSchemaNode listModuleSchemaNode = (ListSchemaNode) moduleSchemaNode;
- final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> moduleNodeValues =
+ final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> moduleNodeValues =
Builders.mapEntryBuilder(listModuleSchemaNode);
List<DataSchemaNode> instanceDataChildrenByName =
moduleNodeValues
.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(module.getName()).build());
+ final QNameModule qNameModule = module.getQNameModule();
+
instanceDataChildrenByName =
ControllerContext.findInstanceDataChildrenByName(listModuleSchemaNode, "revision");
final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
Preconditions.checkState(revisionSchemaNode instanceof LeafSchemaNode);
- final java.util.Optional<Revision> revision = module.getQNameModule().getRevision();
- if (revision.isPresent()) {
- moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) revisionSchemaNode)
- .withValue(revision.get().toString()).build());
- }
+ final Optional<Revision> revision = qNameModule.getRevision();
+ moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) revisionSchemaNode)
+ .withValue(revision.map(Revision::toString).orElse("")).build());
instanceDataChildrenByName =
ControllerContext.findInstanceDataChildrenByName(listModuleSchemaNode, "namespace");
final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
Preconditions.checkState(namespaceSchemaNode instanceof LeafSchemaNode);
moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) namespaceSchemaNode)
- .withValue(module.getNamespace().toString()).build());
+ .withValue(qNameModule.getNamespace().toString()).build());
instanceDataChildrenByName =
ControllerContext.findInstanceDataChildrenByName(listModuleSchemaNode, "feature");
Preconditions.checkArgument(streamSchemaNode instanceof ListSchemaNode,
"streamSchemaNode has to be of type ListSchemaNode");
final ListSchemaNode listStreamSchemaNode = (ListSchemaNode) streamSchemaNode;
- final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamNodeValues =
+ final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamNodeValues =
Builders.mapEntryBuilder(listStreamSchemaNode);
List<DataSchemaNode> instanceDataChildrenByName =
final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
Preconditions.checkState(replaySupportSchemaNode instanceof LeafSchemaNode);
streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replaySupportSchemaNode)
- .withValue(Boolean.valueOf(true)).build());
+ .withValue(Boolean.TRUE).build());
instanceDataChildrenByName =
ControllerContext.findInstanceDataChildrenByName(listStreamSchemaNode, "replay-log-creation-time");
* contains list of qnames of notifications
* @return - checked future object
*/
- private CheckedFuture<DOMRpcResult, DOMRpcException> invokeSalRemoteRpcNotifiStrRPC(
- final NormalizedNodeContext payload) {
+ private ListenableFuture<DOMRpcResult> invokeSalRemoteRpcNotifiStrRPC(final NormalizedNodeContext payload) {
final ContainerNode data = (ContainerNode) payload.getData();
LeafSetNode leafSet = null;
String outputType = "XML";
Notificator.createNotificationListener(paths, streamName, outputType, controllerContext);
}
- final DOMRpcResult defaultDOMRpcResult = new DefaultDOMRpcResult(output);
-
- return Futures.immediateCheckedFuture(defaultDOMRpcResult);
+ return Futures.immediateFuture(new DefaultDOMRpcResult(output));
}
}