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=7484a377fbfee48266dc392d80ed438adadea9b0;hb=c8f61b164f0515cccc8d21f6b9c5a279be818ae6;hp=7a5d521331fdc4c531edd1d3a3920640145d0f07;hpb=2781b3622622720d067829d22e21ada1a2a5b251;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 7a5d521331..7484a377fb 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 @@ -11,17 +11,20 @@ package org.opendaylight.netconf.sal.restconf.impl; 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.net.URI; import java.net.URISyntaxException; +import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -29,13 +32,12 @@ 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.Map.Entry; 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,9 +64,11 @@ 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.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; @@ -78,6 +82,7 @@ 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; @@ -114,8 +119,6 @@ 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; @@ -154,21 +157,19 @@ public class RestconfImpl implements RestconfService { 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); + final Date eventSubscriptionAugRevision = SimpleDateFormatUtil.getRevisionFormat().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"), + QName.create(SAL_REMOTE_AUGMENT, "scope"), QName.create(SAL_REMOTE_AUGMENT, "datastore"), QName.create(SAL_REMOTE_AUGMENT, "notification-output-type"))); } 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"; + 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); } } @@ -204,9 +205,8 @@ 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)); } /** @@ -215,15 +215,15 @@ public class RestconfImpl implements RestconfService { @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); @@ -237,9 +237,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 @@ -250,8 +251,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(); @@ -261,8 +262,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); } @@ -271,12 +272,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 @@ -288,8 +290,8 @@ public class RestconfImpl implements RestconfService { .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)); @@ -303,9 +305,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 @@ -319,14 +321,14 @@ 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); @@ -336,10 +338,10 @@ public class RestconfImpl implements RestconfService { } /** - * 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. + * 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 @@ -347,35 +349,38 @@ public class RestconfImpl implements RestconfService { * - mount point, if in use otherwise null * @return {@link NormalizedNodeContext} */ - private NormalizedNodeContext operationsFromModulesToNormalizedContext(final Set modules, + private static NormalizedNodeContext operationsFromModulesToNormalizedContext(final Set modules, final DOMMountPoint mountPoint) { - final ContainerSchemaNodeImpl fakeCont = new ContainerSchemaNodeImpl(); - final List> listRpcNodes = new ArrayList<>(); + final Collection neededModules = new ArrayList<>(modules.size()); + final ArrayList fakeRpcSchema = new ArrayList<>(); + for (final Module m : modules) { - for (final RpcDefinition rpc : m.getRpcs()) { + final Set rpcs = m.getRpcs(); + if (!rpcs.isEmpty()) { + neededModules.add(m); - 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()); + fakeRpcSchema.ensureCapacity(fakeRpcSchema.size() + rpcs.size()); + rpcs.forEach(rpc -> fakeRpcSchema.add(new FakeLeafSchemaNode(rpc.getQName()))); } } - final ContainerSchemaNode fakeContSchNode = fakeCont; - final DataContainerNodeAttrBuilder containerBuilder = Builders - .containerBuilder(fakeContSchNode); - for (final LeafNode rpcNode : listRpcNodes) { - containerBuilder.withChild(rpcNode); + final ContainerSchemaNode fakeCont = new FakeContainerSchemaNode(fakeRpcSchema); + final DataContainerNodeAttrBuilder containerBuilder = + Builders.containerBuilder(fakeCont); + + for (final LeafSchemaNode leaf : fakeRpcSchema) { + containerBuilder.withChild(Builders.leafBuilder(leaf).build()); } - final Module fakeModule = new ModuleImpl(fakeContSchNode); + final Collection fakeModules = new ArrayList<>(neededModules.size() + 1); + neededModules.forEach(imp -> fakeModules.add(new FakeImportedModule(imp))); + fakeModules.add(new FakeRestconfModule(neededModules, fakeCont)); - final Set fakeModules = new HashSet<>(); - fakeModules.add(fakeModule); - final SchemaContext fakeSchemaCtx = EffectiveSchemaContext.resolveSchemaContext(fakeModules); + final SchemaContext fakeSchemaCtx = + EffectiveSchemaContext.resolveSchemaContext(ImmutableSet.copyOf(fakeModules)); final InstanceIdentifierContext instanceIdentifierContext = - new InstanceIdentifierContext<>(null, fakeContSchNode, mountPoint, fakeSchemaCtx); + new InstanceIdentifierContext<>(null, fakeCont, mountPoint, fakeSchemaCtx); return new NormalizedNodeContext(instanceIdentifierContext, containerBuilder.build()); } @@ -412,7 +417,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); + final Date moduleRevision = SimpleDateFormatUtil.getRevisionFormat().parse(revision); return QName.create(null, moduleRevision, moduleName); } catch (final ParseException e) { LOG.debug("URI has bad format. It should be \'moduleName/yyyy-MM-dd\' " + identifier); @@ -434,9 +439,10 @@ public class RestconfImpl implements RestconfService { 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."); } @@ -463,13 +469,13 @@ 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)); } private static DOMRpcResult checkRpcResponse(final CheckedFuture response) { @@ -478,7 +484,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()); @@ -516,10 +522,10 @@ 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); } @@ -539,12 +545,12 @@ 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); @@ -553,12 +559,11 @@ public class RestconfImpl implements RestconfService { DataChangeScope scope = parseEnumTypeParameter(value, DataChangeScope.class, SCOPE_PARAM_NAME); scope = scope == null ? DEFAULT_SCOPE : scope; - outputType = parseEnumTypeParameter(value, NotificationOutputType.class, - OUTPUT_TYPE_PARAM_NAME); + outputType = parseEnumTypeParameter(value, NotificationOutputType.class, OUTPUT_TYPE_PARAM_NAME); outputType = outputType == null ? NotificationOutputType.XML : outputType; - streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier + "/datastore=" + datastore - + "/scope=" + scope); + streamName = Notificator + .createStreamNameFromUri(fullRestconfIdentifier + "/datastore=" + datastore + "/scope=" + scope); } if (Strings.isNullOrEmpty(streamName)) { @@ -585,7 +590,7 @@ public class RestconfImpl implements RestconfService { @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); } @@ -597,14 +602,16 @@ public class RestconfImpl implements RestconfService { 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 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); + 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 { @@ -626,7 +633,7 @@ public class RestconfImpl implements RestconfService { throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT); } - if (rpc.getInput() != null) { + 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!"); @@ -635,7 +642,7 @@ public class RestconfImpl implements RestconfService { final CheckedFuture response; if (mountPoint != null) { final Optional mountRpcServices = mountPoint.getService(DOMRpcService.class); - if ( ! mountRpcServices.isPresent()) { + if (!mountRpcServices.isPresent()) { throw new RestconfDocumentedException("Rpc service is missing."); } response = mountRpcServices.get().invokeRpc(rpc.getPath(), null); @@ -670,21 +677,49 @@ public class RestconfImpl implements RestconfService { @Override public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) { + boolean withDefa_used = false; + String withDefa = null; + + for (final Entry> entry : uriInfo.getQueryParameters().entrySet()) { + switch (entry.getKey()) { + case "with-defaults": + if (!withDefa_used) { + withDefa_used = true; + withDefa = entry.getValue().iterator().next(); + } else { + throw new RestconfDocumentedException("With-defaults parameter can be used only once."); + } + break; + } + } + boolean tagged = false; + if (withDefa_used) { + 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 @@ -698,17 +733,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); @@ -719,78 +793,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 Exception 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 (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(); } } @@ -799,26 +844,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 @@ -832,15 +878,16 @@ 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) { + private static void isEqualUriAndPayloadKeyValues(final Map uriKeyValues, final MapEntryNode payload, + final List keyDefinitions) { final Map mutableCopyUriKeyValues = Maps.newHashMap(uriKeyValues); for (final QName keyDefinition : keyDefinitions) { @@ -851,9 +898,9 @@ 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 + 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. "; throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); } @@ -863,7 +910,7 @@ public class RestconfImpl implements RestconfService { @Override public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) { - return createConfigurationData(payload, uriInfo); + return createConfigurationData(payload, uriInfo); } // FIXME create RestconfIdetifierHelper and move this method there @@ -881,7 +928,7 @@ public class RestconfImpl implements RestconfService { } final InstanceIdentifierContext parentContext = payload.getInstanceIdentifierContext(); final SchemaNode parentSchemaNode = parentContext.getSchemaNode(); - if(parentSchemaNode instanceof DataNodeContainer) { + if (parentSchemaNode instanceof DataNodeContainer) { final DataNodeContainer cast = (DataNodeContainer) parentSchemaNode; for (final DataSchemaNode child : cast.getChildNodes()) { if (payloadNodeQname.compareTo(child.getQName()) == 0) { @@ -902,51 +949,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 Exception 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 (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); @@ -956,19 +1018,9 @@ 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."); - } - } - private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint, final YangInstanceIdentifier normalizedII) { - if(uriInfo == null) { + if (uriInfo == null) { // This is null if invoked internally return null; } @@ -997,67 +1049,90 @@ 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 (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 Exception 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 NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) { + boolean startTime_used = false; + boolean stopTime_used = false; + boolean filter_used = false; + Date start = null; + Date stop = null; + String filter = null; + + for (final Entry> entry : uriInfo.getQueryParameters().entrySet()) { + switch (entry.getKey()) { + case "start-time": + if (!startTime_used) { + startTime_used = true; + start = parseDateFromQueryParam(entry); + } else { + throw new RestconfDocumentedException("Start-time parameter can be used only once."); + } + break; + case "stop-time": + if (!stopTime_used) { + stopTime_used = true; + stop = parseDateFromQueryParam(entry); + } else { + throw new RestconfDocumentedException("Stop-time parameter can be used only once."); + } + break; + case "filter": + if (!filter_used) { + filter_used = true; + filter = entry.getValue().iterator().next(); + } else { + throw new RestconfDocumentedException("Filter parameter can be used only once."); + } + break; + default: + throw new RestconfDocumentedException("Bad parameter used with notifications: " + entry.getKey()); + } + } + if (!startTime_used && stopTime_used) { + throw new RestconfDocumentedException("Stop-time parameter has to be used with start-time parameter."); + } URI response = null; if (identifier.contains(DATA_SUBSCR)) { - response = dataSubs(identifier, uriInfo); + response = dataSubs(identifier, uriInfo, start, stop, filter); } else if (identifier.contains(NOTIFICATION_STREAM)) { - response = notifStream(identifier, uriInfo); + response = notifStream(identifier, uriInfo, start, stop, filter); } - if(response != null){ + if (response != null) { // prepare node with value of location final InstanceIdentifierContext iid = prepareIIDSubsStreamOutput(); - final NormalizedNodeAttrBuilder> builder = ImmutableLeafNodeBuilder - .create().withValue(response.toString()); + final NormalizedNodeAttrBuilder> builder = + ImmutableLeafNodeBuilder.create().withValue(response.toString()); builder.withNodeIdentifier( NodeIdentifier.create(QName.create("subscribe:to:notification", "2016-10-28", "location"))); @@ -1073,11 +1148,37 @@ public class RestconfImpl implements RestconfService { throw new RestconfDocumentedException(msg); } + private static Date parseDateFromQueryParam(final Entry> entry) { + final DateAndTime event = new DateAndTime(entry.getValue().iterator().next()); + String numOf_ms = ""; + final String value = event.getValue(); + if (value.contains(".")) { + numOf_ms = numOf_ms + "."; + final int lastChar = value.contains("Z") ? value.indexOf("Z") : value.contains("+") ? value.indexOf("+") + : value.contains("-") ? value.indexOf("-") : value.length(); + for (int i = 0; i < lastChar - value.indexOf(".") - 1; i++) { + numOf_ms = numOf_ms + "S"; + } + } + String zone = ""; + if (!value.contains("Z")) { + zone = zone + "XXX"; + } + final DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" + numOf_ms + zone); + + try { + return dateFormatter.parse(value.contains("Z") ? value.replace('T', ' ').substring(0, value.indexOf("Z")) + : value.replace('T', ' ')); + } catch (final ParseException e) { + throw new RestconfDocumentedException("Cannot parse of value in date: " + value + e); + } + } + /** * @return {@link InstanceIdentifierContext} of location leaf for * notification */ - private InstanceIdentifierContext prepareIIDSubsStreamOutput() { + 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 @@ -1098,21 +1199,29 @@ public class RestconfImpl implements RestconfService { * - stream name * @param uriInfo * - uriInfo - * @return {@link Response} + * @param stop + * - stop-time of getting notification + * @param start + * - start-time of getting notification + * @param filter + * - indicate wh ich subset of allpossible events are of interest + * @return {@link URI} of location */ - private URI notifStream(final String identifier, final UriInfo uriInfo) { + private URI notifStream(final String identifier, final UriInfo uriInfo, final Date start, final Date 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, stop, filter); } final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder(); @@ -1136,9 +1245,16 @@ public class RestconfImpl implements RestconfService { * - stream name * @param uriInfo * - uri info - * @return {@link Response} + * @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 URI dataSubs(final String identifier, final UriInfo uriInfo) { + private URI dataSubs(final String identifier, final UriInfo uriInfo, final Date start, final Date 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); @@ -1146,17 +1262,20 @@ 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, stop, filter); 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); @@ -1212,17 +1331,18 @@ public class RestconfImpl implements RestconfService { * * @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; } @@ -1235,10 +1355,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)) { @@ -1274,12 +1395,12 @@ public class RestconfImpl implements RestconfService { 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)); @@ -1291,39 +1412,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()); @@ -1335,43 +1456,40 @@ 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(); } @@ -1383,7 +1501,7 @@ public class RestconfImpl implements RestconfService { * - 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; @@ -1392,7 +1510,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(); } } @@ -1403,10 +1521,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)) { @@ -1428,9 +1546,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); @@ -1440,24 +1558,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; - } - } }