Teach RFC8040 restconf about actions 92/82992/18
authorajay.dp001 <ajay.deep.singh@ericsson.com>
Wed, 10 Jul 2019 11:23:05 +0000 (12:23 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Fri, 6 Sep 2019 16:15:58 +0000 (18:15 +0200)
This adds the handlers to route action requests through
DOMActionService and return results.

JIRA: NETCONF-618
Change-Id: I9d72fa5939b73beefbbb2e8030b1fa0a1f4ff935
Signed-off-by: ajay.dp001 <ajay.deep.singh@ericsson.com>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
19 files changed:
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/Rfc8040RestConfWiring.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/ActionServiceHandler.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/NormalizedNodeJsonBodyWriter.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/NormalizedNodeXmlBodyWriter.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/XmlNormalizedNodeBodyReader.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ActionResultFactory.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/FutureCallbackTx.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/RestconfInvokeOperationsUtil.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/wrapper/ServicesWrapper.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/XmlBodyReaderMountPointTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/test/JsonBodyReaderTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/test/XmlBodyReaderTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/JSONRestconfServiceRfc8040ImplTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImplTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/test/incubate/InMemoryMdsalModule.java
restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/json/json_cont_action.json [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/xml/xml_cont_action.xml [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/yang/instance-identifier-module.yang

index d3fe40686a55de26491a444f9ac1257496ec6477..0f3f5fb29a210d186e744f4b8607f04057e25377 100644 (file)
@@ -11,6 +11,7 @@ import javax.inject.Inject;
 import javax.inject.Singleton;
 import org.apache.aries.blueprint.annotation.service.Reference;
 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
 import javax.inject.Singleton;
 import org.apache.aries.blueprint.annotation.service.Reference;
 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
+import org.opendaylight.restconf.nb.rfc8040.handlers.ActionServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.DOMDataBrokerHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.NotificationServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.DOMDataBrokerHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.NotificationServiceHandler;
@@ -34,18 +35,19 @@ import org.opendaylight.restconf.nb.rfc8040.web.WebInitializer;
  */
 @Singleton
 public class Rfc8040RestConfWiring {
  */
 @Singleton
 public class Rfc8040RestConfWiring {
-
     private final ServicesWrapper servicesWrapper;
 
     @Inject
     private final ServicesWrapper servicesWrapper;
 
     @Inject
-    public Rfc8040RestConfWiring(
-            SchemaContextHandler schemaCtxHandler,
-            DOMMountPointServiceHandler domMountPointServiceHandler, TransactionChainHandler transactionChainHandler,
-            DOMDataBrokerHandler domDataBrokerHandler, RpcServiceHandler rpcServiceHandler,
-            NotificationServiceHandler notificationServiceHandler, @Reference DOMSchemaService domSchemaService) {
+    public Rfc8040RestConfWiring(final SchemaContextHandler schemaCtxHandler,
+            final DOMMountPointServiceHandler domMountPointServiceHandler,
+            final TransactionChainHandler transactionChainHandler,
+            final DOMDataBrokerHandler domDataBrokerHandler, final RpcServiceHandler rpcServiceHandler,
+            final ActionServiceHandler actionServiceHandler,
+            final NotificationServiceHandler notificationServiceHandler,
+            @Reference final DOMSchemaService domSchemaService) {
         servicesWrapper = ServicesWrapper.newInstance(schemaCtxHandler, domMountPointServiceHandler,
         servicesWrapper = ServicesWrapper.newInstance(schemaCtxHandler, domMountPointServiceHandler,
-                transactionChainHandler, domDataBrokerHandler, rpcServiceHandler, notificationServiceHandler,
-                domSchemaService);
+            transactionChainHandler, domDataBrokerHandler, rpcServiceHandler, actionServiceHandler,
+            notificationServiceHandler, domSchemaService);
     }
 
     public ServicesWrapper getServicesWrapper() {
     }
 
     public ServicesWrapper getServicesWrapper() {
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/ActionServiceHandler.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/ActionServiceHandler.java
new file mode 100644 (file)
index 0000000..b6b5eb1
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 Ericsson Software Technology AB. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.nb.rfc8040.handlers;
+
+import static java.util.Objects.requireNonNull;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import org.apache.aries.blueprint.annotation.service.Reference;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.dom.api.DOMActionService;
+
+/**
+ * Implementation of {@link ActionServiceHandler}.
+ */
+@Singleton
+public class ActionServiceHandler implements Handler<DOMActionService> {
+    private final @NonNull DOMActionService actionService;
+
+    /**
+     * Set DOMActionService.
+     *
+     * @param actionService
+     *             DOMActionService
+     */
+    @Inject
+    public ActionServiceHandler(final @Reference DOMActionService actionService) {
+        this.actionService = requireNonNull(actionService);
+    }
+
+    @Override
+    public @NonNull DOMActionService get() {
+        return this.actionService;
+    }
+}
index 2f586909f835b14fe930a06a90332d8d84dfa8fe..1272a0fee43726a0738b723b293b5460ebb130b5 100644 (file)
@@ -41,6 +41,7 @@ import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactorySupplier;
 import org.opendaylight.yangtools.yang.data.codec.gson.JSONNormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 import org.opendaylight.yangtools.yang.data.codec.gson.JSONNormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
@@ -119,6 +120,18 @@ public class NormalizedNodeJsonBodyWriter implements MessageBodyWriter<Normalize
             jsonWriter.beginObject();
             writeChildren(nnWriter, (ContainerNode) data);
             jsonWriter.endObject();
             jsonWriter.beginObject();
             writeChildren(nnWriter, (ContainerNode) data);
             jsonWriter.endObject();
+        } else if (context.getSchemaNode() instanceof ActionDefinition) {
+            /*
+             *  ActionDefinition is not supported as initial codec in JSONStreamWriter,
+             *  so we need to emit initial output declaration..
+             */
+            nnWriter = createNormalizedNodeWriter(context,
+                ((ActionDefinition) context.getSchemaNode()).getOutput().getPath(), jsonWriter, depth, fields);
+            final Module module = context.getSchemaContext().findModule(data.getNodeType().getModule()).get();
+            jsonWriter.name(module.getName() + ":output");
+            jsonWriter.beginObject();
+            writeChildren(nnWriter, (ContainerNode) data);
+            jsonWriter.endObject();
         } else {
             if (SchemaPath.ROOT.equals(path)) {
                 nnWriter = createNormalizedNodeWriter(context, path, jsonWriter, depth, fields);
         } else {
             if (SchemaPath.ROOT.equals(path)) {
                 nnWriter = createNormalizedNodeWriter(context, path, jsonWriter, depth, fields);
index 49af300cf077c216cf9c69b74ea09dcdb9f2c571..9d849ec565001c49b76a495bc53c00cfea3f3ab1 100644 (file)
@@ -39,6 +39,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.codec.xml.XMLStreamNormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.codec.xml.XMLStreamNormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
@@ -118,6 +119,14 @@ public class NormalizedNodeXmlBodyWriter implements MessageBodyWriter<Normalized
                     depth,
                     fields);
             writeElements(xmlWriter, nnWriter, (ContainerNode) data);
                     depth,
                     fields);
             writeElements(xmlWriter, nnWriter, (ContainerNode) data);
+        } else if (pathContext.getSchemaNode() instanceof ActionDefinition) {
+            /*
+             *  ActionDefinition is not supported as initial codec in XMLStreamWriter,
+             *  so we need to emit initial output declaration..
+             */
+            nnWriter = createNormalizedNodeWriter(xmlWriter, schemaCtx,
+                ((ActionDefinition) pathContext.getSchemaNode()).getOutput().getPath(), depth, fields);
+            writeElements(xmlWriter, nnWriter, (ContainerNode) data);
         } else {
             if (SchemaPath.ROOT.equals(path)) {
                 nnWriter = createNormalizedNodeWriter(xmlWriter, schemaCtx, path, depth, fields);
         } else {
             if (SchemaPath.ROOT.equals(path)) {
                 nnWriter = createNormalizedNodeWriter(xmlWriter, schemaCtx, path, depth, fields);
index 1fc91c43816575d5fe2cf7b02db71613118aeb63..0dfe3a04640e46a110d7c97da6e33acbac8da611 100644 (file)
@@ -7,7 +7,8 @@
  */
 package org.opendaylight.restconf.nb.rfc8040.jersey.providers;
 
  */
 package org.opendaylight.restconf.nb.rfc8040.jersey.providers;
 
-import com.google.common.base.Preconditions;
+import static com.google.common.base.Preconditions.checkState;
+
 import com.google.common.collect.Iterables;
 import java.io.IOException;
 import java.io.InputStream;
 import com.google.common.collect.Iterables;
 import java.io.IOException;
 import java.io.InputStream;
@@ -53,7 +54,7 @@ import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -91,21 +92,22 @@ public class XmlNormalizedNodeBodyReader extends AbstractNormalizedNodeBodyReade
             throws XMLStreamException, IOException, ParserConfigurationException, SAXException, URISyntaxException {
         final SchemaNode schemaNodeContext = pathContext.getSchemaNode();
         DataSchemaNode schemaNode;
             throws XMLStreamException, IOException, ParserConfigurationException, SAXException, URISyntaxException {
         final SchemaNode schemaNodeContext = pathContext.getSchemaNode();
         DataSchemaNode schemaNode;
-        boolean isRpc = false;
-        if (schemaNodeContext instanceof RpcDefinition) {
-            schemaNode = ((RpcDefinition) schemaNodeContext).getInput();
-            isRpc = true;
+        final boolean isOperation;
+        if (schemaNodeContext instanceof OperationDefinition) {
+            schemaNode = ((OperationDefinition) schemaNodeContext).getInput();
+            isOperation = true;
         } else if (schemaNodeContext instanceof DataSchemaNode) {
             schemaNode = (DataSchemaNode) schemaNodeContext;
         } else if (schemaNodeContext instanceof DataSchemaNode) {
             schemaNode = (DataSchemaNode) schemaNodeContext;
+            isOperation = false;
         } else {
         } else {
-            throw new IllegalStateException("Unknown SchemaNode");
+            throw new IllegalStateException("Unknown SchemaNode " + schemaNodeContext);
         }
 
         final String docRootElm = doc.getDocumentElement().getLocalName();
         final String docRootNamespace = doc.getDocumentElement().getNamespaceURI();
         final List<YangInstanceIdentifier.PathArgument> iiToDataList = new ArrayList<>();
 
         }
 
         final String docRootElm = doc.getDocumentElement().getLocalName();
         final String docRootNamespace = doc.getDocumentElement().getNamespaceURI();
         final List<YangInstanceIdentifier.PathArgument> iiToDataList = new ArrayList<>();
 
-        if (isPost() && !isRpc) {
+        if (isPost() && !isOperation) {
             final Deque<Object> foundSchemaNodes = findPathToSchemaNodeByName(schemaNode, docRootElm, docRootNamespace);
             if (foundSchemaNodes.isEmpty()) {
                 throw new IllegalStateException(String.format("Child \"%s\" was not found in parent schema node \"%s\"",
             final Deque<Object> foundSchemaNodes = findPathToSchemaNodeByName(schemaNode, docRootElm, docRootNamespace);
             if (foundSchemaNodes.isEmpty()) {
                 throw new IllegalStateException(String.format("Child \"%s\" was not found in parent schema node \"%s\"",
@@ -122,13 +124,11 @@ public class XmlNormalizedNodeBodyReader extends AbstractNormalizedNodeBodyReade
                 }
             }
         // PUT
                 }
             }
         // PUT
-        } else if (!isRpc) {
+        } else if (!isOperation) {
             final QName scQName = schemaNode.getQName();
             final QName scQName = schemaNode.getQName();
-            Preconditions.checkState(
-                    docRootElm.equals(scQName.getLocalName())
-                            && docRootNamespace.equals(scQName.getNamespace().toASCIIString()),
-                    String.format("Not correct message root element \"%s\", should be \"%s\"",
-                            docRootElm, scQName));
+            checkState(docRootElm.equals(scQName.getLocalName())
+                && docRootNamespace.equals(scQName.getNamespace().toASCIIString()),
+                "Not correct message root element \"%s\", should be \"%s\"", docRootElm, scQName);
         }
 
         NormalizedNode<?, ?> parsed;
         }
 
         NormalizedNode<?, ?> parsed;
index 7c55e3486fa298b4784aaabf5f858e6c7fe75604..2b81a48f6e74402e972d767ccd7606053cc9f404 100644 (file)
@@ -18,12 +18,13 @@ import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.List;
 import java.util.Map.Entry;
 import java.time.format.DateTimeFormatter;
 import java.util.List;
 import java.util.Map.Entry;
-import java.util.Objects;
 import java.util.Optional;
 import javax.ws.rs.Path;
 import java.util.Optional;
 import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import org.eclipse.jdt.annotation.NonNull;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.dom.api.DOMActionResult;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
@@ -31,8 +32,11 @@ import org.opendaylight.restconf.common.context.NormalizedNodeContext;
 import org.opendaylight.restconf.common.context.WriterParameters;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError;
 import org.opendaylight.restconf.common.context.WriterParameters;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError;
+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.patch.PatchContext;
 import org.opendaylight.restconf.common.patch.PatchStatusContext;
+import org.opendaylight.restconf.nb.rfc8040.handlers.ActionServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
@@ -46,12 +50,17 @@ import org.opendaylight.restconf.nb.rfc8040.rests.utils.PostDataTransactionUtil;
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.PutDataTransactionUtil;
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.ReadDataTransactionUtil;
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfDataServiceConstant;
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.PutDataTransactionUtil;
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.ReadDataTransactionUtil;
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfDataServiceConstant;
+import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfInvokeOperationsUtil;
 import org.opendaylight.restconf.nb.rfc8040.utils.RestconfConstants;
 import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.Revision;
 import org.opendaylight.restconf.nb.rfc8040.utils.RestconfConstants;
 import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -64,20 +73,24 @@ public class RestconfDataServiceImpl implements RestconfDataService {
     private static final Logger LOG = LoggerFactory.getLogger(RestconfDataServiceImpl.class);
     private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MMM-dd HH:mm:ss");
 
     private static final Logger LOG = LoggerFactory.getLogger(RestconfDataServiceImpl.class);
     private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MMM-dd HH:mm:ss");
 
+    private final RestconfStreamsSubscriptionService delegRestconfSubscrService;
+
+    // FIXME: evaluate thread-safety of updates (synchronized) vs. access (mostly unsynchronized) here
     private SchemaContextHandler schemaContextHandler;
     private TransactionChainHandler transactionChainHandler;
     private DOMMountPointServiceHandler mountPointServiceHandler;
     private SchemaContextHandler schemaContextHandler;
     private TransactionChainHandler transactionChainHandler;
     private DOMMountPointServiceHandler mountPointServiceHandler;
-
-    private final RestconfStreamsSubscriptionService delegRestconfSubscrService;
+    private volatile ActionServiceHandler actionServiceHandler;
 
     public RestconfDataServiceImpl(final SchemaContextHandler schemaContextHandler,
 
     public RestconfDataServiceImpl(final SchemaContextHandler schemaContextHandler,
-                                   final TransactionChainHandler transactionChainHandler,
+            final TransactionChainHandler transactionChainHandler,
             final DOMMountPointServiceHandler mountPointServiceHandler,
             final DOMMountPointServiceHandler mountPointServiceHandler,
-            final RestconfStreamsSubscriptionService delegRestconfSubscrService) {
-        this.schemaContextHandler = Objects.requireNonNull(schemaContextHandler);
-        this.transactionChainHandler = Objects.requireNonNull(transactionChainHandler);
-        this.mountPointServiceHandler = Objects.requireNonNull(mountPointServiceHandler);
-        this.delegRestconfSubscrService = Objects.requireNonNull(delegRestconfSubscrService);
+            final RestconfStreamsSubscriptionService delegRestconfSubscrService,
+            final ActionServiceHandler actionServiceHandler) {
+        this.actionServiceHandler = requireNonNull(actionServiceHandler);
+        this.schemaContextHandler = requireNonNull(schemaContextHandler);
+        this.transactionChainHandler = requireNonNull(transactionChainHandler);
+        this.mountPointServiceHandler = requireNonNull(mountPointServiceHandler);
+        this.delegRestconfSubscrService = requireNonNull(delegRestconfSubscrService);
     }
 
     @Override
     }
 
     @Override
@@ -85,6 +98,8 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         for (final Object object : handlers) {
             if (object instanceof SchemaContextHandler) {
                 schemaContextHandler = (SchemaContextHandler) object;
         for (final Object object : handlers) {
             if (object instanceof SchemaContextHandler) {
                 schemaContextHandler = (SchemaContextHandler) object;
+            } else if (object instanceof ActionServiceHandler) {
+                actionServiceHandler = (ActionServiceHandler) object;
             } else if (object instanceof DOMMountPointServiceHandler) {
                 mountPointServiceHandler = (DOMMountPointServiceHandler) object;
             } else if (object instanceof TransactionChainHandler) {
             } else if (object instanceof DOMMountPointServiceHandler) {
                 mountPointServiceHandler = (DOMMountPointServiceHandler) object;
             } else if (object instanceof TransactionChainHandler) {
@@ -218,7 +233,9 @@ public class RestconfDataServiceImpl implements RestconfDataService {
     @Override
     public Response postData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
         requireNonNull(payload);
     @Override
     public Response postData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
         requireNonNull(payload);
-
+        if (payload.getInstanceIdentifierContext().getSchemaNode() instanceof ActionDefinition) {
+            return invokeAction(payload, uriInfo);
+        }
         boolean insertUsed = false;
         boolean pointUsed = false;
         String insert = null;
         boolean insertUsed = false;
         boolean pointUsed = false;
         String insert = null;
@@ -316,4 +333,56 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         LOG.warn(errMsg);
         throw new RestconfDocumentedException(errMsg);
     }
         LOG.warn(errMsg);
         throw new RestconfDocumentedException(errMsg);
     }
+
+    /**
+     * Invoke Action operation.
+     *
+     * @param payload
+     *             {@link NormalizedNodeContext} - the body of the operation
+     * @param uriInfo
+     *             URI info
+     * @return {@link NormalizedNodeContext} wrapped in {@link Response}
+     */
+    public Response invokeAction(final NormalizedNodeContext payload, final UriInfo uriInfo) {
+        final InstanceIdentifierContext<?> context = payload.getInstanceIdentifierContext();
+        final DOMMountPoint mountPoint = context.getMountPoint();
+        final SchemaPath schemaPath = context.getSchemaNode().getPath();
+        final YangInstanceIdentifier yangIIdContext = context.getInstanceIdentifier();
+        final NormalizedNode<?, ?> data = payload.getData();
+
+        if (yangIIdContext.isEmpty() && !RestconfDataServiceConstant.NETCONF_BASE_QNAME.equals(data.getNodeType())) {
+            throw new RestconfDocumentedException("Instance identifier need to contain at least one path argument",
+                ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
+        }
+
+        final DOMActionResult response;
+        final SchemaContextRef schemaContextRef;
+        if (mountPoint != null) {
+            response = RestconfInvokeOperationsUtil.invokeActionViaMountPoint(mountPoint, (ContainerNode) data,
+                schemaPath, yangIIdContext);
+            schemaContextRef = new SchemaContextRef(mountPoint.getSchemaContext());
+        } else {
+            response = RestconfInvokeOperationsUtil.invokeAction((ContainerNode) data, schemaPath,
+                this.actionServiceHandler, yangIIdContext);
+            schemaContextRef = new SchemaContextRef(this.schemaContextHandler.get());
+        }
+        final DOMActionResult result = RestconfInvokeOperationsUtil.checkActionResponse(response);
+
+        ActionDefinition resultNodeSchema = null;
+        ContainerNode resultData = null;
+        if (result != null) {
+            final Optional<ContainerNode> optOutput = result.getOutput();
+            if (optOutput.isPresent()) {
+                resultData = optOutput.get();
+                resultNodeSchema = (ActionDefinition) context.getSchemaNode();
+            }
+        }
+
+        if (resultData != null && resultData.getValue().isEmpty()) {
+            throw new WebApplicationException(Response.Status.NO_CONTENT);
+        }
+
+        return Response.status(200).entity(new NormalizedNodeContext(new InstanceIdentifierContext<>(yangIIdContext,
+                resultNodeSchema, mountPoint, schemaContextRef.get()), resultData)).build();
+    }
 }
 }
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ActionResultFactory.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ActionResultFactory.java
new file mode 100644 (file)
index 0000000..eda8181
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2019 Ericsson Software Technology AB. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.nb.rfc8040.rests.utils;
+
+import org.opendaylight.mdsal.dom.api.DOMActionResult;
+import org.opendaylight.yangtools.concepts.Builder;
+
+/**
+ * Implementation of {@link ActionResultFactory}.
+ */
+public class ActionResultFactory extends FutureDataFactory<DOMActionResult> implements Builder<DOMActionResult> {
+
+    @Override
+    public DOMActionResult build() throws IllegalArgumentException {
+        return this.result;
+    }
+}
index e925612e63bc8e00c582b4e6c3e195c3660daab6..58178b6c41efcde7af175f4d08fdf0e7f5816776 100644 (file)
@@ -13,8 +13,10 @@ import com.google.common.util.concurrent.ListenableFuture;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
+import org.opendaylight.mdsal.dom.api.DOMActionException;
 import org.opendaylight.mdsal.dom.api.DOMRpcException;
 import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.mdsal.dom.api.DOMRpcException;
 import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
+import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
 import org.opendaylight.netconf.api.NetconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError;
 import org.opendaylight.netconf.api.NetconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError;
@@ -66,6 +68,9 @@ final class FutureCallbackTx {
             if (cause instanceof DOMRpcException) {
                 dataFactory.setResult((T) new DefaultDOMRpcResult(ImmutableList.of(
                     RpcResultBuilder.newError(RpcError.ErrorType.RPC, "operation-failed", cause.getMessage()))));
             if (cause instanceof DOMRpcException) {
                 dataFactory.setResult((T) new DefaultDOMRpcResult(ImmutableList.of(
                     RpcResultBuilder.newError(RpcError.ErrorType.RPC, "operation-failed", cause.getMessage()))));
+            } else if (cause instanceof DOMActionException) {
+                dataFactory.setResult((T) new SimpleDOMActionResult(ImmutableList.of(
+                    RpcResultBuilder.newError(RpcError.ErrorType.RPC, "operation-failed", cause.getMessage()))));
             } else if (cause instanceof TransactionCommitFailedException) {
                 /* If device send some error message we want this message to get to client
                    and not just to throw it away or override it with new generic message.
             } else if (cause instanceof TransactionCommitFailedException) {
                 /* If device send some error message we want this message to get to client
                    and not just to throw it away or override it with new generic message.
index fcc7d3c39c75d75cf889835f8229bfadb4fe69d7..8e1304ea5bfb8682b4683746c4dcd2da6f3d4959 100644 (file)
@@ -8,16 +8,26 @@
 package org.opendaylight.restconf.nb.rfc8040.rests.utils;
 
 import com.google.common.util.concurrent.ListenableFuture;
 package org.opendaylight.restconf.nb.rfc8040.rests.utils;
 
 import com.google.common.util.concurrent.ListenableFuture;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Optional;
 import java.util.concurrent.CancellationException;
 import javax.ws.rs.core.Response.Status;
 import java.util.Optional;
 import java.util.concurrent.CancellationException;
 import javax.ws.rs.core.Response.Status;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.dom.api.DOMActionResult;
+import org.opendaylight.mdsal.dom.api.DOMActionService;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
 import org.opendaylight.mdsal.dom.api.DOMRpcService;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
 import org.opendaylight.mdsal.dom.api.DOMRpcService;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.nb.rfc8040.handlers.ActionServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.RpcServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.RpcServiceHandler;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.slf4j.Logger;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.slf4j.Logger;
@@ -109,4 +119,104 @@ public final class RestconfInvokeOperationsUtil {
         FutureCallbackTx.addCallback(rpc, RestconfDataServiceConstant.PostData.POST_TX_TYPE, dataFactory);
         return dataFactory.build();
     }
         FutureCallbackTx.addCallback(rpc, RestconfDataServiceConstant.PostData.POST_TX_TYPE, dataFactory);
         return dataFactory.build();
     }
+
+    /**
+     * Invoking Action via mount point.
+     *
+     * @param mountPoint
+     *             mount point
+     * @param data
+     *             input data
+     * @param schemaPath
+     *             schema path of data
+     * @return {@link DOMActionResult}
+     */
+    public static DOMActionResult invokeActionViaMountPoint(final DOMMountPoint mountPoint, final ContainerNode data,
+            final SchemaPath schemaPath, final YangInstanceIdentifier yangIId) {
+        final Optional<DOMActionService> mountPointService = mountPoint.getService(DOMActionService.class);
+        if (!mountPointService.isPresent()) {
+            throw new RestconfDocumentedException("DomAction service is missing.");
+        }
+
+        return prepareActionResult(mountPointService.get().invokeAction(schemaPath,
+            prepareDataTreeId(yangIId, schemaPath), data));
+    }
+
+    /**
+     * Invoke Action via ActionServiceHandler.
+     *
+     * @param data
+     *             input data
+     * @param schemaPath
+     *             schema path of data
+     * @param actionServiceHandler
+     *             action service handler to invoke action
+     * @return {@link DOMActionResult}
+     */
+    public static DOMActionResult invokeAction(final ContainerNode data, final SchemaPath schemaPath,
+            final ActionServiceHandler actionServiceHandler, final YangInstanceIdentifier yangIId) {
+        return prepareActionResult(actionServiceHandler.get().invokeAction(schemaPath,
+            prepareDataTreeId(yangIId, schemaPath), data));
+    }
+
+    /**
+     * Check the validity of the result.
+     *
+     * @param response
+     *             response of Action
+     * @return {@link DOMActionResult} result
+     */
+    public static DOMActionResult checkActionResponse(final DOMActionResult response) {
+        if (response != null) {
+            try {
+                if (response.getErrors().isEmpty()) {
+                    return response;
+                }
+                LOG.debug("InvokeAction Error Message {}", response.getErrors());
+                throw new RestconfDocumentedException("InvokeAction Error Message ", null, response.getErrors());
+            } catch (final CancellationException e) {
+                final String errMsg = "The Action Operation was cancelled while executing.";
+                LOG.debug("Cancel Execution: {}", errMsg, e);
+                throw new RestconfDocumentedException(errMsg, ErrorType.RPC, ErrorTag.PARTIAL_OPERATION, e);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Prepare Action Result.
+     *
+     * @param actionResult
+     *            {@link DOMActionResult} - action result
+     * @return {@link DOMActionResult} result
+     */
+    private static DOMActionResult prepareActionResult(final ListenableFuture<? extends DOMActionResult> actionResult) {
+        final ActionResultFactory dataFactory = new ActionResultFactory();
+        FutureCallbackTx.addCallback(actionResult, RestconfDataServiceConstant.PostData.POST_TX_TYPE, dataFactory);
+        return dataFactory.build();
+    }
+
+    /**
+     * Prepare DOMDataTree Identifier.
+     *
+     * @param yangIId
+     *             {@link YangInstanceIdentifier}
+     * @param schemaPath
+     *              {@link SchemaPath}
+     * @return {@link DOMDataTreeIdentifier} domDataTreeIdentifier
+     */
+    private static DOMDataTreeIdentifier prepareDataTreeId(final YangInstanceIdentifier yangIId,
+            final SchemaPath schemaPath) {
+        final List<PathArgument> pathArg = new ArrayList<>();
+        for (PathArgument path : yangIId.getPathArguments()) {
+            if (path.getNodeType().getLocalName().equals(schemaPath.getLastComponent().getLocalName())) {
+                break;
+            }
+            pathArg.add(path);
+        }
+        YangInstanceIdentifier yangInstanceIdentifier = YangInstanceIdentifier.builder().append(pathArg).build();
+        DOMDataTreeIdentifier domDataTreeIdentifier = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,
+            yangInstanceIdentifier);
+        return domDataTreeIdentifier;
+    }
 }
 }
index aabce8a544836399691eb3817e1558ab9b840da5..0c60f0ee771be26b65c3862c4dd8d5e723ed3ea3 100644 (file)
@@ -16,6 +16,7 @@ import org.opendaylight.restconf.common.context.NormalizedNodeContext;
 import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.restconf.common.patch.PatchStatusContext;
 import org.opendaylight.restconf.common.schema.SchemaExportContext;
 import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.restconf.common.patch.PatchStatusContext;
 import org.opendaylight.restconf.common.schema.SchemaExportContext;
+import org.opendaylight.restconf.nb.rfc8040.handlers.ActionServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.DOMDataBrokerHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.NotificationServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.DOMDataBrokerHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.NotificationServiceHandler;
@@ -54,11 +55,11 @@ public final class ServicesWrapper implements BaseServicesWrapper, TransactionSe
     private final RestconfSchemaService delegRestSchService;
     private final RestconfService delegRestService;
 
     private final RestconfSchemaService delegRestSchService;
     private final RestconfService delegRestService;
 
-    private ServicesWrapper(RestconfDataService delegRestconfDataService,
-            RestconfInvokeOperationsService delegRestconfInvokeOpsService,
-            RestconfStreamsSubscriptionService delegRestconfSubscrService,
-            RestconfOperationsService delegRestOpsService, RestconfSchemaService delegRestSchService,
-            RestconfService delegRestService) {
+    private ServicesWrapper(final RestconfDataService delegRestconfDataService,
+            final RestconfInvokeOperationsService delegRestconfInvokeOpsService,
+            final RestconfStreamsSubscriptionService delegRestconfSubscrService,
+            final RestconfOperationsService delegRestOpsService, final RestconfSchemaService delegRestSchService,
+            final RestconfService delegRestService) {
         this.delegRestconfDataService = delegRestconfDataService;
         this.delegRestconfInvokeOpsService = delegRestconfInvokeOpsService;
         this.delegRestconfSubscrService = delegRestconfSubscrService;
         this.delegRestconfDataService = delegRestconfDataService;
         this.delegRestconfInvokeOpsService = delegRestconfInvokeOpsService;
         this.delegRestconfSubscrService = delegRestconfSubscrService;
@@ -70,26 +71,23 @@ public final class ServicesWrapper implements BaseServicesWrapper, TransactionSe
     public static ServicesWrapper newInstance(final SchemaContextHandler schemaCtxHandler,
             final DOMMountPointServiceHandler domMountPointServiceHandler,
             final TransactionChainHandler transactionChainHandler, final DOMDataBrokerHandler domDataBrokerHandler,
     public static ServicesWrapper newInstance(final SchemaContextHandler schemaCtxHandler,
             final DOMMountPointServiceHandler domMountPointServiceHandler,
             final TransactionChainHandler transactionChainHandler, final DOMDataBrokerHandler domDataBrokerHandler,
-            final RpcServiceHandler rpcServiceHandler, final NotificationServiceHandler notificationServiceHandler,
-            final DOMSchemaService domSchemaService) {
-        RestconfOperationsService restconfOpsService =
-                new RestconfOperationsServiceImpl(schemaCtxHandler, domMountPointServiceHandler);
+            final RpcServiceHandler rpcServiceHandler, final ActionServiceHandler actionServiceHandler,
+            final NotificationServiceHandler notificationServiceHandler, final DOMSchemaService domSchemaService) {
+        RestconfOperationsService restconfOpsService = new RestconfOperationsServiceImpl(schemaCtxHandler,
+            domMountPointServiceHandler);
         final DOMYangTextSourceProvider yangTextSourceProvider = domSchemaService.getExtensions()
         final DOMYangTextSourceProvider yangTextSourceProvider = domSchemaService.getExtensions()
-                .getInstance(DOMYangTextSourceProvider.class);
-        RestconfSchemaService restconfSchemaService =
-                new RestconfSchemaServiceImpl(schemaCtxHandler, domMountPointServiceHandler,
-                yangTextSourceProvider);
-        RestconfStreamsSubscriptionService restconfSubscrService =
-                new RestconfStreamsSubscriptionServiceImpl(domDataBrokerHandler,
-                notificationServiceHandler, schemaCtxHandler, transactionChainHandler);
-        RestconfDataService restconfDataService =
-                new RestconfDataServiceImpl(schemaCtxHandler, transactionChainHandler, domMountPointServiceHandler,
-                        restconfSubscrService);
-        RestconfInvokeOperationsService restconfInvokeOpsService =
-                new RestconfInvokeOperationsServiceImpl(rpcServiceHandler, schemaCtxHandler);
+            .getInstance(DOMYangTextSourceProvider.class);
+        RestconfSchemaService restconfSchemaService = new RestconfSchemaServiceImpl(schemaCtxHandler,
+            domMountPointServiceHandler, yangTextSourceProvider);
+        RestconfStreamsSubscriptionService restconfSubscrService = new RestconfStreamsSubscriptionServiceImpl(
+            domDataBrokerHandler, notificationServiceHandler, schemaCtxHandler, transactionChainHandler);
+        RestconfDataService restconfDataService = new RestconfDataServiceImpl(schemaCtxHandler, transactionChainHandler,
+            domMountPointServiceHandler, restconfSubscrService, actionServiceHandler);
+        RestconfInvokeOperationsService restconfInvokeOpsService = new RestconfInvokeOperationsServiceImpl(
+            rpcServiceHandler, schemaCtxHandler);
         RestconfService restconfService = new RestconfImpl(schemaCtxHandler);
         RestconfService restconfService = new RestconfImpl(schemaCtxHandler);
-        return new ServicesWrapper(restconfDataService, restconfInvokeOpsService,
-                restconfSubscrService, restconfOpsService, restconfSchemaService, restconfService);
+        return new ServicesWrapper(restconfDataService, restconfInvokeOpsService, restconfSubscrService,
+            restconfOpsService, restconfSchemaService, restconfService);
     }
 
     @Override
     }
 
     @Override
index 51a82a39bc73d655d07e1a200a61a016dc370a18..7bc0937822a5e3a696d26daf65072efef04b598b 100644 (file)
@@ -107,6 +107,20 @@ public class XmlBodyReaderMountPointTest extends AbstractBodyReaderTest {
         checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue);
     }
 
         checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue);
     }
 
+    @Test
+    public void moduleSubContainerDataPostActionTest() throws Exception {
+        final Optional<DataSchemaNode> dataSchemaNode = schemaContext
+            .findDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
+        final String uri = "instance-identifier-module:cont/yang-ext:mount/instance-identifier-module:cont/cont1/reset";
+        mockBodyReader(uri, this.xmlBodyReader, true);
+        final InputStream inputStream = XmlBodyReaderMountPointTest.class
+            .getResourceAsStream("/instanceidentifier/xml/xml_cont_action.xml");
+        final NormalizedNodeContext returnValue = this.xmlBodyReader.readFrom(null,
+            null, null, this.mediaType, null, inputStream);
+        checkMountPointNormalizedNodeContext(returnValue);
+        checkExpectValueNormalizeNodeContext(dataSchemaNode.get(), returnValue);
+    }
+
     @Test
     public void rpcModuleInputTest() throws Exception {
         final String uri = "instance-identifier-module:cont/yang-ext:mount/invoke-rpc-module:rpc-test";
     @Test
     public void rpcModuleInputTest() throws Exception {
         final String uri = "instance-identifier-module:cont/yang-ext:mount/invoke-rpc-module:rpc-test";
index 1c3050e33e09cd113030ab0cde2210570c9de643..2e02284470695e7bc3191707c8fd229085aa0ca9 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.restconf.nb.rfc8040.jersey.providers.test;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 import com.google.common.collect.Sets;
 import java.io.File;
 
 import com.google.common.collect.Sets;
 import java.io.File;
@@ -17,6 +18,7 @@ import java.io.FileNotFoundException;
 import java.io.InputStream;
 import java.net.URI;
 import java.util.Collection;
 import java.io.InputStream;
 import java.net.URI;
 import java.util.Collection;
+import java.util.Optional;
 import javax.ws.rs.core.MediaType;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import javax.ws.rs.core.MediaType;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -28,6 +30,7 @@ import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.common.Revision;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
 import org.opendaylight.yangtools.yang.common.Revision;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
@@ -110,6 +113,24 @@ public class JsonBodyReaderTest extends AbstractBodyReaderTest {
         checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII);
     }
 
         checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII);
     }
 
+    @Test
+    public void moduleSubContainerDataPostActionTest() throws Exception {
+        final Optional<DataSchemaNode> dataSchemaNode = schemaContext
+            .findDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
+        final QName cont1QName = QName.create(dataSchemaNode.get().getQName(), "cont1");
+        final QName actionQName = QName.create(dataSchemaNode.get().getQName(), "reset");
+        final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.get().getQName())
+            .node(cont1QName).node(actionQName);
+        final String uri = "instance-identifier-module:cont/cont1/reset";
+        mockBodyReader(uri, this.jsonBodyReader, true);
+        final InputStream inputStream = JsonBodyReaderTest.class
+            .getResourceAsStream("/instanceidentifier/json/json_cont_action.json");
+        final NormalizedNodeContext returnValue = this.jsonBodyReader
+            .readFrom(null, null, null, this.mediaType, null, inputStream);
+        checkNormalizedNodeContext(returnValue);
+        assertTrue(returnValue.getInstanceIdentifierContext().getSchemaNode() instanceof ActionDefinition);
+    }
+
     @Test
     public void moduleSubContainerAugmentDataPostTest() throws Exception {
         final DataSchemaNode dataSchemaNode = schemaContext
     @Test
     public void moduleSubContainerAugmentDataPostTest() throws Exception {
         final DataSchemaNode dataSchemaNode = schemaContext
index 7f8d621cf921926c77cb5bf735022440cf353448..fef810d0e422bb0b1e7eb9dd3e466cd542dbf316 100644 (file)
@@ -18,6 +18,7 @@ import java.io.File;
 import java.io.InputStream;
 import java.net.URI;
 import java.util.Collection;
 import java.io.InputStream;
 import java.net.URI;
 import java.util.Collection;
+import java.util.Optional;
 import javax.ws.rs.core.MediaType;
 import org.junit.Assert;
 import org.junit.BeforeClass;
 import javax.ws.rs.core.MediaType;
 import org.junit.Assert;
 import org.junit.BeforeClass;
@@ -35,6 +36,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgum
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
@@ -148,6 +150,24 @@ public class XmlBodyReaderTest extends AbstractBodyReaderTest {
         checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII);
     }
 
         checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII);
     }
 
+    @Test
+    public void moduleSubContainerDataPostActionTest() throws Exception {
+        final Optional<DataSchemaNode> dataSchemaNode = schemaContext
+            .findDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
+        final QName cont1QName = QName.create(dataSchemaNode.get().getQName(), "cont1");
+        final QName actionQName = QName.create(dataSchemaNode.get().getQName(), "reset");
+        final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.get().getQName())
+            .node(cont1QName).node(actionQName);
+        final String uri = "instance-identifier-module:cont/cont1/reset";
+        mockBodyReader(uri, this.xmlBodyReader, true);
+        final InputStream inputStream = XmlBodyReaderTest.class
+            .getResourceAsStream("/instanceidentifier/xml/xml_cont_action.xml");
+        final NormalizedNodeContext returnValue = this.xmlBodyReader.readFrom(null, null, null, this.mediaType, null,
+            inputStream);
+        checkNormalizedNodeContext(returnValue);
+        assertTrue(returnValue.getInstanceIdentifierContext().getSchemaNode() instanceof ActionDefinition);
+    }
+
     @Test
     public void moduleSubContainerAugmentDataPostTest() throws Exception {
         final DataSchemaNode dataSchemaNode = schemaContext
     @Test
     public void moduleSubContainerAugmentDataPostTest() throws Exception {
         final DataSchemaNode dataSchemaNode = schemaContext
index 523bee0811947bd98f39d12c353dc15da9cfc02d..c15d545c890175b04dd6fa7f2d4db33bfe7c1359 100644 (file)
@@ -43,6 +43,7 @@ import org.mockito.MockitoAnnotations;
 import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
 import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
+import org.opendaylight.mdsal.dom.api.DOMActionService;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
@@ -58,6 +59,7 @@ import org.opendaylight.mdsal.dom.api.DOMSchemaService;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.restconf.nb.rfc8040.TestUtils;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.restconf.nb.rfc8040.TestUtils;
+import org.opendaylight.restconf.nb.rfc8040.handlers.ActionServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.DOMDataBrokerHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.NotificationServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.DOMDataBrokerHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.NotificationServiceHandler;
@@ -139,6 +141,9 @@ public class JSONRestconfServiceRfc8040ImplTest {
     @Mock
     private DOMRpcService mockRpcService;
 
     @Mock
     private DOMRpcService mockRpcService;
 
+    @Mock
+    private DOMActionService mockActionService;
+
     @Mock
     private DOMSchemaService domSchemaService;
 
     @Mock
     private DOMSchemaService domSchemaService;
 
@@ -190,8 +195,8 @@ public class JSONRestconfServiceRfc8040ImplTest {
         final DOMNotificationService mockNotificationService = mock(DOMNotificationService.class);
         final ServicesWrapper servicesWrapper = ServicesWrapper.newInstance(schemaContextHandler,
                 mountPointServiceHandler, txChainHandler, new DOMDataBrokerHandler(mockDOMDataBroker),
         final DOMNotificationService mockNotificationService = mock(DOMNotificationService.class);
         final ServicesWrapper servicesWrapper = ServicesWrapper.newInstance(schemaContextHandler,
                 mountPointServiceHandler, txChainHandler, new DOMDataBrokerHandler(mockDOMDataBroker),
-                new RpcServiceHandler(mockRpcService), new NotificationServiceHandler(mockNotificationService),
-                domSchemaService);
+                new RpcServiceHandler(mockRpcService), new ActionServiceHandler(mockActionService),
+                new NotificationServiceHandler(mockNotificationService), domSchemaService);
 
         service = new JSONRestconfServiceRfc8040Impl(servicesWrapper, mountPointServiceHandler,
                 schemaContextHandler);
 
         service = new JSONRestconfServiceRfc8040Impl(servicesWrapper, mountPointServiceHandler,
                 schemaContextHandler);
index c42d0dc5316e9f0f094069797699ec8fc1e9140f..101213e5e33e583638be09abd7d9d3995ae0ea4a 100644 (file)
@@ -57,6 +57,7 @@ import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.restconf.common.patch.PatchEntity;
 import org.opendaylight.restconf.common.patch.PatchStatusContext;
 import org.opendaylight.restconf.nb.rfc8040.TestRestconfUtils;
 import org.opendaylight.restconf.common.patch.PatchEntity;
 import org.opendaylight.restconf.common.patch.PatchStatusContext;
 import org.opendaylight.restconf.nb.rfc8040.TestRestconfUtils;
+import org.opendaylight.restconf.nb.rfc8040.handlers.ActionServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
@@ -117,6 +118,8 @@ public class RestconfDataServiceImplTest {
     @Mock
     private DOMDataBroker mountDataBroker;
     @Mock
     @Mock
     private DOMDataBroker mountDataBroker;
     @Mock
+    private ActionServiceHandler actionServiceHandler;
+    @Mock
     private DOMTransactionChain mountTransactionChain;
     @Mock
     private RestconfStreamsSubscriptionService delegRestconfSubscrService;
     private DOMTransactionChain mountTransactionChain;
     @Mock
     private RestconfStreamsSubscriptionService delegRestconfSubscrService;
@@ -198,7 +201,8 @@ public class RestconfDataServiceImplTest {
 
         schemaContextHandler.onGlobalContextUpdated(this.contextRef.get());
         this.dataService = new RestconfDataServiceImpl(schemaContextHandler, this.transactionChainHandler,
 
         schemaContextHandler.onGlobalContextUpdated(this.contextRef.get());
         this.dataService = new RestconfDataServiceImpl(schemaContextHandler, this.transactionChainHandler,
-                DOMMountPointServiceHandler.newInstance(mountPointService), this.delegRestconfSubscrService);
+                DOMMountPointServiceHandler.newInstance(mountPointService), this.delegRestconfSubscrService,
+                this.actionServiceHandler);
         doReturn(Optional.of(this.mountPoint)).when(this.mountPointService)
                 .getMountPoint(any(YangInstanceIdentifier.class));
         doReturn(this.contextRef.get()).when(this.mountPoint).getSchemaContext();
         doReturn(Optional.of(this.mountPoint)).when(this.mountPointService)
                 .getMountPoint(any(YangInstanceIdentifier.class));
         doReturn(this.contextRef.get()).when(this.mountPoint).getSchemaContext();
index 986575cc4874fc401e571dd05a7111495c00ace3..3a23203c781fe2dbd913b72553638c43be398787 100644 (file)
@@ -14,6 +14,7 @@ import javax.inject.Singleton;
 import org.opendaylight.mdsal.binding.api.DataBroker;
 import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractBaseDataBrokerTest;
 import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractConcurrentDataBrokerTest;
 import org.opendaylight.mdsal.binding.api.DataBroker;
 import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractBaseDataBrokerTest;
 import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractConcurrentDataBrokerTest;
+import org.opendaylight.mdsal.dom.api.DOMActionService;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
 import org.opendaylight.mdsal.dom.api.DOMNotificationPublishService;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
 import org.opendaylight.mdsal.dom.api.DOMNotificationPublishService;
@@ -106,6 +107,12 @@ public class InMemoryMdsalModule extends AbstractModule {
         return DOMRpcRouter.newInstance(schemaService).getRpcService();
     }
 
         return DOMRpcRouter.newInstance(schemaService).getRpcService();
     }
 
+    @Provides
+    @Singleton
+    DOMActionService getDOMActionService(DOMSchemaService schemaService) {
+        return DOMRpcRouter.newInstance(schemaService).getActionService();
+    }
+
     @PreDestroy
     public void close() {
     }
     @PreDestroy
     public void close() {
     }
diff --git a/restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/json/json_cont_action.json b/restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/json/json_cont_action.json
new file mode 100644 (file)
index 0000000..82da66d
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "instance-identifier-module:input": {
+    "delay": 600
+  }
+}
\ No newline at end of file
diff --git a/restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/xml/xml_cont_action.xml b/restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/xml/xml_cont_action.xml
new file mode 100644 (file)
index 0000000..9f39ed4
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<input xmlns="instance:identifier:module">
+  <delay>600</delay>
+</input>
\ No newline at end of file
index ffa5db62ad329c37a3cd9c669b7106f819bbb3e8..6779d00de1ebb1dea6bc7c72c0f5406addc070b8 100644 (file)
@@ -1,12 +1,20 @@
 module instance-identifier-module {
 module instance-identifier-module {
+  yang-version 1.1;
   namespace "instance:identifier:module";
 
   prefix "iimodule";
   namespace "instance:identifier:module";
 
   prefix "iimodule";
-  revision 2014-01-17 {
-  }
+  revision 2014-01-17;
 
 
-    container cont {
-        container cont1 {
+  container cont {
+    container cont1 {
+      action reset {
+        input {
+          leaf delay {
+            type uint32;
+            default 0;
+          }
         }
         }
+      }
     }
     }
-}
\ No newline at end of file
+  }
+}