import com.google.common.base.CharMatcher;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
-import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import java.math.BigInteger;
import java.net.URI;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
+import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
import org.opendaylight.netconf.sal.streams.listeners.Notificator;
import org.opendaylight.netconf.sal.streams.websockets.WebSocketServer;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.ModifiedNodeDoesNotExistException;
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.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
-import org.opendaylight.yangtools.yang.model.util.EmptyType;
-import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
-import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
-import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
-import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
-import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.EffectiveSchemaContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final RestconfImpl INSTANCE = new RestconfImpl();
+ /**
+ * Notifications are served on port 8181.
+ */
private static final int NOTIFICATION_PORT = 8181;
private static final int CHAR_NOT_FOUND = -1;
private static final String SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote";
- private static final String SAL_REMOTE_RPC_SUBSRCIBE = "create-data-change-event-subscription";
-
private BrokerFacade broker;
private ControllerContext controllerContext;
private static final YangInstanceIdentifier.AugmentationIdentifier SAL_REMOTE_AUG_IDENTIFIER;
+ public static final CharSequence DATA_SUBSCR = "data-change-event-subscription";
+ private static final CharSequence CREATE_DATA_SUBSCR = "create-" + DATA_SUBSCR;
+
+ public static final CharSequence NOTIFICATION_STREAM = "notification-stream";
+ private static final CharSequence CREATE_NOTIFICATION_STREAM = "create-" + NOTIFICATION_STREAM;
+
static {
try {
final Date eventSubscriptionAugRevision = new SimpleDateFormat("yyyy-MM-dd").parse("2014-07-08");
@Override
public NormalizedNodeContext getModules(final UriInfo uriInfo) {
- final Set<Module> allModules = controllerContext.getAllModules();
+ final Set<Module> allModules = this.controllerContext.getAllModules();
final MapNode allModuleMap = makeModuleMapNode(allModules);
- final SchemaContext schemaContext = controllerContext.getGlobalSchema();
+ final SchemaContext schemaContext = this.controllerContext.getGlobalSchema();
final Module restconfModule = getRestconfModule();
- final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+ final DataSchemaNode modulesSchemaNode = this.controllerContext.getRestconfModuleRestConfSchemaNode(
restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode);
throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
}
- final InstanceIdentifierContext<?> mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
+ final InstanceIdentifierContext<?> mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier);
final DOMMountPoint mountPoint = mountPointIdentifier.getMountPoint();
- final Set<Module> modules = controllerContext.getAllModules(mountPoint);
+ final Set<Module> modules = this.controllerContext.getAllModules(mountPoint);
final MapNode mountPointModulesMap = makeModuleMapNode(modules);
final Module restconfModule = getRestconfModule();
- final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+ final DataSchemaNode modulesSchemaNode = this.controllerContext.getRestconfModuleRestConfSchemaNode(
restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode);
moduleContainerBuilder.withChild(mountPointModulesMap);
return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, modulesSchemaNode,
- mountPoint, controllerContext.getGlobalSchema()), moduleContainerBuilder.build(),
+ mountPoint, this.controllerContext.getGlobalSchema()), moduleContainerBuilder.build(),
QueryParametersParser.parseWriterParameters(uriInfo));
}
DOMMountPoint mountPoint = null;
final SchemaContext schemaContext;
if (identifier.contains(ControllerContext.MOUNT)) {
- final InstanceIdentifierContext<?> mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
+ final InstanceIdentifierContext<?> mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier);
mountPoint = mountPointIdentifier.getMountPoint();
- module = controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision);
+ module = this.controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision);
schemaContext = mountPoint.getSchemaContext();
} else {
- module = controllerContext.findModuleByNameAndRevision(moduleNameAndRevision);
- schemaContext = controllerContext.getGlobalSchema();
+ module = this.controllerContext.findModuleByNameAndRevision(moduleNameAndRevision);
+ schemaContext = this.controllerContext.getGlobalSchema();
}
if (module == null) {
final Set<Module> modules = Collections.singleton(module);
final MapNode moduleMap = makeModuleMapNode(modules);
- final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+ final DataSchemaNode moduleSchemaNode = this.controllerContext.getRestconfModuleRestConfSchemaNode(
restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
@Override
public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) {
- final SchemaContext schemaContext = controllerContext.getGlobalSchema();
+ final SchemaContext schemaContext = this.controllerContext.getGlobalSchema();
final Set<String> availableStreams = Notificator.getStreamNames();
final Module restconfModule = getRestconfModule();
- final DataSchemaNode streamSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule,
+ final DataSchemaNode streamSchemaNode = this.controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule,
Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
Preconditions.checkState(streamSchemaNode instanceof ListSchemaNode);
listStreamsBuilder.withChild(toStreamEntryNode(streamName, streamSchemaNode));
}
- final DataSchemaNode streamsContainerSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+ final DataSchemaNode streamsContainerSchemaNode = this.controllerContext.getRestconfModuleRestConfSchemaNode(
restconfModule, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
Preconditions.checkState(streamsContainerSchemaNode instanceof ContainerSchemaNode);
@Override
public NormalizedNodeContext getOperations(final UriInfo uriInfo) {
- final Set<Module> allModules = controllerContext.getAllModules();
+ final Set<Module> allModules = this.controllerContext.getAllModules();
return operationsFromModulesToNormalizedContext(allModules, null);
}
Set<Module> modules = null;
DOMMountPoint mountPoint = null;
if (identifier.contains(ControllerContext.MOUNT)) {
- final InstanceIdentifierContext<?> mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
+ final InstanceIdentifierContext<?> mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier);
mountPoint = mountPointIdentifier.getMountPoint();
- modules = controllerContext.getAllModules(mountPoint);
+ modules = this.controllerContext.getAllModules(mountPoint);
} else {
final String errMsg = "URI has bad format. If operations behind mount point should be showed, URI has to end with ";
return operationsFromModulesToNormalizedContext(modules, mountPoint);
}
- private static final Predicate<GroupingBuilder> GROUPING_FILTER = new Predicate<GroupingBuilder>() {
- @Override
- public boolean apply(final GroupingBuilder g) {
- return Draft02.RestConfModule.RESTCONF_GROUPING_SCHEMA_NODE.equals(g.getQName().getLocalName());
- }
- };
-
+ /**
+ * Special case only for GET restconf/operations use (since moment of pre-Beryllium
+ * Yang parser and Yang model API removal). The method is creating fake
+ * schema context with fake module and fake data by use own implementations
+ * of schema nodes and module.
+ *
+ * @param modules
+ * - set of modules for get RPCs from every module
+ * @param mountPoint
+ * - mount point, if in use otherwise null
+ * @return {@link NormalizedNodeContext}
+ */
private NormalizedNodeContext operationsFromModulesToNormalizedContext(final Set<Module> modules,
final DOMMountPoint mountPoint) {
- final Module restconfModule = getRestconfModule();
- final ModuleBuilder restConfModuleBuilder = new ModuleBuilder(restconfModule);
- final Set<GroupingBuilder> gropingBuilders = restConfModuleBuilder.getGroupingBuilders();
- final Iterable<GroupingBuilder> filteredGroups = Iterables.filter(gropingBuilders, GROUPING_FILTER);
- final GroupingBuilder restconfGroupingBuilder = Iterables.getFirst(filteredGroups, null);
- final ContainerSchemaNodeBuilder restContainerSchemaNodeBuilder = (ContainerSchemaNodeBuilder) restconfGroupingBuilder
- .getDataChildByName(Draft02.RestConfModule.RESTCONF_CONTAINER_SCHEMA_NODE);
- final ContainerSchemaNodeBuilder containerSchemaNodeBuilder = (ContainerSchemaNodeBuilder) restContainerSchemaNodeBuilder
- .getDataChildByName(Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE);
-
- final ContainerSchemaNodeBuilder fakeOperationsSchemaNodeBuilder = containerSchemaNodeBuilder;
- final SchemaPath fakeSchemaPath = fakeOperationsSchemaNodeBuilder.getPath().createChild(QName.create("dummy"));
-
- final List<LeafNode<Object>> operationsAsData = new ArrayList<>();
+ final ContainerSchemaNodeImpl fakeCont = new ContainerSchemaNodeImpl();
+ final List<LeafNode<Object>> listRpcNodes = new ArrayList<>();
+ for (final Module m : modules) {
+ for (final RpcDefinition rpc : m.getRpcs()) {
- for (final Module module : modules) {
- final Set<RpcDefinition> rpcs = module.getRpcs();
- for (final RpcDefinition rpc : rpcs) {
- final QName rpcQName = rpc.getQName();
- final String name = module.getName();
-
- final QName qName = QName.create(restconfModule.getQNameModule(), rpcQName.getLocalName());
- final LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, qName, fakeSchemaPath);
- final LeafSchemaNodeBuilder fakeRpcSchemaNodeBuilder = leafSchemaNodeBuilder;
- fakeRpcSchemaNodeBuilder.setAugmenting(true);
-
- final EmptyType instance = EmptyType.getInstance();
- fakeRpcSchemaNodeBuilder.setType(instance);
- final LeafSchemaNode fakeRpcSchemaNode = fakeRpcSchemaNodeBuilder.build();
- fakeOperationsSchemaNodeBuilder.addChildNode(fakeRpcSchemaNode);
-
- final LeafNode<Object> leaf = Builders.leafBuilder(fakeRpcSchemaNode).build();
- operationsAsData.add(leaf);
+ final LeafSchemaNode fakeLeaf = new LeafSchemaNodeImpl(fakeCont.getPath(),
+ QName.create(ModuleImpl.moduleQName, m.getName() + ":" + rpc.getQName().getLocalName()));
+ fakeCont.addNodeChild(fakeLeaf);
+ listRpcNodes.add(Builders.leafBuilder(fakeLeaf).build());
}
}
+ final ContainerSchemaNode fakeContSchNode = fakeCont;
+ final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerBuilder = Builders
+ .containerBuilder(fakeContSchNode);
- final ContainerSchemaNode operContainerSchemaNode = fakeOperationsSchemaNodeBuilder.build();
- final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> operContainerNode = Builders.containerBuilder(operContainerSchemaNode);
-
- for (final LeafNode<Object> oper : operationsAsData) {
- operContainerNode.withChild(oper);
+ for (final LeafNode<Object> rpcNode : listRpcNodes) {
+ containerBuilder.withChild(rpcNode);
}
- final Set<Module> fakeRpcModules = Collections.singleton(restConfModuleBuilder.build());
+ final Module fakeModule = new ModuleImpl(fakeContSchNode);
- final YangParserImpl yangParser = new YangParserImpl();
- final SchemaContext fakeSchemaCx = yangParser.resolveSchemaContext(fakeRpcModules);
-
- final InstanceIdentifierContext<?> fakeIICx = new InstanceIdentifierContext<>(null, operContainerSchemaNode, mountPoint, fakeSchemaCx);
-
- return new NormalizedNodeContext(fakeIICx, operContainerNode.build());
+ final Set<Module> fakeModules = new HashSet<>();
+ fakeModules.add(fakeModule);
+ final SchemaContext fakeSchemaCtx = EffectiveSchemaContext.resolveSchemaContext(fakeModules);
+ final InstanceIdentifierContext<ContainerSchemaNode> instanceIdentifierContext = new InstanceIdentifierContext<>(
+ null, fakeContSchNode, mountPoint, fakeSchemaCtx);
+ return new NormalizedNodeContext(instanceIdentifierContext, containerBuilder.build());
}
private Module getRestconfModule() {
- final Module restconfModule = controllerContext.getRestconfModule();
+ final Module restconfModule = this.controllerContext.getRestconfModule();
if (restconfModule == null) {
LOG.debug("ietf-restconf module was not found.");
throw new RestconfDocumentedException("ietf-restconf module was not found.", ErrorType.APPLICATION,
response = mountRpcServices.get().invokeRpc(type, payload.getData());
} else {
if (namespace.toString().equals(SAL_REMOTE_NAMESPACE)) {
- response = invokeSalRemoteRpcSubscribeRPC(payload);
+ if (identifier.contains(CREATE_DATA_SUBSCR)) {
+ response = invokeSalRemoteRpcSubscribeRPC(payload);
+ } else if (identifier.contains(CREATE_NOTIFICATION_STREAM)) {
+ response = invokeSalRemoteRpcNotifiStrRPC(payload);
+ } else {
+ final String msg = "Not supported operation";
+ LOG.warn(msg);
+ throw new RestconfDocumentedException(msg, ErrorType.RPC, ErrorTag.OPERATION_NOT_SUPPORTED);
+ }
} else {
- response = broker.invokeRpc(type, payload.getData());
+ response = this.broker.invokeRpc(type, payload.getData());
}
- schemaContext = controllerContext.getGlobalSchema();
+ schemaContext = this.controllerContext.getGlobalSchema();
}
final DOMRpcResult result = checkRpcResponse(response);
RpcDefinition resultNodeSchema = null;
final NormalizedNode<?, ?> resultData = result.getResult();
- if (result != null && result.getResult() != null) {
+ if ((result != null) && (result.getResult() != null)) {
resultNodeSchema = (RpcDefinition) payload.getInstanceIdentifierContext().getSchemaNode();
}
}
try {
final DOMRpcResult retValue = response.get();
- if (retValue.getErrors() == null || retValue.getErrors().isEmpty()) {
+ if ((retValue.getErrors() == null) || retValue.getErrors().isEmpty()) {
return retValue;
}
LOG.debug("RpcError message", retValue.getErrors());
}
private static void validateInput(final SchemaNode inputSchema, final NormalizedNodeContext payload) {
- if (inputSchema != null && payload.getData() == null) {
+ if ((inputSchema != null) && (payload.getData() == null)) {
// expected a non null payload
throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
- } else if (inputSchema == null && payload.getData() != null) {
+ } else if ((inputSchema == null) && (payload.getData() != 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
- // those be
- // validate in a more central location inside MD-SAL core.
- // }
}
private CheckedFuture<DOMRpcResult, DOMRpcException> invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) {
}
final YangInstanceIdentifier pathIdentifier = ((YangInstanceIdentifier) pathValue);
- String streamName = null;
+ String streamName = (String) CREATE_DATA_SUBSCR;
if (!pathIdentifier.isEmpty()) {
- final String fullRestconfIdentifier = controllerContext.toFullRestconfIdentifier(pathIdentifier, null);
+ final String fullRestconfIdentifier = DATA_SUBSCR
+ + this.controllerContext.toFullRestconfIdentifier(pathIdentifier, null);
LogicalDatastoreType datastore = parseEnumTypeParameter(value, LogicalDatastoreType.class, DATASTORE_PARAM_NAME);
datastore = datastore == null ? DEFAULT_DATASTORE : datastore;
@Override
public NormalizedNodeContext invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) {
- if (noPayload != null && !CharMatcher.WHITESPACE.matchesAllOf(noPayload)) {
+ if ((noPayload != null) && !CharMatcher.WHITESPACE.matchesAllOf(noPayload)) {
throw new RestconfDocumentedException("Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
}
final SchemaContext schemaContext;
if (identifier.contains(ControllerContext.MOUNT)) {
// mounted RPC call - look up mount instance.
- final InstanceIdentifierContext<?> mountPointId = controllerContext.toMountPointIdentifier(identifier);
+ final InstanceIdentifierContext<?> mountPointId = this.controllerContext.toMountPointIdentifier(identifier);
mountPoint = mountPointId.getMountPoint();
schemaContext = mountPoint.getSchemaContext();
final int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
throw new RestconfDocumentedException(slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
} else {
identifierEncoded = identifier;
- schemaContext = controllerContext.getGlobalSchema();
+ schemaContext = this.controllerContext.getGlobalSchema();
}
- final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
+ final String identifierDecoded = this.controllerContext.urlPathArgDecode(identifierEncoded);
RpcDefinition rpc = null;
if (mountPoint == null) {
- rpc = controllerContext.getRpcDefinition(identifierDecoded, null);
+ rpc = this.controllerContext.getRpcDefinition(identifierDecoded, null);
} else {
rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded);
}
}
response = mountRpcServices.get().invokeRpc(rpc.getPath(), null);
} else {
- response = broker.invokeRpc(rpc.getPath(), null);
+ response = this.broker.invokeRpc(rpc.getPath(), null);
}
final DOMRpcResult result = checkRpcResponse(response);
@Override
public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) {
- final InstanceIdentifierContext<?> iiWithData = controllerContext.toInstanceIdentifier(identifier);
+ final InstanceIdentifierContext<?> iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
final DOMMountPoint mountPoint = iiWithData.getMountPoint();
NormalizedNode<?, ?> data = null;
final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
if (mountPoint != null) {
- data = broker.readConfigurationData(mountPoint, normalizedII);
+ data = this.broker.readConfigurationData(mountPoint, normalizedII);
} else {
- data = broker.readConfigurationData(normalizedII);
+ data = this.broker.readConfigurationData(normalizedII);
}
if(data == null) {
final String errMsg = "Request could not be completed because the relevant data model content does not exist ";
@Override
public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo uriInfo) {
- final InstanceIdentifierContext<?> iiWithData = controllerContext.toInstanceIdentifier(identifier);
+ final InstanceIdentifierContext<?> iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
final DOMMountPoint mountPoint = iiWithData.getMountPoint();
NormalizedNode<?, ?> data = null;
final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
if (mountPoint != null) {
- data = broker.readOperationalData(mountPoint, normalizedII);
+ data = this.broker.readOperationalData(mountPoint, normalizedII);
} else {
- data = broker.readOperationalData(normalizedII);
+ data = this.broker.readOperationalData(normalizedII);
}
if(data == null) {
final String errMsg = "Request could not be completed because the relevant data model content does not exist ";
* failures back to the client and forcing them to handle it via retry (and having to
* document the behavior).
*/
- int tries = 2;
+ PutResult result = null;
+ final TryOfPutData tryPutData = new TryOfPutData();
while(true) {
- try {
- if (mountPoint != null) {
- broker.commitConfigurationDataPut(mountPoint, normalizedII, payload.getData()).checkedGet();
- } else {
- broker.commitConfigurationDataPut(controllerContext.getGlobalSchema(), normalizedII, payload.getData()).checkedGet();
+ if (mountPoint != null) {
+
+ result = this.broker.commitMountPointDataPut(mountPoint, normalizedII, payload.getData());
+ } else {
+ result = this.broker.commitConfigurationDataPut(this.controllerContext.getGlobalSchema(), normalizedII,
+ payload.getData());
+ }
+ final CountDownLatch waiter = new CountDownLatch(1);
+ Futures.addCallback(result.getFutureOfPutData(), new FutureCallback<Void>() {
+
+ @Override
+ public void onSuccess(final Void result) {
+ handlingLoggerPut(null, tryPutData, identifier);
+ waiter.countDown();
}
+ @Override
+ public void onFailure(final Throwable t) {
+ waiter.countDown();
+ handlingLoggerPut(t, tryPutData, identifier);
+ }
+ });
+
+ try {
+ waiter.await();
+ } catch (final InterruptedException e) {
+ final String msg = "Problem while waiting for response";
+ LOG.warn(msg);
+ throw new RestconfDocumentedException(msg, e);
+ }
+
+ if(tryPutData.isDone()){
break;
- } catch (final TransactionCommitFailedException e) {
- if(e instanceof OptimisticLockFailedException) {
- if(--tries <= 0) {
- LOG.debug("Got OptimisticLockFailedException on last try - failing " + identifier);
- throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
- }
+ } else {
+ throw new RestconfDocumentedException("Problem while PUT operations");
+ }
+ }
- LOG.debug("Got OptimisticLockFailedException - trying again " + identifier);
- } else {
- LOG.debug("Update ConfigDataStore fail " + identifier, e);
- throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
+ return Response.status(result.getStatus()).build();
+ }
+
+ protected void handlingLoggerPut(final Throwable t, final TryOfPutData tryPutData, final String identifier) {
+ if (t != null) {
+ if (t instanceof OptimisticLockFailedException) {
+ if (tryPutData.countGet() <= 0) {
+ LOG.debug("Got OptimisticLockFailedException on last try - failing " + identifier);
+ throw new RestconfDocumentedException(t.getMessage(), t);
}
- } catch (Exception e) {
- final String errMsg = "Error updating data ";
- LOG.debug(errMsg + identifier, e);
- throw new RestconfDocumentedException(errMsg, e);
+ LOG.debug("Got OptimisticLockFailedException - trying again " + identifier);
+ tryPutData.countDown();
+ } else {
+ LOG.debug("Update ConfigDataStore fail " + identifier, t);
+ throw new RestconfDocumentedException(t.getMessage(), t);
}
+ } else {
+ LOG.trace("PUT Successful " + identifier);
+ tryPutData.done();
}
-
- return Response.status(Status.OK).build();
}
private static void validateTopLevelNodeName(final NormalizedNodeContext node,
final NormalizedNode<?, ?> data = payload.getData();
if (schemaNode instanceof ListSchemaNode) {
final List<QName> keyDefinitions = ((ListSchemaNode) schemaNode).getKeyDefinition();
- if (lastPathArgument instanceof NodeIdentifierWithPredicates && data instanceof MapEntryNode) {
+ if ((lastPathArgument instanceof NodeIdentifierWithPredicates) && (data instanceof MapEntryNode)) {
final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument).getKeyValues();
isEqualUriAndPayloadKeyValues(uriKeyValues, (MapEntryNode) data, keyDefinitions);
}
final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
final InstanceIdentifierContext<?> iiWithData = payload.getInstanceIdentifierContext();
final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
- try {
- if (mountPoint != null) {
- broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData()).checkedGet();
- } else {
- broker.commitConfigurationDataPost(controllerContext.getGlobalSchema(), normalizedII, payload.getData()).checkedGet();
+
+ CheckedFuture<Void, TransactionCommitFailedException> future;
+ if (mountPoint != null) {
+ future = this.broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData());
+ } else {
+ future = this.broker.commitConfigurationDataPost(this.controllerContext.getGlobalSchema(), normalizedII,
+ payload.getData());
+ }
+
+ final CountDownLatch waiter = new CountDownLatch(1);
+ Futures.addCallback(future, new FutureCallback<Void>() {
+
+ @Override
+ public void onSuccess(final Void result) {
+ handlerLoggerPost(null, uriInfo);
+ waiter.countDown();
}
- } catch(final RestconfDocumentedException e) {
- throw e;
- } catch (final Exception e) {
- final String errMsg = "Error creating data ";
- LOG.info(errMsg + (uriInfo != null ? uriInfo.getPath() : ""), e);
- throw new RestconfDocumentedException(errMsg, e);
+
+ @Override
+ public void onFailure(final Throwable t) {
+ waiter.countDown();
+ handlerLoggerPost(t, uriInfo);
+ }
+ });
+
+ try {
+ waiter.await();
+ } catch (final InterruptedException e) {
+ final String msg = "Problem while waiting for response";
+ LOG.warn(msg);
+ throw new RestconfDocumentedException(msg, e);
}
final ResponseBuilder responseBuilder = Response.status(Status.NO_CONTENT);
return responseBuilder.build();
}
+ protected void handlerLoggerPost(final Throwable t, final UriInfo uriInfo) {
+ if (t != null) {
+ final String errMsg = "Error creating data ";
+ LOG.warn(errMsg + (uriInfo != null ? uriInfo.getPath() : ""), t);
+ throw new RestconfDocumentedException(errMsg, t);
+ } else {
+ LOG.trace("Successfuly create data.");
+ }
+ }
+
private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint, final YangInstanceIdentifier normalizedII) {
if(uriInfo == null) {
// This is null if invoked internally
final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
uriBuilder.path("config");
try {
- uriBuilder.path(controllerContext.toFullRestconfIdentifier(normalizedII, mountPoint));
+ uriBuilder.path(this.controllerContext.toFullRestconfIdentifier(normalizedII, mountPoint));
} catch (final Exception e) {
LOG.info("Location for instance identifier" + normalizedII + "wasn't created", e);
return null;
@Override
public Response deleteConfigurationData(final String identifier) {
- final InstanceIdentifierContext<?> iiWithData = controllerContext.toInstanceIdentifier(identifier);
+ final InstanceIdentifierContext<?> iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
final DOMMountPoint mountPoint = iiWithData.getMountPoint();
final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
- try {
- if (mountPoint != null) {
- broker.commitConfigurationDataDelete(mountPoint, normalizedII);
- } else {
- broker.commitConfigurationDataDelete(normalizedII).get();
+ final CheckedFuture<Void, TransactionCommitFailedException> future;
+ if (mountPoint != null) {
+ future = this.broker.commitConfigurationDataDelete(mountPoint, normalizedII);
+ } else {
+ future = this.broker.commitConfigurationDataDelete(normalizedII);
+ }
+
+ final CountDownLatch waiter = new CountDownLatch(1);
+ Futures.addCallback(future, new FutureCallback<Void>() {
+
+ @Override
+ public void onSuccess(final Void result) {
+ handlerLoggerDelete(null);
+ waiter.countDown();
}
- } catch (final Exception e) {
- final Optional<Throwable> searchedException = Iterables.tryFind(Throwables.getCausalChain(e),
- Predicates.instanceOf(ModifiedNodeDoesNotExistException.class));
- if (searchedException.isPresent()) {
- throw new RestconfDocumentedException("Data specified for deleting doesn't exist.", ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
+
+ @Override
+ public void onFailure(final Throwable t) {
+ waiter.countDown();
+ handlerLoggerDelete(t);
}
- final String errMsg = "Error while deleting data";
- LOG.info(errMsg, e);
- throw new RestconfDocumentedException(errMsg, e);
+
+ });
+
+ try {
+ waiter.await();
+ } catch (final InterruptedException e) {
+ final String msg = "Problem while waiting for response";
+ LOG.warn(msg);
+ throw new RestconfDocumentedException(msg, e);
}
+
return Response.status(Status.OK).build();
}
+ protected void handlerLoggerDelete(final Throwable t) {
+ if (t != null) {
+ final String errMsg = "Error while deleting data";
+ LOG.info(errMsg, t);
+ throw new RestconfDocumentedException(errMsg, t);
+ } else {
+ LOG.trace("Successfuly delete data.");
+ }
+ }
+
/**
* Subscribes to some path in schema context (stream) to listen on changes on this stream.
*
*/
@Override
public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
+ if (identifier.contains(DATA_SUBSCR)) {
+ return dataSubs(identifier, uriInfo);
+ } else if (identifier.contains(NOTIFICATION_STREAM)) {
+ return notifStream(identifier, uriInfo);
+ }
+ final String msg = "Bad type of notification of sal-remote";
+ LOG.warn(msg);
+ throw new RestconfDocumentedException(msg);
+ }
+
+ /**
+ * Register notification listener by stream name
+ *
+ * @param identifier
+ * - stream name
+ * @param uriInfo
+ * - uriInfo
+ * @return {@link Response}
+ */
+ private Response notifStream(final String identifier, final UriInfo uriInfo) {
+ final String streamName = Notificator.createStreamNameFromUri(identifier);
+ if (Strings.isNullOrEmpty(streamName)) {
+ throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+ }
+ final List<NotificationListenerAdapter> listeners = Notificator.getNotificationListenerFor(streamName);
+ if ((listeners == null) || listeners.isEmpty()) {
+ throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL,
+ ErrorTag.UNKNOWN_ELEMENT);
+ }
+
+ for (final NotificationListenerAdapter listener : listeners) {
+ this.broker.registerToListenNotification(listener);
+ }
+
+ final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
+ int notificationPort = NOTIFICATION_PORT;
+ try {
+ final WebSocketServer webSocketServerInstance = WebSocketServer.getInstance();
+ notificationPort = webSocketServerInstance.getPort();
+ } catch (final NullPointerException e) {
+ WebSocketServer.createInstance(NOTIFICATION_PORT);
+ }
+ final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme("ws");
+ final URI uriToWebsocketServer = uriToWebsocketServerBuilder.replacePath(streamName).build();
+
+ return Response.status(Status.OK).location(uriToWebsocketServer).build();
+ }
+
+ /**
+ * Register data change listener by stream name
+ *
+ * @param identifier
+ * - stream name
+ * @param uriInfo
+ * - uri info
+ * @return {@link Response}
+ */
+ private Response dataSubs(final String identifier, final UriInfo uriInfo) {
final String streamName = Notificator.createStreamNameFromUri(identifier);
if (Strings.isNullOrEmpty(streamName)) {
throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
}
- broker.registerToListenDataChanges(datastore, scope, listener);
+ this.broker.registerToListenDataChanges(datastore, scope, listener);
final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
int notificationPort = NOTIFICATION_PORT;
}
@Override
- public PATCHStatusContext patchConfigurationData(String identifier, PATCHContext context, UriInfo uriInfo) {
+ public PATCHStatusContext patchConfigurationData(final String identifier, final PATCHContext context, final UriInfo uriInfo) {
if (context == null) {
throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
}
- return broker.patchConfigurationDataWithinTransaction(context, controllerContext.getGlobalSchema());
+
+ try {
+ return this.broker.patchConfigurationDataWithinTransaction(context,
+ this.controllerContext.getGlobalSchema());
+ } catch (final InterruptedException e) {
+ LOG.debug("Patch transaction failed", e);
+ throw new RestconfDocumentedException(e.getMessage());
+ }
}
@Override
- public PATCHStatusContext patchConfigurationData(PATCHContext context, @Context UriInfo uriInfo) {
+ public PATCHStatusContext patchConfigurationData(final PATCHContext context, @Context final UriInfo uriInfo) {
if (context == null) {
throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
}
- return broker.patchConfigurationDataWithinTransaction(context, controllerContext.getGlobalSchema());
+
+ try {
+ return this.broker.patchConfigurationDataWithinTransaction(context,
+ this.controllerContext.getGlobalSchema());
+ } catch (final InterruptedException e) {
+ LOG.debug("Patch transaction failed", e);
+ throw new RestconfDocumentedException(e.getMessage());
+ }
}
/**
* Load parameter for subscribing to stream from input composite node
*
- * @param compNode
+ * @param value
* contains value
* @return enum object if its string value is equal to {@code paramName}. In other cases null.
*/
return result;
}
- public BigInteger getOperationalReceived() {
- // TODO Auto-generated method stub
- return null;
- }
-
private MapNode makeModuleMapNode(final Set<Module> modules) {
Preconditions.checkNotNull(modules);
final Module restconfModule = getRestconfModule();
- final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+ final DataSchemaNode moduleSchemaNode = this.controllerContext.getRestconfModuleRestConfSchemaNode(
restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
return streamNodeValues.build();
}
+
+ /**
+ * Prepare stream for notification
+ *
+ * @param payload
+ * - contains list of qnames of notifications
+ * @return - checked future object
+ */
+ private CheckedFuture<DOMRpcResult, DOMRpcException> invokeSalRemoteRpcNotifiStrRPC(
+ final NormalizedNodeContext payload) {
+ final ContainerNode data = (ContainerNode) payload.getData();
+ LeafSetNode leafSet = null;
+ String outputType = "XML";
+ for (final DataContainerChild<? extends PathArgument, ?> dataChild : data.getValue()) {
+ if (dataChild instanceof LeafSetNode) {
+ leafSet = (LeafSetNode) dataChild;
+ } else if (dataChild instanceof AugmentationNode) {
+ outputType = (String) (((AugmentationNode) dataChild).getValue()).iterator().next().getValue();
+ }
+ }
+
+ final Collection<LeafSetEntryNode> entryNodes = leafSet.getValue();
+ final List<SchemaPath> paths = new ArrayList<>();
+ String streamName = CREATE_NOTIFICATION_STREAM + "/";
+
+ final Iterator<LeafSetEntryNode> iterator = entryNodes.iterator();
+ while (iterator.hasNext()) {
+ final QName valueQName = QName.create((String) iterator.next().getValue());
+ final Module module = ControllerContext.getInstance()
+ .findModuleByNamespace(valueQName.getModule().getNamespace());
+ Preconditions.checkNotNull(module, "Module for namespace " + valueQName.getModule().getNamespace()
+ + " does not exist");
+ NotificationDefinition notifiDef = null;
+ for (final NotificationDefinition notification : module.getNotifications()) {
+ if (notification.getQName().equals(valueQName)) {
+ notifiDef = notification;
+ break;
+ }
+ }
+ final String moduleName = module.getName();
+ Preconditions.checkNotNull(notifiDef,
+ "Notification " + valueQName + "doesn't exist in module " + moduleName);
+ paths.add(notifiDef.getPath());
+ streamName = streamName + moduleName + ":" + valueQName.getLocalName();
+ if (iterator.hasNext()) {
+ streamName = streamName + ",";
+ }
+ }
+
+ final QName rpcQName = payload.getInstanceIdentifierContext().getSchemaNode().getQName();
+ final QName outputQname = QName.create(rpcQName, "output");
+ final QName streamNameQname = QName.create(rpcQName, "notification-stream-identifier");
+
+ final ContainerNode output = ImmutableContainerNodeBuilder.create()
+ .withNodeIdentifier(new NodeIdentifier(outputQname))
+ .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build();
+
+ if (!Notificator.existNotificationListenerFor(streamName)) {
+ Notificator.createNotificationListener(paths, streamName, outputType);
+ }
+
+ final DOMRpcResult defaultDOMRpcResult = new DefaultDOMRpcResult(output);
+
+ return Futures.immediateCheckedFuture(defaultDOMRpcResult);
+ }
+
+ private class TryOfPutData {
+ int tries = 2;
+ boolean done = false;
+
+ void countDown() {
+ this.tries--;
+ }
+
+ void done() {
+ this.done = true;
+ }
+
+ boolean isDone() {
+ return this.done;
+ }
+ int countGet() {
+ return this.tries;
+ }
+ }
}