X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=restconf%2Fsal-rest-connector%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fnetconf%2Fsal%2Frestconf%2Fimpl%2FRestconfImpl.java;h=19a130fafe6f921aecda5c6f30ea8e7480dd00aa;hb=4281bb297a90bc3c71a50d610147727ff67ea9ea;hp=be2e5edd6e29978c26e00a5a70227ead01e76469;hpb=1582427eb81ac3250f2db5c041838d11432b45a0;p=netconf.git diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java index be2e5edd6e..19a130fafe 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015 Brocade Communication Systems, Inc., Cisco Systems, Inc. and others. All rights reserved. + * Copyright (c) 2014 - 2016 Brocade Communication Systems, Inc., Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, @@ -8,34 +8,39 @@ 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.base.Strings; +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableSet; 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.net.URISyntaxException; import java.text.ParseException; -import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; 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; @@ -62,26 +67,34 @@ 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.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime; +import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.QNameModule; +import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; 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.data.impl.schema.builder.api.DataContainerNodeAttrBuilder; 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.impl.ImmutableContainerNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; @@ -95,6 +108,7 @@ 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.SimpleSchemaContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -109,14 +123,8 @@ public class RestconfImpl implements RestconfService { private static final int CHAR_NOT_FOUND = -1; - private static final SimpleDateFormat REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); - private static final String SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote"; - private BrokerFacade broker; - - private ControllerContext controllerContext; - private static final Logger LOG = LoggerFactory.getLogger(RestconfImpl.class); private static final DataChangeScope DEFAULT_SCOPE = DataChangeScope.BASE; @@ -129,15 +137,30 @@ public class RestconfImpl implements RestconfService { private static final String SCOPE_PARAM_NAME = "scope"; + private static final String OUTPUT_TYPE_PARAM_NAME = "notification-output-type"; + private static final String NETCONF_BASE = "urn:ietf:params:xml:ns:netconf:base:1.0"; private static final String NETCONF_BASE_PAYLOAD_NAME = "data"; - private static final QName NETCONF_BASE_QNAME; + private static final QName NETCONF_BASE_QNAME = QName.create(QNameModule.create(URI.create(NETCONF_BASE), null), + NETCONF_BASE_PAYLOAD_NAME).intern(); private static final QNameModule SAL_REMOTE_AUGMENT; - private static final YangInstanceIdentifier.AugmentationIdentifier SAL_REMOTE_AUG_IDENTIFIER; + static { + try { + SAL_REMOTE_AUGMENT = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT, + SimpleDateFormatUtil.getRevisionFormat().parse("2014-07-08")); + } catch (final ParseException e) { + throw new ExceptionInInitializerError(e); + } + } + + private static final AugmentationIdentifier SAL_REMOTE_AUG_IDENTIFIER = + new AugmentationIdentifier(ImmutableSet.of( + QName.create(SAL_REMOTE_AUGMENT, "scope"), QName.create(SAL_REMOTE_AUGMENT, "datastore"), + QName.create(SAL_REMOTE_AUGMENT, "notification-output-type"))); public static final CharSequence DATA_SUBSCR = "data-change-event-subscription"; private static final CharSequence CREATE_DATA_SUBSCR = "create-" + DATA_SUBSCR; @@ -145,23 +168,19 @@ public class RestconfImpl implements RestconfService { 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"); - NETCONF_BASE_QNAME = QName.create(QNameModule.create(new URI(NETCONF_BASE), null), NETCONF_BASE_PAYLOAD_NAME ); - SAL_REMOTE_AUGMENT = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT, - eventSubscriptionAugRevision); - SAL_REMOTE_AUG_IDENTIFIER = new YangInstanceIdentifier.AugmentationIdentifier(Sets.newHashSet(QName.create(SAL_REMOTE_AUGMENT, "scope"), - QName.create(SAL_REMOTE_AUGMENT, "datastore"))); - } catch (final ParseException e) { - final String errMsg = "It wasn't possible to convert revision date of sal-remote-augment to date"; - LOG.debug(errMsg); - throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED); - } catch (final URISyntaxException e) { - final String errMsg = "It wasn't possible to create instance of URI class with "+NETCONF_BASE+" URI"; - throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED); - } - } + private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder() + .appendValue(ChronoField.YEAR, 4).appendLiteral('-') + .appendValue(ChronoField.MONTH_OF_YEAR, 2).appendLiteral('-') + .appendValue(ChronoField.DAY_OF_MONTH, 2).appendLiteral('T') + .appendValue(ChronoField.HOUR_OF_DAY, 2).appendLiteral(':') + .appendValue(ChronoField.MINUTE_OF_HOUR, 2).appendLiteral(':') + .appendValue(ChronoField.SECOND_OF_MINUTE, 2) + .appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true) + .appendOffset("+HH:MM", "Z").toFormatter(); + + private BrokerFacade broker; + + private ControllerContext controllerContext; public void setBroker(final BrokerFacade broker) { this.broker = broker; @@ -194,25 +213,25 @@ public class RestconfImpl implements RestconfService { Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode); moduleContainerBuilder.withChild(allModuleMap); - return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, modulesSchemaNode, - null, schemaContext), moduleContainerBuilder.build(), - QueryParametersParser.parseWriterParameters(uriInfo)); + return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, modulesSchemaNode, null, schemaContext), + moduleContainerBuilder.build(), QueryParametersParser.parseWriterParameters(uriInfo)); } /** - * Valid only for mount point + * Valid only for mount point. */ @Override public NormalizedNodeContext getModules(final String identifier, final UriInfo uriInfo) { Preconditions.checkNotNull(identifier); - if ( ! identifier.contains(ControllerContext.MOUNT)) { + if (!identifier.contains(ControllerContext.MOUNT)) { final String errMsg = "URI has bad format. If modules behind mount point should be showed," + " URI has to end with " + ControllerContext.MOUNT; LOG.debug(errMsg + " for " + identifier); throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); } - final InstanceIdentifierContext mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); + final InstanceIdentifierContext mountPointIdentifier = + this.controllerContext.toMountPointIdentifier(identifier); final DOMMountPoint mountPoint = mountPointIdentifier.getMountPoint(); final Set modules = this.controllerContext.getAllModules(mountPoint); final MapNode mountPointModulesMap = makeModuleMapNode(modules); @@ -226,9 +245,10 @@ public class RestconfImpl implements RestconfService { Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode); moduleContainerBuilder.withChild(mountPointModulesMap); - return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, modulesSchemaNode, - mountPoint, this.controllerContext.getGlobalSchema()), moduleContainerBuilder.build(), - QueryParametersParser.parseWriterParameters(uriInfo)); + return new NormalizedNodeContext( + new InstanceIdentifierContext<>(null, modulesSchemaNode, mountPoint, + this.controllerContext.getGlobalSchema()), + moduleContainerBuilder.build(), QueryParametersParser.parseWriterParameters(uriInfo)); } @Override @@ -239,7 +259,8 @@ public class RestconfImpl implements RestconfService { DOMMountPoint mountPoint = null; final SchemaContext schemaContext; if (identifier.contains(ControllerContext.MOUNT)) { - final InstanceIdentifierContext mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); + final InstanceIdentifierContext mountPointIdentifier = + this.controllerContext.toMountPointIdentifier(identifier); mountPoint = mountPointIdentifier.getMountPoint(); module = this.controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision); schemaContext = mountPoint.getSchemaContext(); @@ -249,8 +270,8 @@ public class RestconfImpl implements RestconfService { } if (module == null) { - final String errMsg = "Module with name '" + moduleNameAndRevision.getLocalName() - + "' and revision '" + moduleNameAndRevision.getRevision() + "' was not found."; + final String errMsg = "Module with name '" + moduleNameAndRevision.getLocalName() + "' and revision '" + + moduleNameAndRevision.getRevision() + "' was not found."; LOG.debug(errMsg); throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT); } @@ -259,12 +280,13 @@ public class RestconfImpl implements RestconfService { final Set modules = Collections.singleton(module); final MapNode moduleMap = makeModuleMapNode(modules); - final DataSchemaNode moduleSchemaNode = this.controllerContext.getRestconfModuleRestConfSchemaNode( - restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE); + final DataSchemaNode moduleSchemaNode = this.controllerContext + .getRestconfModuleRestConfSchemaNode(restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE); Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode); - return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, moduleSchemaNode, mountPoint, - schemaContext), moduleMap, QueryParametersParser.parseWriterParameters(uriInfo)); + return new NormalizedNodeContext( + new InstanceIdentifierContext<>(null, moduleSchemaNode, mountPoint, schemaContext), moduleMap, + QueryParametersParser.parseWriterParameters(uriInfo)); } @Override @@ -272,12 +294,12 @@ public class RestconfImpl implements RestconfService { final SchemaContext schemaContext = this.controllerContext.getGlobalSchema(); final Set availableStreams = Notificator.getStreamNames(); final Module restconfModule = getRestconfModule(); - final DataSchemaNode streamSchemaNode = this.controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule, - Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE); + final DataSchemaNode streamSchemaNode = this.controllerContext + .getRestconfModuleRestConfSchemaNode(restconfModule, Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE); Preconditions.checkState(streamSchemaNode instanceof ListSchemaNode); - final CollectionNodeBuilder listStreamsBuilder = Builders - .mapBuilder((ListSchemaNode) streamSchemaNode); + final CollectionNodeBuilder listStreamsBuilder = + Builders.mapBuilder((ListSchemaNode) streamSchemaNode); for (final String streamName : availableStreams) { listStreamsBuilder.withChild(toStreamEntryNode(streamName, streamSchemaNode)); @@ -291,9 +313,9 @@ public class RestconfImpl implements RestconfService { Builders.containerBuilder((ContainerSchemaNode) streamsContainerSchemaNode); streamsContainerBuilder.withChild(listStreamsBuilder.build()); - - return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, streamsContainerSchemaNode, null, - schemaContext), streamsContainerBuilder.build(), QueryParametersParser.parseWriterParameters(uriInfo)); + return new NormalizedNodeContext( + new InstanceIdentifierContext<>(null, streamsContainerSchemaNode, null, schemaContext), + streamsContainerBuilder.build(), QueryParametersParser.parseWriterParameters(uriInfo)); } @Override @@ -307,22 +329,66 @@ public class RestconfImpl implements RestconfService { Set modules = null; DOMMountPoint mountPoint = null; if (identifier.contains(ControllerContext.MOUNT)) { - final InstanceIdentifierContext mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); + final InstanceIdentifierContext mountPointIdentifier = + this.controllerContext.toMountPointIdentifier(identifier); mountPoint = mountPointIdentifier.getMountPoint(); 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 "; + final String errMsg = + "URI has bad format. If operations behind mount point should be showed, URI has to " + "end with "; LOG.debug(errMsg + ControllerContext.MOUNT + " for " + identifier); - throw new RestconfDocumentedException(errMsg + ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + throw new RestconfDocumentedException(errMsg + ControllerContext.MOUNT, ErrorType.PROTOCOL, + ErrorTag.INVALID_VALUE); } return operationsFromModulesToNormalizedContext(modules, mountPoint); } - private NormalizedNodeContext operationsFromModulesToNormalizedContext(final Set modules, + /** + * 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 static NormalizedNodeContext operationsFromModulesToNormalizedContext(final Set modules, final DOMMountPoint mountPoint) { - throw new UnsupportedOperationException(); + + final Collection neededModules = new ArrayList<>(modules.size()); + final ArrayList fakeRpcSchema = new ArrayList<>(); + + for (final Module m : modules) { + final Set rpcs = m.getRpcs(); + if (!rpcs.isEmpty()) { + neededModules.add(m); + + fakeRpcSchema.ensureCapacity(fakeRpcSchema.size() + rpcs.size()); + rpcs.forEach(rpc -> fakeRpcSchema.add(new FakeLeafSchemaNode(rpc.getQName()))); + } + } + + final ContainerSchemaNode fakeCont = new FakeContainerSchemaNode(fakeRpcSchema); + final DataContainerNodeAttrBuilder containerBuilder = + Builders.containerBuilder(fakeCont); + + for (final LeafSchemaNode leaf : fakeRpcSchema) { + containerBuilder.withChild(Builders.leafBuilder(leaf).build()); + } + + final Collection fakeModules = new ArrayList<>(neededModules.size() + 1); + neededModules.forEach(imp -> fakeModules.add(new FakeImportedModule(imp))); + fakeModules.add(new FakeRestconfModule(neededModules, fakeCont)); + + final SchemaContext fakeSchemaCtx = SimpleSchemaContext.forModules(ImmutableSet.copyOf(fakeModules)); + final InstanceIdentifierContext instanceIdentifierContext = + new InstanceIdentifierContext<>(null, fakeCont, mountPoint, fakeSchemaCtx); + return new NormalizedNodeContext(instanceIdentifierContext, containerBuilder.build()); } private Module getRestconfModule() { @@ -347,7 +413,7 @@ public class RestconfImpl implements RestconfService { final Splitter splitter = Splitter.on("/").omitEmptyStrings(); final Iterable split = splitter.split(moduleNameAndRevision); - final List pathArgs = Lists. newArrayList(split); + final List pathArgs = Lists.newArrayList(split); if (pathArgs.size() < 2) { LOG.debug("URI has bad format. It should be \'moduleName/yyyy-MM-dd\' " + identifier); throw new RestconfDocumentedException( @@ -358,8 +424,7 @@ public class RestconfImpl implements RestconfService { try { final String moduleName = pathArgs.get(0); final String revision = pathArgs.get(1); - final Date moduleRevision = REVISION_FORMAT.parse(revision); - return QName.create(null, moduleRevision, moduleName); + return QName.create(null, SimpleDateFormatUtil.getRevisionFormat().parse(revision), moduleName); } catch (final ParseException e) { LOG.debug("URI has bad format. It should be \'moduleName/yyyy-MM-dd\' " + identifier); throw new RestconfDocumentedException("URI has bad format. It should be \'moduleName/yyyy-MM-dd\'", @@ -373,15 +438,17 @@ public class RestconfImpl implements RestconfService { } @Override - public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) { + public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, + final UriInfo uriInfo) { final SchemaPath type = payload.getInstanceIdentifierContext().getSchemaNode().getPath(); final URI namespace = payload.getInstanceIdentifierContext().getSchemaNode().getQName().getNamespace(); final CheckedFuture response; final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint(); final SchemaContext schemaContext; + if (mountPoint != null) { final Optional mountRpcServices = mountPoint.getService(DOMRpcService.class); - if ( ! mountRpcServices.isPresent()) { + if (!mountRpcServices.isPresent()) { LOG.debug("Error: Rpc service is missing."); throw new RestconfDocumentedException("Rpc service is missing."); } @@ -408,13 +475,81 @@ public class RestconfImpl implements RestconfService { 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(); } - return new NormalizedNodeContext(new InstanceIdentifierContext(null, - resultNodeSchema, mountPoint, schemaContext), resultData, - QueryParametersParser.parseWriterParameters(uriInfo)); + 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); + } + + String identifierEncoded = null; + DOMMountPoint mountPoint = null; + final SchemaContext schemaContext; + if (identifier.contains(ControllerContext.MOUNT)) { + // mounted RPC call - look up mount instance. + final InstanceIdentifierContext mountPointId = this.controllerContext.toMountPointIdentifier(identifier); + mountPoint = mountPointId.getMountPoint(); + schemaContext = mountPoint.getSchemaContext(); + final int startOfRemoteRpcName = + identifier.lastIndexOf(ControllerContext.MOUNT) + ControllerContext.MOUNT.length() + 1; + final String remoteRpcName = identifier.substring(startOfRemoteRpcName); + identifierEncoded = remoteRpcName; + + } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) { + final String slashErrorMsg = String.format( + "Identifier %n%s%ncan\'t contain slash " + + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.", + identifier); + LOG.debug(slashErrorMsg); + throw new RestconfDocumentedException(slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + } else { + identifierEncoded = identifier; + schemaContext = this.controllerContext.getGlobalSchema(); + } + + final String identifierDecoded = this.controllerContext.urlPathArgDecode(identifierEncoded); + + RpcDefinition rpc = null; + if (mountPoint == null) { + rpc = this.controllerContext.getRpcDefinition(identifierDecoded, null); + } else { + rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded); + } + + if (rpc == null) { + LOG.debug("RPC " + identifierDecoded + " does not exist."); + throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT); + } + + if (!rpc.getInput().getChildNodes().isEmpty()) { + LOG.debug("RPC " + rpc + " does not need input value."); + // FIXME : find a correct Error from specification + throw new IllegalStateException("RPC " + rpc + " does'n need input value!"); + } + + final CheckedFuture response; + if (mountPoint != null) { + final Optional mountRpcServices = mountPoint.getService(DOMRpcService.class); + if (!mountRpcServices.isPresent()) { + throw new RestconfDocumentedException("Rpc service is missing."); + } + response = mountRpcServices.get().invokeRpc(rpc.getPath(), null); + } else { + response = this.broker.invokeRpc(rpc.getPath(), null); + } + + final DOMRpcResult result = checkRpcResponse(response); + + return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, rpc, mountPoint, schemaContext), + result.getResult(), QueryParametersParser.parseWriterParameters(uriInfo)); } private static DOMRpcResult checkRpcResponse(final CheckedFuture response) { @@ -423,7 +558,7 @@ public class RestconfImpl implements RestconfService { } 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()); @@ -444,11 +579,14 @@ public class RestconfImpl implements RestconfService { throw new RestconfDocumentedException(cause.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); } else if (cause instanceof DOMRpcImplementationNotAvailableException) { - throw new RestconfDocumentedException(cause.getMessage(), ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED); + throw new RestconfDocumentedException(cause.getMessage(), ErrorType.APPLICATION, + ErrorTag.OPERATION_NOT_SUPPORTED); } - throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",cause); + throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.", + cause); } else { - throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",e); + throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.", + e); } } catch (final CancellationException e) { final String errMsg = "The operation was cancelled while executing."; @@ -458,22 +596,17 @@ public class RestconfImpl implements RestconfService { } 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 invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) { + private CheckedFuture + invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) { final ContainerNode value = (ContainerNode) payload.getData(); final QName rpcQName = payload.getInstanceIdentifierContext().getSchemaNode().getQName(); final Optional> path = value.getChild(new NodeIdentifier( @@ -486,20 +619,25 @@ public class RestconfImpl implements RestconfService { throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED); } - final YangInstanceIdentifier pathIdentifier = ((YangInstanceIdentifier) pathValue); + final YangInstanceIdentifier pathIdentifier = (YangInstanceIdentifier) pathValue; String streamName = (String) CREATE_DATA_SUBSCR; + NotificationOutputType outputType = null; if (!pathIdentifier.isEmpty()) { - final String fullRestconfIdentifier = DATA_SUBSCR - + this.controllerContext.toFullRestconfIdentifier(pathIdentifier, null); + final String fullRestconfIdentifier = + DATA_SUBSCR + this.controllerContext.toFullRestconfIdentifier(pathIdentifier, null); - LogicalDatastoreType datastore = parseEnumTypeParameter(value, LogicalDatastoreType.class, DATASTORE_PARAM_NAME); + LogicalDatastoreType datastore = + parseEnumTypeParameter(value, LogicalDatastoreType.class, DATASTORE_PARAM_NAME); datastore = datastore == null ? DEFAULT_DATASTORE : datastore; DataChangeScope scope = parseEnumTypeParameter(value, DataChangeScope.class, SCOPE_PARAM_NAME); scope = scope == null ? DEFAULT_SCOPE : scope; - streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier + "/datastore=" + datastore - + "/scope=" + scope); + outputType = parseEnumTypeParameter(value, NotificationOutputType.class, OUTPUT_TYPE_PARAM_NAME); + outputType = outputType == null ? NotificationOutputType.XML : outputType; + + streamName = Notificator + .createStreamNameFromUri(fullRestconfIdentifier + "/datastore=" + datastore + "/scope=" + scope); } if (Strings.isNullOrEmpty(streamName)) { @@ -511,11 +649,12 @@ public class RestconfImpl implements RestconfService { final QName outputQname = QName.create(rpcQName, "output"); final QName streamNameQname = QName.create(rpcQName, "stream-name"); - final ContainerNode output = ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(outputQname)) - .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build(); + final ContainerNode output = + ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(outputQname)) + .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build(); if (!Notificator.existListenerFor(streamName)) { - Notificator.createListener(pathIdentifier, streamName); + Notificator.createListener(pathIdentifier, streamName, outputType); } final DOMRpcResult defaultDOMRpcResult = new DefaultDOMRpcResult(output); @@ -523,72 +662,6 @@ public class RestconfImpl implements RestconfService { return Futures.immediateCheckedFuture(defaultDOMRpcResult); } - @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); - } - - String identifierEncoded = null; - DOMMountPoint mountPoint = null; - final SchemaContext schemaContext; - if (identifier.contains(ControllerContext.MOUNT)) { - // mounted RPC call - look up mount instance. - final InstanceIdentifierContext mountPointId = this.controllerContext.toMountPointIdentifier(identifier); - mountPoint = mountPointId.getMountPoint(); - schemaContext = mountPoint.getSchemaContext(); - final int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT) - + ControllerContext.MOUNT.length() + 1; - final String remoteRpcName = identifier.substring(startOfRemoteRpcName); - identifierEncoded = remoteRpcName; - - } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) { - final String slashErrorMsg = String.format("Identifier %n%s%ncan\'t contain slash " - + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.", identifier); - LOG.debug(slashErrorMsg); - throw new RestconfDocumentedException(slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); - } else { - identifierEncoded = identifier; - schemaContext = this.controllerContext.getGlobalSchema(); - } - - final String identifierDecoded = this.controllerContext.urlPathArgDecode(identifierEncoded); - - RpcDefinition rpc = null; - if (mountPoint == null) { - rpc = this.controllerContext.getRpcDefinition(identifierDecoded, null); - } else { - rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded); - } - - if (rpc == null) { - LOG.debug("RPC " + identifierDecoded + " does not exist."); - throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT); - } - - if (rpc.getInput() != null) { - LOG.debug("RPC " + rpc + " does not need input value."); - // FIXME : find a correct Error from specification - throw new IllegalStateException("RPC " + rpc + " does'n need input value!"); - } - - final CheckedFuture response; - if (mountPoint != null) { - final Optional mountRpcServices = mountPoint.getService(DOMRpcService.class); - if ( ! mountRpcServices.isPresent()) { - throw new RestconfDocumentedException("Rpc service is missing."); - } - response = mountRpcServices.get().invokeRpc(rpc.getPath(), null); - } else { - response = this.broker.invokeRpc(rpc.getPath(), null); - } - - final DOMRpcResult result = checkRpcResponse(response); - - return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, rpc, mountPoint, schemaContext), - result.getResult(), QueryParametersParser.parseWriterParameters(uriInfo)); - } - private static RpcDefinition findRpc(final SchemaContext schemaContext, final String identifierDecoded) { final String[] splittedIdentifier = identifierDecoded.split(":"); if (splittedIdentifier.length != 2) { @@ -610,21 +683,52 @@ public class RestconfImpl implements RestconfService { @Override public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) { + boolean withDefaUsed = false; + String withDefa = null; + + for (final Entry> entry : uriInfo.getQueryParameters().entrySet()) { + switch (entry.getKey()) { + case "with-defaults": + if (!withDefaUsed) { + withDefaUsed = true; + withDefa = entry.getValue().iterator().next(); + } else { + throw new RestconfDocumentedException("With-defaults parameter can be used only once."); + } + break; + default: + LOG.info("Unknown key : {}.", entry.getKey()); + break; + } + } + boolean tagged = false; + if (withDefaUsed) { + if (withDefa.equals("report-all-tagged")) { + tagged = true; + withDefa = null; + } + if (withDefa.equals("report-all")) { + withDefa = null; + } + } + final InstanceIdentifierContext iiWithData = this.controllerContext.toInstanceIdentifier(identifier); final DOMMountPoint mountPoint = iiWithData.getMountPoint(); NormalizedNode data = null; final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier(); if (mountPoint != null) { - data = this.broker.readConfigurationData(mountPoint, normalizedII); + data = this.broker.readConfigurationData(mountPoint, normalizedII, withDefa); } else { - data = this.broker.readConfigurationData(normalizedII); + data = this.broker.readConfigurationData(normalizedII, withDefa); } - if(data == null) { - final String errMsg = "Request could not be completed because the relevant data model content does not exist "; + if (data == null) { + final String errMsg = + "Request could not be completed because the relevant data model content does not exist "; LOG.debug(errMsg + identifier); throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.DATA_MISSING); } - return new NormalizedNodeContext(iiWithData, data, QueryParametersParser.parseWriterParameters(uriInfo)); + return new NormalizedNodeContext(iiWithData, data, + QueryParametersParser.parseWriterParameters(uriInfo, tagged)); } @Override @@ -638,17 +742,56 @@ public class RestconfImpl implements RestconfService { } else { 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 "; + if (data == null) { + final String errMsg = + "Request could not be completed because the relevant data model content does not exist "; LOG.debug(errMsg + identifier); - throw new RestconfDocumentedException(errMsg , ErrorType.APPLICATION, ErrorTag.DATA_MISSING); + throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.DATA_MISSING); } return new NormalizedNodeContext(iiWithData, data, QueryParametersParser.parseWriterParameters(uriInfo)); } @Override - public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload) { + public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload, + final UriInfo uriInfo) { + boolean insertUsed = false; + boolean pointUsed = false; + String insert = null; + String point = null; + + for (final Entry> entry : uriInfo.getQueryParameters().entrySet()) { + switch (entry.getKey()) { + case "insert": + if (!insertUsed) { + insertUsed = true; + insert = entry.getValue().iterator().next(); + } else { + throw new RestconfDocumentedException("Insert parameter can be used only once."); + } + break; + case "point": + if (!pointUsed) { + pointUsed = true; + point = entry.getValue().iterator().next(); + } else { + throw new RestconfDocumentedException("Point parameter can be used only once."); + } + break; + default: + throw new RestconfDocumentedException("Bad parameter for post: " + entry.getKey()); + } + } + + if (pointUsed && !insertUsed) { + throw new RestconfDocumentedException("Point parameter can't be used without Insert parameter."); + } + if (pointUsed && (insert.equals("first") || insert.equals("last"))) { + throw new RestconfDocumentedException( + "Point parameter can be used only with 'after' or 'before' values of Insert parameter."); + } + Preconditions.checkNotNull(identifier); + final InstanceIdentifierContext iiWithData = payload.getInstanceIdentifierContext(); validateInput(iiWithData.getSchemaNode(), payload); @@ -659,78 +802,49 @@ public class RestconfImpl implements RestconfService { final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier(); /* - * There is a small window where another write transaction could be updating the same data - * simultaneously and we get an OptimisticLockFailedException. This error is likely - * transient and The WriteTransaction#submit API docs state that a retry will likely - * succeed. So we'll try again if that scenario occurs. If it fails a third time then it - * probably will never succeed so we'll fail in that case. + * There is a small window where another write transaction could be + * updating the same data simultaneously and we get an + * OptimisticLockFailedException. This error is likely transient and The + * WriteTransaction#submit API docs state that a retry will likely + * succeed. So we'll try again if that scenario occurs. If it fails a + * third time then it probably will never succeed so we'll fail in that + * case. * - * By retrying we're attempting to hide the internal implementation of the data store and - * how it handles concurrent updates from the restconf client. The client has instructed us - * to put the data and we should make every effort to do so without pushing optimistic lock - * failures back to the client and forcing them to handle it via retry (and having to - * document the behavior). + * By retrying we're attempting to hide the internal implementation of + * the data store and how it handles concurrent updates from the + * restconf client. The client has instructed us to put the data and we + * should make every effort to do so without pushing optimistic lock + * failures back to the client and forcing them to handle it via retry + * (and having to document the behavior). */ PutResult result = null; - final TryOfPutData tryPutData = new TryOfPutData(); - while(true) { + int tries = 2; + while (true) { if (mountPoint != null) { - result = this.broker.commitMountPointDataPut(mountPoint, normalizedII, payload.getData()); + result = this.broker.commitMountPointDataPut(mountPoint, normalizedII, payload.getData(), insert, + point); } else { result = this.broker.commitConfigurationDataPut(this.controllerContext.getGlobalSchema(), normalizedII, - payload.getData()); + payload.getData(), insert, point); } - final CountDownLatch waiter = new CountDownLatch(1); - Futures.addCallback(result.getFutureOfPutData(), new FutureCallback() { - - @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; - } else { - throw new RestconfDocumentedException("Problem while PUT operations"); - } - } - - return Response.status(result.getStatus()).build(); - } + result.getFutureOfPutData().checkedGet(); + return Response.status(result.getStatus()).build(); + } 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()); + } - 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); + LOG.debug("Got OptimisticLockFailedException - trying again " + identifier); + } else { + LOG.debug("Update failed for " + identifier, e); + throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList()); } - 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(); } } @@ -739,26 +853,27 @@ public class RestconfImpl implements RestconfService { final String payloadName = node.getData().getNodeType().getLocalName(); - //no arguments + // no arguments if (identifier.isEmpty()) { - //no "data" payload + // no "data" payload if (!node.getData().getNodeType().equals(NETCONF_BASE_QNAME)) { throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); } - //any arguments + // any arguments } else { final String identifierName = identifier.getLastPathArgument().getNodeType().getLocalName(); if (!payloadName.equals(identifierName)) { - throw new RestconfDocumentedException("Payload name (" + payloadName - + ") is different from identifier name (" + identifierName + ")", ErrorType.PROTOCOL, - ErrorTag.MALFORMED_MESSAGE); + throw new RestconfDocumentedException( + "Payload name (" + payloadName + ") is different from identifier name (" + identifierName + ")", + ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); } } } /** - * Validates whether keys in {@code payload} are equal to values of keys in {@code iiWithData} for list schema node + * 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 @@ -772,15 +887,17 @@ public class RestconfImpl implements RestconfService { final NormalizedNode data = payload.getData(); if (schemaNode instanceof ListSchemaNode) { final List keyDefinitions = ((ListSchemaNode) schemaNode).getKeyDefinition(); - if ((lastPathArgument instanceof NodeIdentifierWithPredicates) && (data instanceof MapEntryNode)) { - final Map uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument).getKeyValues(); + if (lastPathArgument instanceof NodeIdentifierWithPredicates && data instanceof MapEntryNode) { + final Map uriKeyValues = + ((NodeIdentifierWithPredicates) lastPathArgument).getKeyValues(); isEqualUriAndPayloadKeyValues(uriKeyValues, (MapEntryNode) data, keyDefinitions); } } } - private static void isEqualUriAndPayloadKeyValues(final Map uriKeyValues, - final MapEntryNode payload, final List keyDefinitions) { + @VisibleForTesting + public static void isEqualUriAndPayloadKeyValues(final Map uriKeyValues, final MapEntryNode payload, + final List keyDefinitions) { final Map mutableCopyUriKeyValues = Maps.newHashMap(uriKeyValues); for (final QName keyDefinition : keyDefinitions) { @@ -791,48 +908,19 @@ public class RestconfImpl implements RestconfService { final Object dataKeyValue = payload.getIdentifier().getKeyValues().get(keyDefinition); - if ( ! uriKeyValue.equals(dataKeyValue)) { - final String errMsg = "The value '" + uriKeyValue + "' for key '" + keyDefinition.getLocalName() + - "' specified in the URI doesn't match the value '" + dataKeyValue + "' specified in the message body. "; + if (!Objects.deepEquals(uriKeyValue, dataKeyValue)) { + final String errMsg = "The value '" + uriKeyValue + "' for key '" + keyDefinition.getLocalName() + + "' specified in the URI doesn't match the value '" + dataKeyValue + + "' specified in the message body. "; throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); } } } @Override - public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) { - return createConfigurationData(payload, uriInfo); - } - - // FIXME create RestconfIdetifierHelper and move this method there - private static YangInstanceIdentifier checkConsistencyOfNormalizedNodeContext(final NormalizedNodeContext payload) { - Preconditions.checkArgument(payload != null); - Preconditions.checkArgument(payload.getData() != null); - Preconditions.checkArgument(payload.getData().getNodeType() != null); - Preconditions.checkArgument(payload.getInstanceIdentifierContext() != null); - Preconditions.checkArgument(payload.getInstanceIdentifierContext().getInstanceIdentifier() != null); - - final QName payloadNodeQname = payload.getData().getNodeType(); - final YangInstanceIdentifier yangIdent = payload.getInstanceIdentifierContext().getInstanceIdentifier(); - if (payloadNodeQname.compareTo(yangIdent.getLastPathArgument().getNodeType()) > 0) { - return yangIdent; - } - final InstanceIdentifierContext parentContext = payload.getInstanceIdentifierContext(); - final SchemaNode parentSchemaNode = parentContext.getSchemaNode(); - if(parentSchemaNode instanceof DataNodeContainer) { - final DataNodeContainer cast = (DataNodeContainer) parentSchemaNode; - for (final DataSchemaNode child : cast.getChildNodes()) { - if (payloadNodeQname.compareTo(child.getQName()) == 0) { - return YangInstanceIdentifier.builder(yangIdent).node(child.getQName()).build(); - } - } - } - if (parentSchemaNode instanceof RpcDefinition) { - return yangIdent; - } - final String errMsg = "Error parsing input: DataSchemaNode has not children "; - LOG.info(errMsg + yangIdent); - throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); + public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, + final UriInfo uriInfo) { + return createConfigurationData(payload, uriInfo); } @Override @@ -840,51 +928,66 @@ public class RestconfImpl implements RestconfService { if (payload == null) { throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); } - - // FIXME: move this to parsing stage (we can have augmentation nodes here which do not have namespace) -// final URI payloadNS = payload.getData().getNodeType().getNamespace(); -// 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); -// } - final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint(); final InstanceIdentifierContext iiWithData = payload.getInstanceIdentifierContext(); final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier(); + boolean insertUsed = false; + boolean pointUsed = false; + String insert = null; + String point = null; + + for (final Entry> entry : uriInfo.getQueryParameters().entrySet()) { + switch (entry.getKey()) { + case "insert": + if (!insertUsed) { + insertUsed = true; + insert = entry.getValue().iterator().next(); + } else { + throw new RestconfDocumentedException("Insert parameter can be used only once."); + } + break; + case "point": + if (!pointUsed) { + pointUsed = true; + point = entry.getValue().iterator().next(); + } else { + throw new RestconfDocumentedException("Point parameter can be used only once."); + } + break; + default: + throw new RestconfDocumentedException("Bad parameter for post: " + entry.getKey()); + } + } + + if (pointUsed && !insertUsed) { + throw new RestconfDocumentedException("Point parameter can't be used without Insert parameter."); + } + if (pointUsed && (insert.equals("first") || insert.equals("last"))) { + throw new RestconfDocumentedException( + "Point parameter can be used only with 'after' or 'before' values of Insert parameter."); + } + CheckedFuture future; if (mountPoint != null) { - future = this.broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData()); + future = this.broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData(), insert, + point); } else { future = this.broker.commitConfigurationDataPost(this.controllerContext.getGlobalSchema(), normalizedII, - payload.getData()); + payload.getData(), insert, point); } - final CountDownLatch waiter = new CountDownLatch(1); - Futures.addCallback(future, new FutureCallback() { - - @Override - public void onSuccess(final Void result) { - handlerLoggerPost(null, uriInfo); - waiter.countDown(); - } - - @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); + future.checkedGet(); + } catch (final RestconfDocumentedException e) { + throw e; + } catch (final TransactionCommitFailedException e) { + LOG.info("Error creating data " + (uriInfo != null ? uriInfo.getPath() : ""), e); + throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList()); } + LOG.trace("Successfuly created data."); + final ResponseBuilder responseBuilder = Response.status(Status.NO_CONTENT); // FIXME: Provide path to result. final URI location = resolveLocation(uriInfo, "", mountPoint, normalizedII); @@ -894,18 +997,41 @@ public class RestconfImpl implements RestconfService { 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."); + // FIXME create RestconfIdetifierHelper and move this method there + private static YangInstanceIdentifier checkConsistencyOfNormalizedNodeContext(final NormalizedNodeContext payload) { + Preconditions.checkArgument(payload != null); + Preconditions.checkArgument(payload.getData() != null); + Preconditions.checkArgument(payload.getData().getNodeType() != null); + Preconditions.checkArgument(payload.getInstanceIdentifierContext() != null); + Preconditions.checkArgument(payload.getInstanceIdentifierContext().getInstanceIdentifier() != null); + + final QName payloadNodeQname = payload.getData().getNodeType(); + final YangInstanceIdentifier yangIdent = payload.getInstanceIdentifierContext().getInstanceIdentifier(); + if (payloadNodeQname.compareTo(yangIdent.getLastPathArgument().getNodeType()) > 0) { + return yangIdent; + } + final InstanceIdentifierContext parentContext = payload.getInstanceIdentifierContext(); + final SchemaNode parentSchemaNode = parentContext.getSchemaNode(); + if (parentSchemaNode instanceof DataNodeContainer) { + final DataNodeContainer cast = (DataNodeContainer) parentSchemaNode; + for (final DataSchemaNode child : cast.getChildNodes()) { + if (payloadNodeQname.compareTo(child.getQName()) == 0) { + return YangInstanceIdentifier.builder(yangIdent).node(child.getQName()).build(); + } + } } + if (parentSchemaNode instanceof RpcDefinition) { + return yangIdent; + } + final String errMsg = "Error parsing input: DataSchemaNode has not children "; + LOG.info(errMsg + yangIdent); + throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); } - private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint, final YangInstanceIdentifier normalizedII) { - if(uriInfo == null) { + @SuppressWarnings("checkstyle:IllegalCatch") + private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint, + final YangInstanceIdentifier normalizedII) { + if (uriInfo == null) { // This is null if invoked internally return null; } @@ -934,87 +1060,179 @@ public class RestconfImpl implements RestconfService { future = this.broker.commitConfigurationDataDelete(normalizedII); } - final CountDownLatch waiter = new CountDownLatch(1); - Futures.addCallback(future, new FutureCallback() { - - @Override - public void onSuccess(final Void result) { - handlerLoggerDelete(null); - waiter.countDown(); - } - - @Override - public void onFailure(final Throwable t) { - waiter.countDown(); - handlerLoggerDelete(t); + try { + future.checkedGet(); + } catch (final TransactionCommitFailedException e) { + final Optional searchedException = Iterables.tryFind(Throwables.getCausalChain(e), + Predicates.instanceOf(ModifiedNodeDoesNotExistException.class)); + if (searchedException.isPresent()) { + throw new RestconfDocumentedException("Data specified for delete doesn't exist.", ErrorType.APPLICATION, + ErrorTag.DATA_MISSING); } - }); - - try { - waiter.await(); - } catch (final InterruptedException e) { - final String msg = "Problem while waiting for response"; - LOG.warn(msg); - throw new RestconfDocumentedException(msg, e); + final String errMsg = "Error while deleting data"; + LOG.info(errMsg, e); + throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList()); } 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. + * Subscribes to some path in schema context (stream) to listen on changes + * on this stream. * - * Additional parameters for subscribing to stream are loaded via rpc input parameters: + *

+ * Additional parameters for subscribing to stream are loaded via rpc input + * parameters: *

    - *
  • datastore - default CONFIGURATION (other values of {@link LogicalDatastoreType} enum type)
  • + *
  • datastore - default CONFIGURATION (other values of + * {@link LogicalDatastoreType} enum type)
  • *
  • scope - default BASE (other values of {@link DataChangeScope})
  • *
*/ @Override - public Response subscribeToStream(final String identifier, final UriInfo uriInfo) { + public NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) { + boolean startTimeUsed = false; + boolean stopTimeUsed = false; + Instant start = Instant.now(); + Instant stop = null; + boolean filterUsed = false; + String filter = null; + boolean leafNodesOnlyUsed = false; + boolean leafNodesOnly = false; + + for (final Entry> entry : uriInfo.getQueryParameters().entrySet()) { + switch (entry.getKey()) { + case "start-time": + if (!startTimeUsed) { + startTimeUsed = true; + start = parseDateFromQueryParam(entry); + } else { + throw new RestconfDocumentedException("Start-time parameter can be used only once."); + } + break; + case "stop-time": + if (!stopTimeUsed) { + stopTimeUsed = true; + stop = parseDateFromQueryParam(entry); + } else { + throw new RestconfDocumentedException("Stop-time parameter can be used only once."); + } + break; + case "filter": + if (!filterUsed) { + filterUsed = true; + filter = entry.getValue().iterator().next(); + } else { + throw new RestconfDocumentedException("Filter parameter can be used only once."); + } + break; + case "odl-leaf-nodes-only": + if (!leafNodesOnlyUsed) { + leafNodesOnlyUsed = true; + leafNodesOnly = Boolean.parseBoolean(entry.getValue().iterator().next()); + } else { + throw new RestconfDocumentedException("Odl-leaf-nodes-only parameter can be used only once."); + } + break; + default: + throw new RestconfDocumentedException("Bad parameter used with notifications: " + entry.getKey()); + } + } + if (!startTimeUsed && stopTimeUsed) { + throw new RestconfDocumentedException("Stop-time parameter has to be used with start-time parameter."); + } + URI response = null; if (identifier.contains(DATA_SUBSCR)) { - return dataSubs(identifier, uriInfo); + response = dataSubs(identifier, uriInfo, start, stop, filter, leafNodesOnly); } else if (identifier.contains(NOTIFICATION_STREAM)) { - return notifStream(identifier, uriInfo); + response = notifStream(identifier, uriInfo, start, stop, filter); } + + if (response != null) { + // prepare node with value of location + final InstanceIdentifierContext iid = prepareIIDSubsStreamOutput(); + final NormalizedNodeAttrBuilder> builder = + ImmutableLeafNodeBuilder.create().withValue(response.toString()); + builder.withNodeIdentifier( + NodeIdentifier.create(QName.create("subscribe:to:notification", "2016-10-28", "location"))); + + // prepare new header with location + final Map headers = new HashMap<>(); + headers.put("Location", response); + + return new NormalizedNodeContext(iid, builder.build(), headers); + } + final String msg = "Bad type of notification of sal-remote"; LOG.warn(msg); throw new RestconfDocumentedException(msg); } + private static Instant parseDateFromQueryParam(final Entry> entry) { + final DateAndTime event = new DateAndTime(entry.getValue().iterator().next()); + final String value = event.getValue(); + final TemporalAccessor p; + try { + p = FORMATTER.parse(value); + } catch (final DateTimeParseException e) { + throw new RestconfDocumentedException("Cannot parse of value in date: " + value, e); + } + return Instant.from(p); + } + + /** + * Prepare instance identifier. + * + * @return {@link InstanceIdentifierContext} of location leaf for + * notification + */ + private static InstanceIdentifierContext prepareIIDSubsStreamOutput() { + final QName qnameBase = QName.create("subscribe:to:notification", "2016-10-28", "notifi"); + final SchemaContext schemaCtx = ControllerContext.getInstance().getGlobalSchema(); + final DataSchemaNode location = ((ContainerSchemaNode) schemaCtx + .findModuleByNamespaceAndRevision(qnameBase.getNamespace(), qnameBase.getRevision()) + .getDataChildByName(qnameBase)).getDataChildByName(QName.create(qnameBase, "location")); + final List path = new ArrayList<>(); + path.add(NodeIdentifier.create(qnameBase)); + path.add(NodeIdentifier.create(QName.create(qnameBase, "location"))); + + return new InstanceIdentifierContext(YangInstanceIdentifier.create(path), location, null, + schemaCtx); + } + /** - * Register notification listener by stream name + * Register notification listener by stream name. * * @param identifier - * - stream name + * stream name * @param uriInfo - * - uriInfo - * @return {@link Response} + * uriInfo + * @param stop + * stop-time of getting notification + * @param start + * start-time of getting notification + * @param filter + * indicate which subset of all possible events are of interest + * @return {@link URI} of location */ - private Response notifStream(final String identifier, final UriInfo uriInfo) { + private URI notifStream(final String identifier, final UriInfo uriInfo, final Instant start, + final Instant stop, final String filter) { final String streamName = Notificator.createStreamNameFromUri(identifier); if (Strings.isNullOrEmpty(streamName)) { throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); } final List listeners = Notificator.getNotificationListenerFor(streamName); - if ((listeners == null) || listeners.isEmpty()) { + 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); + listener.setQueryParams(start, + java.util.Optional.ofNullable(stop), java.util.Optional.ofNullable(filter), false); } final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder(); @@ -1028,19 +1246,26 @@ public class RestconfImpl implements RestconfService { final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme("ws"); final URI uriToWebsocketServer = uriToWebsocketServerBuilder.replacePath(streamName).build(); - return Response.status(Status.OK).location(uriToWebsocketServer).build(); + return uriToWebsocketServer; } /** - * Register data change listener by stream name + * Register data change listener by stream name. * * @param identifier - * - stream name + * stream name * @param uriInfo - * - uri info - * @return {@link Response} + * uri info + * @param stop + * start-time of getting notification + * @param start + * stop-time of getting notification + * @param filter + * indicate which subset of all possible events are of interest + * @return {@link URI} of location */ - private Response dataSubs(final String identifier, final UriInfo uriInfo) { + private URI dataSubs(final String identifier, final UriInfo uriInfo, final Instant start, final Instant stop, + final String filter, final boolean leafNodesOnly) { final String streamName = Notificator.createStreamNameFromUri(identifier); if (Strings.isNullOrEmpty(streamName)) { throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); @@ -1048,17 +1273,21 @@ public class RestconfImpl implements RestconfService { final ListenerAdapter listener = Notificator.getListenerFor(streamName); if (listener == null) { - throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT); + 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); final Map paramToValues = resolveValuesFromUri(identifier); - final LogicalDatastoreType datastore = parserURIEnumParameter(LogicalDatastoreType.class, - paramToValues.get(DATASTORE_PARAM_NAME)); + final LogicalDatastoreType datastore = + parserURIEnumParameter(LogicalDatastoreType.class, paramToValues.get(DATASTORE_PARAM_NAME)); if (datastore == null) { throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /datastore=)", ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE); } - final DataChangeScope scope = parserURIEnumParameter(DataChangeScope.class, paramToValues.get(SCOPE_PARAM_NAME)); + final DataChangeScope scope = + parserURIEnumParameter(DataChangeScope.class, paramToValues.get(SCOPE_PARAM_NAME)); if (scope == null) { throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /scope=)", ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE); @@ -1077,54 +1306,57 @@ public class RestconfImpl implements RestconfService { final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme("ws"); final URI uriToWebsocketServer = uriToWebsocketServerBuilder.replacePath(streamName).build(); - return Response.status(Status.OK).location(uriToWebsocketServer).build(); + return uriToWebsocketServer; } + @SuppressWarnings("checkstyle:IllegalCatch") @Override - public PATCHStatusContext patchConfigurationData(final String identifier, final PATCHContext context, final 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); } try { - return this.broker.patchConfigurationDataWithinTransaction(context, - this.controllerContext.getGlobalSchema()); - } catch (final InterruptedException e) { + return this.broker.patchConfigurationDataWithinTransaction(context); + } catch (final Exception e) { LOG.debug("Patch transaction failed", e); throw new RestconfDocumentedException(e.getMessage()); } } + @SuppressWarnings("checkstyle:IllegalCatch") @Override - public PATCHStatusContext patchConfigurationData(final PATCHContext context, @Context final 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); } try { - return this.broker.patchConfigurationDataWithinTransaction(context, - this.controllerContext.getGlobalSchema()); - } catch (final InterruptedException e) { + return this.broker.patchConfigurationDataWithinTransaction(context); + } catch (final Exception e) { LOG.debug("Patch transaction failed", e); throw new RestconfDocumentedException(e.getMessage()); } } /** - * Load parameter for subscribing to stream from input composite node + * Load parameter for subscribing to stream from input composite node. * * @param value * contains value - * @return enum object if its string value is equal to {@code paramName}. In other cases null. + * @return enum object if its string value is equal to {@code paramName}. In + * other cases null. */ private static T parseEnumTypeParameter(final ContainerNode value, final Class classDescriptor, final String paramName) { - final Optional> augNode = value.getChild(SAL_REMOTE_AUG_IDENTIFIER); + final Optional> augNode = + value.getChild(SAL_REMOTE_AUG_IDENTIFIER); if (!augNode.isPresent() && !(augNode instanceof AugmentationNode)) { return null; } - final Optional> enumNode = - ((AugmentationNode) augNode.get()).getChild(new NodeIdentifier(QName.create(SAL_REMOTE_AUGMENT, paramName))); + final Optional> enumNode = ((AugmentationNode) augNode.get()) + .getChild(new NodeIdentifier(QName.create(SAL_REMOTE_AUGMENT, paramName))); if (!enumNode.isPresent()) { return null; } @@ -1137,10 +1369,11 @@ public class RestconfImpl implements RestconfService { } /** - * Checks whether {@code value} is one of the string representation of enumeration {@code classDescriptor} + * Checks whether {@code value} is one of the string representation of + * enumeration {@code classDescriptor}. * - * @return enum object if string value of {@code classDescriptor} enumeration is equal to {@code value}. Other cases - * null. + * @return enum object if string value of {@code classDescriptor} + * enumeration is equal to {@code value}. Other cases null. */ private static T parserURIEnumParameter(final Class classDescriptor, final String value) { if (Strings.isNullOrEmpty(value)) { @@ -1173,20 +1406,15 @@ public class RestconfImpl implements RestconfService { return result; } - public BigInteger getOperationalReceived() { - // TODO Auto-generated method stub - return null; - } - private MapNode makeModuleMapNode(final Set modules) { Preconditions.checkNotNull(modules); final Module restconfModule = getRestconfModule(); - final DataSchemaNode moduleSchemaNode = this.controllerContext.getRestconfModuleRestConfSchemaNode( - restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE); + final DataSchemaNode moduleSchemaNode = this.controllerContext + .getRestconfModuleRestConfSchemaNode(restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE); Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode); - final CollectionNodeBuilder listModuleBuilder = Builders - .mapBuilder((ListSchemaNode) moduleSchemaNode); + final CollectionNodeBuilder listModuleBuilder = + Builders.mapBuilder((ListSchemaNode) moduleSchemaNode); for (final Module module : modules) { listModuleBuilder.withChild(toModuleEntryNode(module, moduleSchemaNode)); @@ -1198,39 +1426,39 @@ public class RestconfImpl implements RestconfService { Preconditions.checkArgument(moduleSchemaNode instanceof ListSchemaNode, "moduleSchemaNode has to be of type ListSchemaNode"); final ListSchemaNode listModuleSchemaNode = (ListSchemaNode) moduleSchemaNode; - final DataContainerNodeAttrBuilder moduleNodeValues = Builders - .mapEntryBuilder(listModuleSchemaNode); + final DataContainerNodeAttrBuilder moduleNodeValues = + Builders.mapEntryBuilder(listModuleSchemaNode); - List instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( - (listModuleSchemaNode), "name"); + List instanceDataChildrenByName = + ControllerContext.findInstanceDataChildrenByName(listModuleSchemaNode, "name"); final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode); - moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(module.getName()) - .build()); + moduleNodeValues + .withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(module.getName()).build()); - instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( - (listModuleSchemaNode), "revision"); + instanceDataChildrenByName = + ControllerContext.findInstanceDataChildrenByName(listModuleSchemaNode, "revision"); final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); Preconditions.checkState(revisionSchemaNode instanceof LeafSchemaNode); - final String revision = REVISION_FORMAT.format(module.getRevision()); - moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) revisionSchemaNode).withValue(revision) - .build()); + final String revision = module.getQNameModule().getFormattedRevision(); + moduleNodeValues + .withChild(Builders.leafBuilder((LeafSchemaNode) revisionSchemaNode).withValue(revision).build()); - instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( - (listModuleSchemaNode), "namespace"); + 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()); - instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( - (listModuleSchemaNode), "feature"); + instanceDataChildrenByName = + ControllerContext.findInstanceDataChildrenByName(listModuleSchemaNode, "feature"); final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); Preconditions.checkState(featureSchemaNode instanceof LeafListSchemaNode); - final ListNodeBuilder> featuresBuilder = Builders - .leafSetBuilder((LeafListSchemaNode) featureSchemaNode); + final ListNodeBuilder> featuresBuilder = + Builders.leafSetBuilder((LeafListSchemaNode) featureSchemaNode); for (final FeatureDefinition feature : module.getFeatures()) { - featuresBuilder.withChild(Builders.leafSetEntryBuilder(((LeafListSchemaNode) featureSchemaNode)) + featuresBuilder.withChild(Builders.leafSetEntryBuilder((LeafListSchemaNode) featureSchemaNode) .withValue(feature.getQName().getLocalName()).build()); } moduleNodeValues.withChild(featuresBuilder.build()); @@ -1242,55 +1470,52 @@ public class RestconfImpl implements RestconfService { Preconditions.checkArgument(streamSchemaNode instanceof ListSchemaNode, "streamSchemaNode has to be of type ListSchemaNode"); final ListSchemaNode listStreamSchemaNode = (ListSchemaNode) streamSchemaNode; - final DataContainerNodeAttrBuilder streamNodeValues = Builders - .mapEntryBuilder(listStreamSchemaNode); + final DataContainerNodeAttrBuilder streamNodeValues = + Builders.mapEntryBuilder(listStreamSchemaNode); - List instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( - (listStreamSchemaNode), "name"); + List instanceDataChildrenByName = + ControllerContext.findInstanceDataChildrenByName(listStreamSchemaNode, "name"); final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode); - streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(streamName) - .build()); + streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(streamName).build()); - instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( - (listStreamSchemaNode), "description"); + instanceDataChildrenByName = + ControllerContext.findInstanceDataChildrenByName(listStreamSchemaNode, "description"); final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); Preconditions.checkState(descriptionSchemaNode instanceof LeafSchemaNode); - streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode) - .withValue("DESCRIPTION_PLACEHOLDER").build()); + streamNodeValues.withChild( + Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue("DESCRIPTION_PLACEHOLDER").build()); - instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( - (listStreamSchemaNode), "replay-support"); + instanceDataChildrenByName = + ControllerContext.findInstanceDataChildrenByName(listStreamSchemaNode, "replay-support"); final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); Preconditions.checkState(replaySupportSchemaNode instanceof LeafSchemaNode); streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replaySupportSchemaNode) .withValue(Boolean.valueOf(true)).build()); - instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( - (listStreamSchemaNode), "replay-log-creation-time"); + instanceDataChildrenByName = + ControllerContext.findInstanceDataChildrenByName(listStreamSchemaNode, "replay-log-creation-time"); final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); Preconditions.checkState(replayLogCreationTimeSchemaNode instanceof LeafSchemaNode); - streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replayLogCreationTimeSchemaNode) - .withValue("").build()); + streamNodeValues.withChild( + Builders.leafBuilder((LeafSchemaNode) replayLogCreationTimeSchemaNode).withValue("").build()); - instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( - (listStreamSchemaNode), "events"); + instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(listStreamSchemaNode, "events"); final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); Preconditions.checkState(eventsSchemaNode instanceof LeafSchemaNode); - streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) eventsSchemaNode) - .withValue("").build()); + streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) eventsSchemaNode).withValue("").build()); return streamNodeValues.build(); } /** - * Prepare stream for notification + * Prepare stream for notification. * * @param payload - * - contains list of qnames of notifications + * contains list of qnames of notifications * @return - checked future object */ - private CheckedFuture invokeSalRemoteRpcNotifiStrRPC( + private static CheckedFuture invokeSalRemoteRpcNotifiStrRPC( final NormalizedNodeContext payload) { final ContainerNode data = (ContainerNode) payload.getData(); LeafSetNode leafSet = null; @@ -1299,7 +1524,7 @@ public class RestconfImpl implements RestconfService { if (dataChild instanceof LeafSetNode) { leafSet = (LeafSetNode) dataChild; } else if (dataChild instanceof AugmentationNode) { - outputType = (String) (((AugmentationNode) dataChild).getValue()).iterator().next().getValue(); + outputType = (String) ((AugmentationNode) dataChild).getValue().iterator().next().getValue(); } } @@ -1310,10 +1535,10 @@ public class RestconfImpl implements RestconfService { final Iterator 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"); + 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)) { @@ -1335,9 +1560,9 @@ public class RestconfImpl implements RestconfService { 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(); + final ContainerNode output = + ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(outputQname)) + .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build(); if (!Notificator.existNotificationListenerFor(streamName)) { Notificator.createNotificationListener(paths, streamName, outputType); @@ -1347,24 +1572,4 @@ public class RestconfImpl implements RestconfService { 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; - } - } }