X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=restconf%2Frestconf-nb-bierman02%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fnetconf%2Fsal%2Frestconf%2Fimpl%2FRestconfImpl.java;h=d771c7f1885ea24ca3f70733a34ebbc82b602279;hb=b8f5ebd2f817de1d613d84cf41f3516728767eba;hp=8a869a58ccae495e7164a2665238400f7ff0e2ed;hpb=824baf9fe14e31465c58f3842c4cb0aa88b34757;p=netconf.git diff --git a/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java b/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java index 8a869a58cc..d771c7f188 100644 --- a/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java +++ b/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java @@ -5,12 +5,10 @@ * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ - package org.opendaylight.netconf.sal.restconf.impl; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.CharMatcher; -import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.base.Predicates; import com.google.common.base.Splitter; @@ -18,27 +16,29 @@ 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.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.FluentFuture; import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import java.net.URI; -import java.text.ParseException; 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.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; @@ -48,16 +48,15 @@ import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; -import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException; -import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; -import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint; -import org.opendaylight.controller.md.sal.dom.api.DOMRpcException; -import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException; -import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; -import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; -import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult; +import org.opendaylight.mdsal.common.api.CommitInfo; +import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.mdsal.common.api.OptimisticLockFailedException; +import org.opendaylight.mdsal.common.api.TransactionCommitFailedException; +import org.opendaylight.mdsal.dom.api.DOMMountPoint; +import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException; +import org.opendaylight.mdsal.dom.api.DOMRpcResult; +import org.opendaylight.mdsal.dom.api.DOMRpcService; +import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult; import org.opendaylight.netconf.sal.rest.api.Draft02; import org.opendaylight.netconf.sal.rest.api.RestconfService; import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter; @@ -71,12 +70,14 @@ import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag; import org.opendaylight.restconf.common.errors.RestconfError.ErrorType; import org.opendaylight.restconf.common.patch.PatchContext; import org.opendaylight.restconf.common.patch.PatchStatusContext; +import org.opendaylight.restconf.common.util.DataChangeScope; import org.opendaylight.restconf.common.validation.RestconfValidationUtils; 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.Empty; 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.common.Revision; 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; @@ -95,13 +96,12 @@ import org.opendaylight.yangtools.yang.data.api.schema.tree.ModifiedNodeDoesNotE import org.opendaylight.yangtools.yang.data.impl.schema.Builders; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder; -import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder; import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder; -import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder; import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder; import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; -import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.FeatureDefinition; import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; @@ -117,10 +117,7 @@ import org.opendaylight.yangtools.yang.model.util.SimpleSchemaContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class RestconfImpl implements RestconfService { - - private static final RestconfImpl INSTANCE = new RestconfImpl(); - +public final class RestconfImpl implements RestconfService { /** * Notifications are served on port 8181. */ @@ -148,19 +145,11 @@ public class RestconfImpl implements RestconfService { private static final String NETCONF_BASE_PAYLOAD_NAME = "data"; - private static final QName NETCONF_BASE_QNAME = QName.create(QNameModule.create(URI.create(NETCONF_BASE), null), + private static final QName NETCONF_BASE_QNAME = QName.create(QNameModule.create(URI.create(NETCONF_BASE)), NETCONF_BASE_PAYLOAD_NAME).intern(); - private static final QNameModule SAL_REMOTE_AUGMENT; - - 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 QNameModule SAL_REMOTE_AUGMENT = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT, + Revision.of("2014-07-08")); private static final AugmentationIdentifier SAL_REMOTE_AUG_IDENTIFIER = new AugmentationIdentifier(ImmutableSet.of( @@ -183,23 +172,17 @@ public class RestconfImpl implements RestconfService { .appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true) .appendOffset("+HH:MM", "Z").toFormatter(); - private BrokerFacade broker; + private final BrokerFacade broker; - private ControllerContext controllerContext; + private final ControllerContext controllerContext; - public void setBroker(final BrokerFacade broker) { + private RestconfImpl(final BrokerFacade broker, final ControllerContext controllerContext) { this.broker = broker; - } - - public void setControllerContext(final ControllerContext controllerContext) { this.controllerContext = controllerContext; } - private RestconfImpl() { - } - - public static RestconfImpl getInstance() { - return INSTANCE; + public static RestconfImpl newInstance(final BrokerFacade broker, final ControllerContext controllerContext) { + return new RestconfImpl(broker, controllerContext); } @Override @@ -214,7 +197,7 @@ public class RestconfImpl implements RestconfService { restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE); Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode); - final DataContainerNodeAttrBuilder moduleContainerBuilder = + final DataContainerNodeBuilder moduleContainerBuilder = Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode); moduleContainerBuilder.withChild(allModuleMap); @@ -231,7 +214,7 @@ public class RestconfImpl implements RestconfService { 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); + LOG.debug("{} for {}", errMsg, identifier); throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); } @@ -246,7 +229,7 @@ public class RestconfImpl implements RestconfService { restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE); Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode); - final DataContainerNodeAttrBuilder moduleContainerBuilder = + final DataContainerNodeBuilder moduleContainerBuilder = Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode); moduleContainerBuilder.withChild(mountPointModulesMap); @@ -259,7 +242,7 @@ public class RestconfImpl implements RestconfService { @Override public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) { Preconditions.checkNotNull(identifier); - final QName moduleNameAndRevision = getModuleNameAndRevision(identifier); + final Entry nameRev = getModuleNameAndRevision(identifier); Module module = null; DOMMountPoint mountPoint = null; final SchemaContext schemaContext; @@ -267,18 +250,18 @@ public class RestconfImpl implements RestconfService { final InstanceIdentifierContext mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); mountPoint = mountPointIdentifier.getMountPoint(); - module = this.controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision); + module = this.controllerContext.findModuleByNameAndRevision(mountPoint, nameRev.getKey(), + nameRev.getValue()); schemaContext = mountPoint.getSchemaContext(); } else { - module = this.controllerContext.findModuleByNameAndRevision(moduleNameAndRevision); + module = this.controllerContext.findModuleByNameAndRevision(nameRev.getKey(), nameRev.getValue()); schemaContext = this.controllerContext.getGlobalSchema(); } if (module == null) { - 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); + LOG.debug("Module with name '{}' and revision '{}' was not found.", nameRev.getKey(), nameRev.getValue()); + throw new RestconfDocumentedException("Module with name '" + nameRev.getKey() + "' and revision '" + + nameRev.getValue() + "' was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT); } final Module restconfModule = getRestconfModule(); @@ -314,7 +297,7 @@ public class RestconfImpl implements RestconfService { restconfModule, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE); Preconditions.checkState(streamsContainerSchemaNode instanceof ContainerSchemaNode); - final DataContainerNodeAttrBuilder streamsContainerBuilder = + final DataContainerNodeBuilder streamsContainerBuilder = Builders.containerBuilder((ContainerSchemaNode) streamsContainerSchemaNode); streamsContainerBuilder.withChild(listStreamsBuilder.build()); @@ -340,11 +323,10 @@ public class RestconfImpl implements RestconfService { 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 "; - LOG.debug(errMsg + ControllerContext.MOUNT + " for " + identifier); - throw new RestconfDocumentedException(errMsg + ControllerContext.MOUNT, ErrorType.PROTOCOL, - ErrorTag.INVALID_VALUE); + final String errMsg = "URI has bad format. If operations behind mount point should be showed, URI has to " + + " end with " + ControllerContext.MOUNT; + LOG.debug("{} for {}", errMsg, identifier); + throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); } return operationsFromModulesToNormalizedContext(modules, mountPoint); @@ -379,11 +361,11 @@ public class RestconfImpl implements RestconfService { } final ContainerSchemaNode fakeCont = new FakeContainerSchemaNode(fakeRpcSchema); - final DataContainerNodeAttrBuilder containerBuilder = + final DataContainerNodeBuilder containerBuilder = Builders.containerBuilder(fakeCont); for (final LeafSchemaNode leaf : fakeRpcSchema) { - containerBuilder.withChild(Builders.leafBuilder(leaf).build()); + containerBuilder.withChild(Builders.leafBuilder(leaf).withValue(Empty.getInstance()).build()); } final Collection fakeModules = new ArrayList<>(neededModules.size() + 1); @@ -407,7 +389,7 @@ public class RestconfImpl implements RestconfService { return restconfModule; } - private static QName getModuleNameAndRevision(final String identifier) { + private static Entry getModuleNameAndRevision(final String identifier) { final int mountIndex = identifier.indexOf(ControllerContext.MOUNT); String moduleNameAndRevision = ""; if (mountIndex >= 0) { @@ -416,24 +398,21 @@ public class RestconfImpl implements RestconfService { moduleNameAndRevision = identifier; } - final Splitter splitter = Splitter.on("/").omitEmptyStrings(); - final Iterable split = splitter.split(moduleNameAndRevision); - final List pathArgs = Lists.newArrayList(split); + final Splitter splitter = Splitter.on('/').omitEmptyStrings(); + final List pathArgs = splitter.splitToList(moduleNameAndRevision); if (pathArgs.size() < 2) { - LOG.debug("URI has bad format. It should be \'moduleName/yyyy-MM-dd\' " + identifier); + LOG.debug("URI has bad format. It should be \'moduleName/yyyy-MM-dd\' {}", identifier); throw new RestconfDocumentedException( "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); } try { - final String moduleName = pathArgs.get(0); - final String revision = pathArgs.get(1); - 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); + return new SimpleImmutableEntry<>(pathArgs.get(0), Revision.of(pathArgs.get(1))); + } catch (final DateTimeParseException 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\'", - ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, e); } } @@ -445,9 +424,14 @@ public class RestconfImpl implements RestconfService { @Override public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) { + if (payload == null) { + // no payload specified, reroute this to no payload invokeRpc implementation + return invokeRpc(identifier, "", uriInfo); + } + final SchemaPath type = payload.getInstanceIdentifierContext().getSchemaNode().getPath(); final URI namespace = payload.getInstanceIdentifierContext().getSchemaNode().getQName().getNamespace(); - final CheckedFuture response; + final ListenableFuture response; final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint(); final SchemaContext schemaContext; @@ -479,9 +463,12 @@ public class RestconfImpl implements RestconfService { final DOMRpcResult result = checkRpcResponse(response); RpcDefinition resultNodeSchema = null; - final NormalizedNode resultData = result.getResult(); + final NormalizedNode resultData; if (result != null && result.getResult() != null) { + resultData = result.getResult(); resultNodeSchema = (RpcDefinition) payload.getInstanceIdentifierContext().getSchemaNode(); + } else { + resultData = null; } return new NormalizedNodeContext( @@ -491,7 +478,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); } @@ -508,13 +495,11 @@ public class RestconfImpl implements RestconfService { 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 if (identifier.indexOf('/') != CHAR_NOT_FOUND) { + LOG.debug("Identifier {} cannot contain slash character (/).", identifier); + throw new RestconfDocumentedException(String.format("Identifier %n%s%ncan\'t contain slash character (/).%n" + + "If slash is part of identifier name then use %%2F placeholder.", identifier), ErrorType.PROTOCOL, + ErrorTag.INVALID_VALUE); } else { identifierEncoded = identifier; schemaContext = this.controllerContext.getGlobalSchema(); @@ -524,23 +509,23 @@ public class RestconfImpl implements RestconfService { RpcDefinition rpc = null; if (mountPoint == null) { - rpc = this.controllerContext.getRpcDefinition(identifierDecoded, null); + rpc = this.controllerContext.getRpcDefinition(identifierDecoded); } else { rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded); } if (rpc == null) { - LOG.debug("RPC " + identifierDecoded + " does not exist."); + LOG.debug("RPC {} does not exist.", identifierDecoded); 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!"); + LOG.debug("RPC {} does not need input value.", rpc); + throw new RestconfDocumentedException("RPC " + rpc + " does not take any input value.", + ErrorType.RPC, ErrorTag.INVALID_VALUE); } - final CheckedFuture response; + final ListenableFuture response; if (mountPoint != null) { final Optional mountRpcServices = mountPoint.getService(DOMRpcService.class); if (!mountRpcServices.isPresent()) { @@ -557,21 +542,22 @@ public class RestconfImpl implements RestconfService { result.getResult(), QueryParametersParser.parseWriterParameters(uriInfo)); } - private static DOMRpcResult checkRpcResponse(final CheckedFuture response) { + @SuppressWarnings("checkstyle:avoidHidingCauseException") + private static DOMRpcResult checkRpcResponse(final ListenableFuture response) { if (response == null) { return null; } try { final DOMRpcResult retValue = response.get(); - if (retValue.getErrors() == null || retValue.getErrors().isEmpty()) { + if (retValue.getErrors().isEmpty()) { return retValue; } - LOG.debug("RpcError message", retValue.getErrors()); + LOG.debug("RpcError message {}", retValue.getErrors()); throw new RestconfDocumentedException("RpcError message", null, retValue.getErrors()); } catch (final InterruptedException e) { final String errMsg = "The operation was interrupted while executing and did not complete."; - LOG.debug("Rpc Interrupt - " + errMsg, e); - throw new RestconfDocumentedException(errMsg, ErrorType.RPC, ErrorTag.PARTIAL_OPERATION); + LOG.debug("Rpc Interrupt - {}", errMsg, e); + throw new RestconfDocumentedException(errMsg, ErrorType.RPC, ErrorTag.PARTIAL_OPERATION, e); } catch (final ExecutionException e) { LOG.debug("Execution RpcError: ", e); Throwable cause = e.getCause(); @@ -595,7 +581,7 @@ public class RestconfImpl implements RestconfService { } } catch (final CancellationException e) { final String errMsg = "The operation was cancelled while executing."; - LOG.debug("Cancel RpcExecution: " + errMsg, e); + LOG.debug("Cancel RpcExecution: {}", errMsg, e); throw new RestconfDocumentedException(errMsg, ErrorType.RPC, ErrorTag.PARTIAL_OPERATION); } } @@ -610,18 +596,18 @@ public class RestconfImpl implements RestconfService { } } - private CheckedFuture - invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) { + private ListenableFuture invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) { final ContainerNode value = (ContainerNode) payload.getData(); final QName rpcQName = payload.getInstanceIdentifierContext().getSchemaNode().getQName(); - final Optional> path = value.getChild(new NodeIdentifier( - QName.create(payload.getInstanceIdentifierContext().getSchemaNode().getQName(), "path"))); + final java.util.Optional> path = value.getChild( + new NodeIdentifier(QName.create(payload.getInstanceIdentifierContext().getSchemaNode().getQName(), + "path"))); final Object pathValue = path.isPresent() ? path.get().getValue() : null; if (!(pathValue instanceof YangInstanceIdentifier)) { - final String errMsg = "Instance identifier was not normalized correctly "; - LOG.debug(errMsg + rpcQName); - throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED); + LOG.debug("Instance identifier {} was not normalized correctly", rpcQName); + throw new RestconfDocumentedException("Instance identifier was not normalized correctly", + ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED); } final YangInstanceIdentifier pathIdentifier = (YangInstanceIdentifier) pathValue; @@ -646,9 +632,10 @@ public class RestconfImpl implements RestconfService { } if (Strings.isNullOrEmpty(streamName)) { - final String errMsg = "Path is empty or contains value node which is not Container or List build-in type."; - LOG.debug(errMsg + pathIdentifier); - throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + LOG.debug("Path is empty or contains value node which is not Container or List built-in type at {}", + pathIdentifier); + throw new RestconfDocumentedException("Path is empty or contains value node which is not Container or List " + + "built-in type.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); } final QName outputQname = QName.create(rpcQName, "output"); @@ -659,20 +646,18 @@ public class RestconfImpl implements RestconfService { .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build(); if (!Notificator.existListenerFor(streamName)) { - Notificator.createListener(pathIdentifier, streamName, outputType); + Notificator.createListener(pathIdentifier, streamName, outputType, controllerContext); } - final DOMRpcResult defaultDOMRpcResult = new DefaultDOMRpcResult(output); - - return Futures.immediateCheckedFuture(defaultDOMRpcResult); + return Futures.immediateFuture(new DefaultDOMRpcResult(output)); } private static RpcDefinition findRpc(final SchemaContext schemaContext, final String identifierDecoded) { final String[] splittedIdentifier = identifierDecoded.split(":"); if (splittedIdentifier.length != 2) { - final String errMsg = identifierDecoded + " couldn't be splitted to 2 parts (module:rpc name)"; - LOG.debug(errMsg); - throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.INVALID_VALUE); + LOG.debug("{} could not be split to 2 parts (module:rpc name)", identifierDecoded); + throw new RestconfDocumentedException(identifierDecoded + " could not be split to 2 parts " + + "(module:rpc name)", ErrorType.APPLICATION, ErrorTag.INVALID_VALUE); } for (final Module module : schemaContext.getModules()) { if (module.getName().equals(splittedIdentifier[0])) { @@ -708,11 +693,11 @@ public class RestconfImpl implements RestconfService { } boolean tagged = false; if (withDefaUsed) { - if (withDefa.equals("report-all-tagged")) { + if ("report-all-tagged".equals(withDefa)) { tagged = true; withDefa = null; } - if (withDefa.equals("report-all")) { + if ("report-all".equals(withDefa)) { withDefa = null; } } @@ -727,10 +712,7 @@ public class RestconfImpl implements RestconfService { 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 "; - LOG.debug(errMsg + identifier); - throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.DATA_MISSING); + throw dataMissing(identifier); } return new NormalizedNodeContext(iiWithData, data, QueryParametersParser.parseWriterParameters(uriInfo, tagged)); @@ -748,14 +730,18 @@ public class RestconfImpl implements RestconfService { 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 "; - LOG.debug(errMsg + identifier); - throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.DATA_MISSING); + throw dataMissing(identifier); } return new NormalizedNodeContext(iiWithData, data, QueryParametersParser.parseWriterParameters(uriInfo)); } + private static RestconfDocumentedException dataMissing(final String identifier) { + LOG.debug("Request could not be completed because the relevant data model content does not exist {}", + identifier); + return new RestconfDocumentedException("Request could not be completed because the relevant data model content " + + "does not exist", ErrorType.APPLICATION, ErrorTag.DATA_MISSING); + } + @Override public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) { @@ -835,21 +821,28 @@ public class RestconfImpl implements RestconfService { } try { - result.getFutureOfPutData().checkedGet(); - return Response.status(result.getStatus()).build(); - } catch (final TransactionCommitFailedException e) { - if (e instanceof OptimisticLockFailedException) { + result.getFutureOfPutData().get(); + } catch (final InterruptedException e) { + LOG.debug("Update failed for {}", identifier, e); + throw new RestconfDocumentedException(e.getMessage(), e); + } catch (final ExecutionException e) { + final TransactionCommitFailedException failure = Throwables.getCauseAs(e, + TransactionCommitFailedException.class); + if (failure instanceof OptimisticLockFailedException) { if (--tries <= 0) { - LOG.debug("Got OptimisticLockFailedException on last try - failing " + identifier); - throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList()); + LOG.debug("Got OptimisticLockFailedException on last try - failing {}", identifier); + throw new RestconfDocumentedException(e.getMessage(), e, failure.getErrorList()); } - LOG.debug("Got OptimisticLockFailedException - trying again " + identifier); - } else { - LOG.debug("Update failed for " + identifier, e); - throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList()); + LOG.debug("Got OptimisticLockFailedException - trying again {}", identifier); + continue; } + + LOG.debug("Update failed for {}", identifier, e); + throw RestconfDocumentedException.decodeAndThrow(e.getMessage(), failure); } + + return Response.status(result.getStatus()).build(); } } @@ -942,26 +935,28 @@ public class RestconfImpl implements RestconfService { 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 (uriInfo != 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()); + } } } @@ -973,7 +968,7 @@ public class RestconfImpl implements RestconfService { "Point parameter can be used only with 'after' or 'before' values of Insert parameter."); } - CheckedFuture future; + FluentFuture future; if (mountPoint != null) { future = this.broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData(), insert, point); @@ -983,12 +978,14 @@ public class RestconfImpl implements RestconfService { } try { - 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()); + future.get(); + } catch (final InterruptedException e) { + LOG.info("Error creating data {}", uriInfo != null ? uriInfo.getPath() : "", e); + throw new RestconfDocumentedException(e.getMessage(), e); + } catch (final ExecutionException e) { + LOG.info("Error creating data {}", uriInfo != null ? uriInfo.getPath() : "", e); + throw RestconfDocumentedException.decodeAndThrow(e.getMessage(), Throwables.getCauseAs(e, + TransactionCommitFailedException.class)); } LOG.trace("Successfuly created data."); @@ -1002,37 +999,6 @@ public class RestconfImpl implements RestconfService { return responseBuilder.build(); } - // 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); - } - @SuppressWarnings("checkstyle:IllegalCatch") private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint, final YangInstanceIdentifier normalizedII) { @@ -1046,7 +1012,7 @@ public class RestconfImpl implements RestconfService { try { uriBuilder.path(this.controllerContext.toFullRestconfIdentifier(normalizedII, mountPoint)); } catch (final Exception e) { - LOG.info("Location for instance identifier" + normalizedII + "wasn't created", e); + LOG.info("Location for instance identifier {} was not created", normalizedII, e); return null; } return uriBuilder.build(); @@ -1058,7 +1024,7 @@ public class RestconfImpl implements RestconfService { final DOMMountPoint mountPoint = iiWithData.getMountPoint(); final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier(); - final CheckedFuture future; + final FluentFuture future; if (mountPoint != null) { future = this.broker.commitConfigurationDataDelete(mountPoint, normalizedII); } else { @@ -1066,18 +1032,19 @@ public class RestconfImpl implements RestconfService { } try { - future.checkedGet(); - } catch (final TransactionCommitFailedException e) { + future.get(); + } catch (final InterruptedException e) { + throw new RestconfDocumentedException(e.getMessage(), e); + } catch (final ExecutionException e) { final Optional searchedException = Iterables.tryFind(Throwables.getCausalChain(e), - Predicates.instanceOf(ModifiedNodeDoesNotExistException.class)); + Predicates.instanceOf(ModifiedNodeDoesNotExistException.class)).toJavaUtil(); if (searchedException.isPresent()) { throw new RestconfDocumentedException("Data specified for delete doesn't exist.", ErrorType.APPLICATION, - ErrorTag.DATA_MISSING); + ErrorTag.DATA_MISSING, e); } - final String errMsg = "Error while deleting data"; - LOG.info(errMsg, e); - throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList()); + throw RestconfDocumentedException.decodeAndThrow(e.getMessage(), Throwables.getCauseAs(e, + TransactionCommitFailedException.class)); } return Response.status(Status.OK).build(); @@ -1158,7 +1125,7 @@ public class RestconfImpl implements RestconfService { if (response != null) { // prepare node with value of location final InstanceIdentifierContext iid = prepareIIDSubsStreamOutput(); - final NormalizedNodeAttrBuilder> builder = + final NormalizedNodeBuilder> builder = ImmutableLeafNodeBuilder.create().withValue(response.toString()); builder.withNodeIdentifier( NodeIdentifier.create(QName.create("subscribe:to:notification", "2016-10-28", "location"))); @@ -1193,11 +1160,11 @@ public class RestconfImpl implements RestconfService { * @return {@link InstanceIdentifierContext} of location leaf for * notification */ - private static InstanceIdentifierContext prepareIIDSubsStreamOutput() { + private InstanceIdentifierContext prepareIIDSubsStreamOutput() { final QName qnameBase = QName.create("subscribe:to:notification", "2016-10-28", "notifi"); - final SchemaContext schemaCtx = ControllerContext.getInstance().getGlobalSchema(); + final SchemaContext schemaCtx = controllerContext.getGlobalSchema(); final DataSchemaNode location = ((ContainerSchemaNode) schemaCtx - .findModuleByNamespaceAndRevision(qnameBase.getNamespace(), qnameBase.getRevision()) + .findModule(qnameBase.getModule()).orElse(null) .getDataChildByName(qnameBase)).getDataChildByName(QName.create(qnameBase, "location")); final List path = new ArrayList<>(); path.add(NodeIdentifier.create(qnameBase)); @@ -1241,17 +1208,23 @@ public class RestconfImpl implements RestconfService { } final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder(); - int notificationPort = NOTIFICATION_PORT; - try { - final WebSocketServer webSocketServerInstance = WebSocketServer.getInstance(); - notificationPort = webSocketServerInstance.getPort(); - } catch (final NullPointerException e) { - WebSocketServer.createInstance(NOTIFICATION_PORT); - } - final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme("ws"); - final URI uriToWebsocketServer = uriToWebsocketServerBuilder.replacePath(streamName).build(); - return uriToWebsocketServer; + final WebSocketServer webSocketServerInstance = WebSocketServer.getInstance(NOTIFICATION_PORT); + final int notificationPort = webSocketServerInstance.getPort(); + + + final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme(getWsScheme(uriInfo)); + + return uriToWebsocketServerBuilder.replacePath(streamName).build(); + } + + private static String getWsScheme(final UriInfo uriInfo) { + URI uri = uriInfo.getAbsolutePath(); + if (uri == null) { + return "ws"; + } + String subscriptionScheme = uri.getScheme().toLowerCase(Locale.ROOT); + return subscriptionScheme.equals("https") ? "wss" : "ws"; } /** @@ -1301,17 +1274,13 @@ public class RestconfImpl implements RestconfService { this.broker.registerToListenDataChanges(datastore, scope, listener); final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder(); - int notificationPort = NOTIFICATION_PORT; - try { - final WebSocketServer webSocketServerInstance = WebSocketServer.getInstance(); - notificationPort = webSocketServerInstance.getPort(); - } catch (final NullPointerException e) { - WebSocketServer.createInstance(NOTIFICATION_PORT); - } - final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme("ws"); - final URI uriToWebsocketServer = uriToWebsocketServerBuilder.replacePath(streamName).build(); - return uriToWebsocketServer; + final WebSocketServer webSocketServerInstance = WebSocketServer.getInstance(NOTIFICATION_PORT); + final int notificationPort = webSocketServerInstance.getPort(); + + final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme(getWsScheme(uriInfo)); + + return uriToWebsocketServerBuilder.replacePath(streamName).build(); } @SuppressWarnings("checkstyle:IllegalCatch") @@ -1326,7 +1295,7 @@ public class RestconfImpl implements RestconfService { return this.broker.patchConfigurationDataWithinTransaction(context); } catch (final Exception e) { LOG.debug("Patch transaction failed", e); - throw new RestconfDocumentedException(e.getMessage()); + throw new RestconfDocumentedException(e.getMessage(), e); } } @@ -1341,7 +1310,7 @@ public class RestconfImpl implements RestconfService { return this.broker.patchConfigurationDataWithinTransaction(context); } catch (final Exception e) { LOG.debug("Patch transaction failed", e); - throw new RestconfDocumentedException(e.getMessage()); + throw new RestconfDocumentedException(e.getMessage(), e); } } @@ -1355,13 +1324,17 @@ public class RestconfImpl implements RestconfService { */ private static T parseEnumTypeParameter(final ContainerNode value, final Class classDescriptor, final String paramName) { - final Optional> augNode = - value.getChild(SAL_REMOTE_AUG_IDENTIFIER); - if (!augNode.isPresent() && !(augNode instanceof AugmentationNode)) { + final java.util.Optional> optAugNode = value.getChild( + SAL_REMOTE_AUG_IDENTIFIER); + if (!optAugNode.isPresent()) { + return null; + } + final DataContainerChild augNode = optAugNode.get(); + if (!(augNode instanceof AugmentationNode)) { return null; } - final Optional> enumNode = ((AugmentationNode) augNode.get()) - .getChild(new NodeIdentifier(QName.create(SAL_REMOTE_AUGMENT, paramName))); + final java.util.Optional> enumNode = ((AugmentationNode) augNode) + .getChild(new NodeIdentifier(QName.create(SAL_REMOTE_AUGMENT, paramName))); if (!enumNode.isPresent()) { return null; } @@ -1427,11 +1400,11 @@ public class RestconfImpl implements RestconfService { return listModuleBuilder.build(); } - protected MapEntryNode toModuleEntryNode(final Module module, final DataSchemaNode moduleSchemaNode) { + private MapEntryNode toModuleEntryNode(final Module module, final DataSchemaNode moduleSchemaNode) { Preconditions.checkArgument(moduleSchemaNode instanceof ListSchemaNode, "moduleSchemaNode has to be of type ListSchemaNode"); final ListSchemaNode listModuleSchemaNode = (ListSchemaNode) moduleSchemaNode; - final DataContainerNodeAttrBuilder moduleNodeValues = + final DataContainerNodeBuilder moduleNodeValues = Builders.mapEntryBuilder(listModuleSchemaNode); List instanceDataChildrenByName = @@ -1441,20 +1414,22 @@ public class RestconfImpl implements RestconfService { moduleNodeValues .withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(module.getName()).build()); + final QNameModule qNameModule = module.getQNameModule(); + instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(listModuleSchemaNode, "revision"); final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); Preconditions.checkState(revisionSchemaNode instanceof LeafSchemaNode); - final String revision = module.getQNameModule().getFormattedRevision(); - moduleNodeValues - .withChild(Builders.leafBuilder((LeafSchemaNode) revisionSchemaNode).withValue(revision).build()); + final java.util.Optional revision = qNameModule.getRevision(); + moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) revisionSchemaNode) + .withValue(revision.map(Revision::toString).orElse("")).build()); instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(listModuleSchemaNode, "namespace"); final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); Preconditions.checkState(namespaceSchemaNode instanceof LeafSchemaNode); moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) namespaceSchemaNode) - .withValue(module.getNamespace().toString()).build()); + .withValue(qNameModule.getNamespace().toString()).build()); instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(listModuleSchemaNode, "feature"); @@ -1475,7 +1450,7 @@ 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 = + final DataContainerNodeBuilder streamNodeValues = Builders.mapEntryBuilder(listStreamSchemaNode); List instanceDataChildrenByName = @@ -1496,7 +1471,7 @@ public class RestconfImpl implements RestconfService { final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); Preconditions.checkState(replaySupportSchemaNode instanceof LeafSchemaNode); streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replaySupportSchemaNode) - .withValue(Boolean.valueOf(true)).build()); + .withValue(Boolean.TRUE).build()); instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(listStreamSchemaNode, "replay-log-creation-time"); @@ -1508,7 +1483,7 @@ public class RestconfImpl implements RestconfService { 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).build()); return streamNodeValues.build(); } @@ -1520,8 +1495,7 @@ public class RestconfImpl implements RestconfService { * contains list of qnames of notifications * @return - checked future object */ - private static CheckedFuture invokeSalRemoteRpcNotifiStrRPC( - final NormalizedNodeContext payload) { + private ListenableFuture invokeSalRemoteRpcNotifiStrRPC(final NormalizedNodeContext payload) { final ContainerNode data = (ContainerNode) payload.getData(); LeafSetNode leafSet = null; String outputType = "XML"; @@ -1537,11 +1511,11 @@ public class RestconfImpl implements RestconfService { final List paths = new ArrayList<>(); String streamName = CREATE_NOTIFICATION_STREAM + "/"; + StringBuilder streamNameBuilder = new StringBuilder(streamName); 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()); + final Module module = controllerContext.findModuleByNamespace(valueQName.getModule().getNamespace()); Preconditions.checkNotNull(module, "Module for namespace " + valueQName.getModule().getNamespace() + " does not exist"); NotificationDefinition notifiDef = null; @@ -1555,12 +1529,14 @@ public class RestconfImpl implements RestconfService { Preconditions.checkNotNull(notifiDef, "Notification " + valueQName + "doesn't exist in module " + moduleName); paths.add(notifiDef.getPath()); - streamName = streamName + moduleName + ":" + valueQName.getLocalName(); + streamNameBuilder.append(moduleName).append(':').append(valueQName.getLocalName()); if (iterator.hasNext()) { - streamName = streamName + ","; + streamNameBuilder.append(','); } } + streamName = streamNameBuilder.toString(); + final QName rpcQName = payload.getInstanceIdentifierContext().getSchemaNode().getQName(); final QName outputQname = QName.create(rpcQName, "output"); final QName streamNameQname = QName.create(rpcQName, "notification-stream-identifier"); @@ -1570,11 +1546,9 @@ public class RestconfImpl implements RestconfService { .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build(); if (!Notificator.existNotificationListenerFor(streamName)) { - Notificator.createNotificationListener(paths, streamName, outputType); + Notificator.createNotificationListener(paths, streamName, outputType, controllerContext); } - final DOMRpcResult defaultDOMRpcResult = new DefaultDOMRpcResult(output); - - return Futures.immediateCheckedFuture(defaultDOMRpcResult); + return Futures.immediateFuture(new DefaultDOMRpcResult(output)); } }