Bug 5679 - implement ietf-restconf-monitoring - streams 03/49103/7
authorJakub Toth <jatoth@cisco.com>
Wed, 7 Dec 2016 19:19:57 +0000 (20:19 +0100)
committerJakub Toth <jatoth@cisco.com>
Tue, 20 Dec 2016 16:09:05 +0000 (16:09 +0000)
  *removed service and tests for getting streams
  *implemented put/delete specific stream to operational DS
  *new mapping for data-change and yang notifications
  *new tests + fixed old

Change-Id: I6c48149ba0bde1e6ba574b514d0fd70a976f1bf4
Signed-off-by: Jakub Toth <jatoth@cisco.com>
21 files changed:
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/api/RestconfService.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapter.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/NotificationListenerAdapter.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/Draft18.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/base/services/api/BaseServicesWrapper.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/base/services/api/RestconfStreamsService.java [deleted file]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/base/services/impl/RestconfStreamsServiceImpl.java [deleted file]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/common/wrapper/services/ServicesWrapperImpl.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImpl.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/CreateStreamUtil.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/ResolveEnumUtil.java [moved from restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/StreamUtil.java with 83% similarity]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/SubscribeToStreamUtil.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtil.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/schema/context/RestconfSchemaUtil.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/rest/services/impl/RestconfStreamsServiceTest.java [deleted file]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImplTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtilTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/utils/schema/context/RestconfSchemaUtilTest.java
restconf/sal-rest-connector/src/test/resources/modules/nested-module.yang
restconf/sal-rest-connector/src/test/resources/notifications/toaster.yang [new file with mode: 0644]

index ddaa7bf3553579233e3d1a61bae385d6ecb2455a..e2775381a14b9e33cffd035257a5ce7729e84c5b 100644 (file)
@@ -27,7 +27,6 @@ import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
 import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
 import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
 import org.opendaylight.restconf.base.services.api.RestconfOperationsService;
-import org.opendaylight.restconf.base.services.api.RestconfStreamsService;
 import org.opendaylight.restconf.restful.services.api.RestconfDataService;
 import org.opendaylight.restconf.restful.services.api.RestconfInvokeOperationsService;
 
@@ -218,7 +217,7 @@ public interface RestconfService {
 
     /**
      * @deprecated do not use this method. It will be replaced by
-     *             {@link RestconfStreamsService#getAvailableStreams(UriInfo)}
+     *             {@link RestconfDataService#readData(String, UriInfo)}
      */
     @Deprecated
     @GET
index ccc30a5616c82b7c082b17048afdc8c493557ec3..331daf7344dda15d7ee0a910697c6b4a9ec98a0b 100644 (file)
@@ -21,11 +21,9 @@ import java.io.StringReader;
 import java.io.UnsupportedEncodingException;
 import java.nio.charset.StandardCharsets;
 import java.text.SimpleDateFormat;
-import java.util.Collection;
 import java.util.Date;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Random;
 import java.util.Set;
 import java.util.concurrent.Executors;
 import java.util.regex.Pattern;
@@ -48,9 +46,15 @@ import javax.xml.xpath.XPathFactory;
 import org.json.JSONObject;
 import org.json.XML;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.restconf.Draft18.MonitoringModule;
+import org.opendaylight.restconf.handlers.SchemaContextHandler;
+import org.opendaylight.restconf.handlers.TransactionChainHandler;
+import org.opendaylight.restconf.parser.IdentifierCodec;
 import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -98,6 +102,8 @@ public class ListenerAdapter implements DOMDataChangeListener {
     private Date start = null;
     private Date stop = null;
     private String filter = null;
+    private TransactionChainHandler transactionChainHandler;
+    private SchemaContextHandler schemaHandler;
 
     /**
      * Creates new {@link ListenerAdapter} listener specified by path and stream
@@ -594,27 +600,6 @@ public class ListenerAdapter implements DOMDataChangeListener {
         textContent.append(qName.getLocalName());
     }
 
-    /**
-     * Generates new prefix which consists of four random characters <a-z>.
-     *
-     * @param prefixes
-     *            Collection of prefixes.
-     * @return New prefix which consists of four random characters <a-z>.
-     */
-    private static String generateNewPrefix(final Collection<String> prefixes) {
-        StringBuilder result = null;
-        final Random random = new Random();
-        do {
-            result = new StringBuilder();
-            for (int i = 0; i < 4; i++) {
-                final int randomNumber = 0x61 + (Math.abs(random.nextInt()) % 26);
-                result.append(Character.toChars(randomNumber));
-            }
-        } while (prefixes.contains(result.toString()));
-
-        return result.toString();
-    }
-
     /**
      * Gets path pointed to data in data store.
      *
@@ -643,9 +628,15 @@ public class ListenerAdapter implements DOMDataChangeListener {
     }
 
     /**
-     * Removes all subscribers and unregisters event bus change recorder form event bus.
+     * Removes all subscribers and unregisters event bus change recorder form
+     * event bus and delete data in DS
      */
     public void close() throws Exception {
+        final DOMDataWriteTransaction wTx = this.transactionChainHandler.get().newWriteOnlyTransaction();
+        wTx.delete(LogicalDatastoreType.OPERATIONAL, IdentifierCodec.deserialize(MonitoringModule.PATH_TO_STREAM_WITHOUT_KEY
+                        + this.path.getLastPathArgument().getNodeType().getLocalName(), this.schemaHandler.get()));
+        wTx.submit().checkedGet();
+
         this.subscribers = new ConcurrentSet<>();
         this.registration.close();
         this.registration = null;
@@ -744,4 +735,29 @@ public class ListenerAdapter implements DOMDataChangeListener {
         this.filter = filter;
     }
 
+    /**
+     * Get output type
+     *
+     * @return outputType
+     */
+    public String getOutputType() {
+        return this.outputType.getName();
+    }
+
+    /**
+     * Transaction chain to delete data in DS on close()
+     *
+     * @param transactionChainHandler
+     *            - creating new write transaction to delete data on close
+     * @param schemaHandler
+     *            - for getting schema to deserialize
+     *            {@link MonitoringModule#PATH_TO_STREAM_WITHOUT_KEY} to
+     *            {@link YangInstanceIdentifier}
+     */
+    public void setCloseVars(final TransactionChainHandler transactionChainHandler,
+            final SchemaContextHandler schemaHandler) {
+        this.transactionChainHandler = transactionChainHandler;
+        this.schemaHandler = schemaHandler;
+    }
+
 }
index 608ddcbca1f8d12e572536b4fba4f2f5b1c4a4a6..2d3ad3a10b3cc4306e0e15ad3abe0a41217dca56 100644 (file)
@@ -42,10 +42,17 @@ import javax.xml.xpath.XPath;
 import javax.xml.xpath.XPathConstants;
 import javax.xml.xpath.XPathFactory;
 import org.json.JSONObject;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.restconf.Draft18.MonitoringModule;
+import org.opendaylight.restconf.handlers.SchemaContextHandler;
+import org.opendaylight.restconf.handlers.TransactionChainHandler;
+import org.opendaylight.restconf.parser.IdentifierCodec;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
@@ -92,6 +99,8 @@ public class NotificationListenerAdapter implements DOMNotificationListener {
     private DOMNotification notification;
     private ListenerRegistration<DOMNotificationListener> registration;
     private Set<Channel> subscribers = new ConcurrentSet<>();
+    private TransactionChainHandler transactionChainHandler;
+    private SchemaContextHandler schemaHandler;
 
     /**
      * Set path of listener and stream name, register event bus.
@@ -229,9 +238,20 @@ public class NotificationListenerAdapter implements DOMNotificationListener {
     }
 
     /**
-     * Reset lists, close registration and unregister bus event.
+     * Reset lists, close registration and unregister bus event and delete data in DS.
      */
     public void close() {
+        final DOMDataWriteTransaction wTx = this.transactionChainHandler.get().newWriteOnlyTransaction();
+        wTx.delete(LogicalDatastoreType.OPERATIONAL,
+                IdentifierCodec.deserialize(
+                        MonitoringModule.PATH_TO_STREAM_WITHOUT_KEY + this.path.getLastComponent().getLocalName(),
+                        this.schemaHandler.get()));
+        try {
+            wTx.submit().checkedGet();
+        } catch (final TransactionCommitFailedException e) {
+            throw new RestconfDocumentedException("Problem while deleting data from DS.", e);
+        }
+
         this.subscribers = new ConcurrentSet<>();
         this.registration.close();
         this.registration = null;
@@ -512,4 +532,30 @@ public class NotificationListenerAdapter implements DOMNotificationListener {
         this.stop = stop;
         this.filter = filter;
     }
+
+    /**
+     * Get outputType of listenere
+     *
+     * @return the outputType
+     */
+    public String getOutputType() {
+        return this.outputType;
+    }
+
+    /**
+     * Transaction chain to delete data in DS on close()
+     *
+     * @param transactionChainHandler
+     *            - creating new write transaction to delete data on close
+     * @param schemaHandler
+     *            - for getting schema to deserialize
+     *            {@link MonitoringModule#PATH_TO_STREAM_WITHOUT_KEY} to
+     *            {@link YangInstanceIdentifier}
+     */
+    public void setCloseVars(final TransactionChainHandler transactionChainHandler,
+            final SchemaContextHandler schemaHandler) {
+        this.transactionChainHandler = transactionChainHandler;
+        this.schemaHandler = schemaHandler;
+
+    }
 }
index d2321dadef7367e4a71c199d7a3e097575a2afec..61660e085aa409d4be30c705f0f0f0f498b610c8 100644 (file)
@@ -198,6 +198,9 @@ public final class Draft18 {
         public static final String NAME = "ietf-restconf-monitoring";
         public static final String NAMESPACE = "urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring";
         public static final String REVISION = "2016-08-15";
+        public static final String PATH_TO_STREAM_WITHOUT_KEY =
+                "ietf-restconf-monitoring:restconf-state/streams/stream=";
+        public static final String PATH_TO_STREAMS = "ietf-restconf-monitoring:restconf-state/streams";
 
         public static Date DATE = null;
         public static URI URI_MODULE = null;
@@ -251,10 +254,6 @@ public final class Draft18 {
         public static final String LEAF_LOCATION_ACCESS_NAME = "location";
         public static final QName LEAF_LOCATION_ACCESS_QNAME = QName.create(MODULE_QNAME, LEAF_LOCATION_ACCESS_NAME);
 
-        // TODO remove after implement ietf-restconf monitoring
-        public static final String STREAMS_CONTAINER_SCHEMA_NODE = "streams";
-        public static final String STREAM_LIST_SCHEMA_NODE = "stream";
-
         /**
          * Constants for capabilities
          */
index 81254058670a051bf644b624da773b2a8bd6076d..ff1bf3ae56fe4f9353eba9d3e75eb08aec4af8a9 100644 (file)
@@ -11,10 +11,9 @@ package org.opendaylight.restconf.base.services.api;
  * Wrapper for all base services:
  * <ul>
  * <li>{@link RestconfOperationsService}
- * <li>{@link RestconfStreamsService}
  * <li>{@link RestconfSchemaService}
  * </ul>
  *
  */
-public interface BaseServicesWrapper extends RestconfOperationsService, RestconfStreamsService, RestconfSchemaService {
+public interface BaseServicesWrapper extends RestconfOperationsService, RestconfSchemaService {
 }
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/base/services/api/RestconfStreamsService.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/base/services/api/RestconfStreamsService.java
deleted file mode 100644 (file)
index 4109a5c..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.restconf.base.services.api;
-
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.UriInfo;
-import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
-import org.opendaylight.restconf.Draft18;
-import org.opendaylight.restconf.utils.RestconfConstants;
-
-/**
- * Container that provides access to the event notification streams supported by
- * the server.
- *
- */
-public interface RestconfStreamsService {
-
-    /**
-     * List of streams supported by the server.
-     *
-     * @param uriInfo
-     *            - URI information
-     * @return {@link NormalizedNodeContext}
-     */
-    @GET
-    @Path("data/ietf-restconf-monitoring:restconf-state/streams")
-    @Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
-            MediaType.APPLICATION_XML, MediaType.TEXT_XML })
-    public NormalizedNodeContext getAvailableStreams(@Context UriInfo uriInfo);
-}
\ No newline at end of file
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/base/services/impl/RestconfStreamsServiceImpl.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/base/services/impl/RestconfStreamsServiceImpl.java
deleted file mode 100644 (file)
index c1815c7..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.restconf.base.services.impl;
-
-import com.google.common.base.Preconditions;
-import java.util.Set;
-import javax.ws.rs.core.UriInfo;
-import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
-import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
-import org.opendaylight.netconf.sal.streams.listeners.Notificator;
-import org.opendaylight.restconf.Draft18;
-import org.opendaylight.restconf.base.services.api.RestconfStreamsService;
-import org.opendaylight.restconf.common.references.SchemaContextRef;
-import org.opendaylight.restconf.handlers.SchemaContextHandler;
-import org.opendaylight.restconf.utils.mapping.RestconfMappingNodeUtil;
-import org.opendaylight.restconf.utils.schema.context.RestconfSchemaUtil;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
-import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
-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.model.api.ContainerSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-
-/**
- * Implementation of {@link RestconfStreamsService}
- *
- */
-public class RestconfStreamsServiceImpl implements RestconfStreamsService {
-
-    private final SchemaContextHandler schemaContextHandler;
-
-    /**
-     * Set {@link SchemaContextHandler} for getting actual {@link SchemaContext}
-     * .
-     *
-     * @param schemaContextHandler
-     *            - handling schema context
-     */
-    public RestconfStreamsServiceImpl(final SchemaContextHandler schemaContextHandler) {
-        this.schemaContextHandler = schemaContextHandler;
-    }
-
-    @Override
-    public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) {
-        final SchemaContextRef schemaContextRef = new SchemaContextRef(this.schemaContextHandler.get());
-        final Set<String> availableStreams = Notificator.getStreamNames();
-
-        final DataSchemaNode streamListSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode(
-                schemaContextRef.getRestconfModule(), Draft18.MonitoringModule.STREAM_LIST_SCHEMA_NODE);
-        Preconditions.checkState(streamListSchemaNode instanceof ListSchemaNode);
-        final CollectionNodeBuilder<MapEntryNode, MapNode> listStreamBuilder = Builders
-                .mapBuilder((ListSchemaNode) streamListSchemaNode);
-
-        for (final String streamValue : availableStreams) {
-            listStreamBuilder.withChild(RestconfMappingNodeUtil.toStreamEntryNode(streamValue, streamListSchemaNode));
-        }
-
-        final DataSchemaNode streamContSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode(
-                schemaContextRef.getRestconfModule(), Draft18.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE);
-        Preconditions.checkState(streamContSchemaNode instanceof ContainerSchemaNode);
-        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> streamsContainerBuilder = Builders
-                .containerBuilder((ContainerSchemaNode) streamContSchemaNode);
-
-        streamsContainerBuilder.withChild(listStreamBuilder.build());
-
-        return new NormalizedNodeContext(
-                new InstanceIdentifierContext<>(null, streamContSchemaNode, null, schemaContextRef.get()),
-                streamsContainerBuilder.build());
-    }
-}
index d3a2daf48d79aa9cef916ace1da6d29138ed8d11..dfc6620287d0298e074b4bda357408ae1251d6c1 100644 (file)
@@ -17,10 +17,8 @@ import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
 import org.opendaylight.restconf.base.services.api.BaseServicesWrapper;
 import org.opendaylight.restconf.base.services.api.RestconfOperationsService;
 import org.opendaylight.restconf.base.services.api.RestconfSchemaService;
-import org.opendaylight.restconf.base.services.api.RestconfStreamsService;
 import org.opendaylight.restconf.base.services.impl.RestconfOperationsServiceImpl;
 import org.opendaylight.restconf.base.services.impl.RestconfSchemaServiceImpl;
-import org.opendaylight.restconf.base.services.impl.RestconfStreamsServiceImpl;
 import org.opendaylight.restconf.handlers.DOMDataBrokerHandler;
 import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.restconf.handlers.NotificationServiceHandler;
@@ -50,7 +48,6 @@ public class ServicesWrapperImpl implements BaseServicesWrapper, TransactionServ
     private RestconfInvokeOperationsService delegRestconfInvokeOpsService;
     private RestconfStreamsSubscriptionService delegRestconfSubscrService;
     private RestconfOperationsService delegRestOpsService;
-    private RestconfStreamsService delegRestStrsService;
     private RestconfSchemaService delegRestSchService;
 
     private ServicesWrapperImpl() {
@@ -74,11 +71,6 @@ public class ServicesWrapperImpl implements BaseServicesWrapper, TransactionServ
         return this.delegRestOpsService.getOperations(identifier, uriInfo);
     }
 
-    @Override
-    public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) {
-        return this.delegRestStrsService.getAvailableStreams(uriInfo);
-    }
-
     @Override
     public SchemaExportContext getSchema(final String mountAndModuleId) {
         return this.delegRestSchService.getSchema(mountAndModuleId);
@@ -142,13 +134,12 @@ public class ServicesWrapperImpl implements BaseServicesWrapper, TransactionServ
             final RpcServiceHandler rpcServiceHandler, final NotificationServiceHandler notificationServiceHandler) {
         this.delegRestOpsService = new RestconfOperationsServiceImpl(schemaCtxHandler, domMountPointServiceHandler);
         this.delegRestSchService = new RestconfSchemaServiceImpl(schemaCtxHandler, domMountPointServiceHandler);
-        this.delegRestStrsService = new RestconfStreamsServiceImpl(schemaCtxHandler);
         this.delegRestconfDataService = new RestconfDataServiceImpl(schemaCtxHandler, transactionChainHandler,
                 domMountPointServiceHandler);
         this.delegRestconfInvokeOpsService = new RestconfInvokeOperationsServiceImpl(rpcServiceHandler,
                 schemaCtxHandler);
         this.delegRestconfSubscrService =
                 new RestconfStreamsSubscriptionServiceImpl(domDataBrokerHandler, notificationServiceHandler,
-                        schemaCtxHandler);
+                        schemaCtxHandler, transactionChainHandler);
     }
 }
index 9852330a0e532a0e84e5989d43c296db0d405df5..3c81d2322cf86cc90ea333426678de67017cb23e 100644 (file)
@@ -20,6 +20,7 @@ import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.restconf.handlers.DOMDataBrokerHandler;
 import org.opendaylight.restconf.handlers.NotificationServiceHandler;
 import org.opendaylight.restconf.handlers.SchemaContextHandler;
+import org.opendaylight.restconf.handlers.TransactionChainHandler;
 import org.opendaylight.restconf.restful.services.api.RestconfStreamsSubscriptionService;
 import org.opendaylight.restconf.restful.utils.RestconfStreamsConstants;
 import org.opendaylight.restconf.restful.utils.SubscribeToStreamUtil;
@@ -45,11 +46,15 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu
 
     private final SchemaContextHandler schemaHandler;
 
+    private final TransactionChainHandler transactionChainHandler;
+
     public RestconfStreamsSubscriptionServiceImpl(final DOMDataBrokerHandler domDataBrokerHandler,
-            final NotificationServiceHandler notificationServiceHandler, final SchemaContextHandler schemaHandler) {
+            final NotificationServiceHandler notificationServiceHandler, final SchemaContextHandler schemaHandler,
+            final TransactionChainHandler transactionChainHandler) {
         this.domDataBrokerHandler = domDataBrokerHandler;
         this.notificationServiceHandler = notificationServiceHandler;
         this.schemaHandler = schemaHandler;
+        this.transactionChainHandler = transactionChainHandler;
     }
 
     @Override
@@ -95,10 +100,11 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu
         URI response = null;
         if (identifier.contains(RestconfStreamsConstants.DATA_SUBSCR)) {
             response =
-                    SubscribeToStreamUtil.dataSubs(identifier, uriInfo, start, stop, this.domDataBrokerHandler, filter);
+                    SubscribeToStreamUtil.notifiDataStream(identifier, uriInfo, start, stop, this.domDataBrokerHandler, filter,
+                            this.transactionChainHandler, this.schemaHandler);
         } else if (identifier.contains(RestconfStreamsConstants.NOTIFICATION_STREAM)) {
-            response = SubscribeToStreamUtil.notifStream(identifier, uriInfo, start, stop,
-                    this.notificationServiceHandler, filter);
+            response = SubscribeToStreamUtil.notifYangStream(identifier, uriInfo, start, stop,
+                    this.notificationServiceHandler, filter, this.transactionChainHandler, this.schemaHandler);
         }
 
         if (response != null) {
index b513f81672799404369c18c74d7321e050dc2040..d199604aff0180d55d3319aceb141591acbab85b 100644 (file)
@@ -162,7 +162,7 @@ public final class CreateStreamUtil {
             return null;
         }
 
-        return StreamUtil.resolveEnum(clazz, (String) value);
+        return ResolveEnumUtil.resolveEnum(clazz, (String) value);
     }
 
     private static YangInstanceIdentifier preparePath(final ContainerNode data, final QName qName) {
similarity index 83%
rename from restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/StreamUtil.java
rename to restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/ResolveEnumUtil.java
index 38cfc326baac45170e41f0cbefa7c696b7fa586f..6ca5624f5e923ddde5dd4989e62fef2ccc0a5b51 100644 (file)
@@ -8,16 +8,12 @@
 package org.opendaylight.restconf.restful.utils;
 
 /**
- * Common util class for stream
- * <ul>
- * <li>{@link SubscribeToStreamUtil}
- * <li>{@link CreateStreamUtil}
- * </ul>
+ * Common util class for resolve enum from String
  *
  */
-public final class StreamUtil {
+public final class ResolveEnumUtil {
 
-    private StreamUtil() {
+    private ResolveEnumUtil() {
         throw new UnsupportedOperationException("Util class");
     }
 
index f6d45e56258e22ae4ef6aa37b2cec16335a2d19c..f387941b67b2a15612c7c78ba5a2dd207a2db5bf 100644 (file)
@@ -23,8 +23,11 @@ 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.ReadFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
@@ -34,18 +37,24 @@ import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
 import org.opendaylight.netconf.sal.streams.listeners.Notificator;
 import org.opendaylight.netconf.sal.streams.websockets.WebSocketServer;
+import org.opendaylight.restconf.Draft18.MonitoringModule;
 import org.opendaylight.restconf.handlers.DOMDataBrokerHandler;
 import org.opendaylight.restconf.handlers.NotificationServiceHandler;
 import org.opendaylight.restconf.handlers.SchemaContextHandler;
+import org.opendaylight.restconf.handlers.TransactionChainHandler;
+import org.opendaylight.restconf.parser.IdentifierCodec;
 import org.opendaylight.restconf.utils.RestconfConstants;
+import org.opendaylight.restconf.utils.mapping.RestconfMappingNodeUtil;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.slf4j.Logger;
@@ -72,11 +81,11 @@ public final class SubscribeToStreamUtil {
      *            - string of enum value
      * @return enum
      */
-    public static <T> T parseURIEnum(final Class<T> clazz, final String value) {
+    private static <T> T parseURIEnum(final Class<T> clazz, final String value) {
         if ((value == null) || value.equals("")) {
             return null;
         }
-        return StreamUtil.resolveEnum(clazz, value);
+        return ResolveEnumUtil.resolveEnum(clazz, value);
     }
 
     /**
@@ -141,7 +150,9 @@ public final class SubscribeToStreamUtil {
     }
 
     /**
-     * Register listeners by streamName in identifier to listen to yang notifications
+     * Register listeners by streamName in identifier to listen to yang
+     * notifications, put or delete info about listener to DS according to
+     * ietf-restconf-monitoring
      *
      * @param identifier
      *            - identifier as stream name
@@ -155,10 +166,16 @@ public final class SubscribeToStreamUtil {
      *            - DOMNotificationService handler for register listeners
      * @param filter
      *            - indicate which subset of all possible events are of interest
+     * @param transactionChainHandler
+     *            - to put new data about stream to DS and delete after close
+     *            listener
+     * @param schemaHandler
+     *            - for getting schema context
      * @return location for listening
      */
-    public static URI notifStream(final String identifier, final UriInfo uriInfo, final Date start, final Date stop,
-            final NotificationServiceHandler notifiServiceHandler, final String filter) {
+    public static URI notifYangStream(final String identifier, final UriInfo uriInfo, Date start, final Date stop,
+            final NotificationServiceHandler notifiServiceHandler, final String filter,
+            final TransactionChainHandler transactionChainHandler, final SchemaContextHandler schemaHandler) {
         final String streamName = Notificator.createStreamNameFromUri(identifier);
         if (Strings.isNullOrEmpty(streamName)) {
             throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
@@ -169,11 +186,6 @@ public final class SubscribeToStreamUtil {
                     ErrorTag.UNKNOWN_ELEMENT);
         }
 
-        for (final NotificationListenerAdapter listener : listeners) {
-            registerToListenNotification(listener, notifiServiceHandler);
-            listener.setQueryParams(start, stop, filter);
-        }
-
         final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
         int notificationPort = RestconfStreamsConstants.NOTIFICATION_PORT;
         try {
@@ -185,9 +197,41 @@ public final class SubscribeToStreamUtil {
         final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme("ws");
         final URI uriToWebsocketServer = uriToWebsocketServerBuilder.replacePath(streamName).build();
 
+        final DOMDataReadWriteTransaction wTx = transactionChainHandler.get().newReadWriteTransaction();
+        final boolean exist = checkExist(schemaHandler, wTx);
+        final Module monitoringModule = schemaHandler.get()
+                .findModuleByNamespaceAndRevision(MonitoringModule.URI_MODULE, MonitoringModule.DATE);
+        if (start == null) {
+            start = new Date();
+        }
+        for (final NotificationListenerAdapter listener : listeners) {
+            registerToListenNotification(listener, notifiServiceHandler);
+            listener.setQueryParams(start, stop, filter);
+            listener.setCloseVars(transactionChainHandler, schemaHandler);
+            final NormalizedNode mapToStreams =
+                    RestconfMappingNodeUtil.mapYangNotificationStreamByIetfRestconfMonitoring(listener.getSchemaPath().getLastComponent(),
+                    schemaHandler.get().getNotifications(), start, listener.getOutputType(),
+                            uriToWebsocketServer, monitoringModule, exist);
+            writeDataToDS(schemaHandler, listener.getSchemaPath().getLastComponent().getLocalName(), wTx, exist, mapToStreams);
+        }
+        submitData(wTx);
+
         return uriToWebsocketServer;
     }
 
+    private static boolean checkExist(final SchemaContextHandler schemaHandler, final DOMDataReadWriteTransaction wTx) {
+        boolean exist;
+        try {
+            exist = wTx
+                    .exists(LogicalDatastoreType.OPERATIONAL, IdentifierCodec
+                            .deserialize(MonitoringModule.PATH_TO_STREAMS, schemaHandler.get()))
+                    .checkedGet();
+        } catch (final ReadFailedException e1) {
+            throw new RestconfDocumentedException("Problem while checking data if exists", e1);
+        }
+        return exist;
+    }
+
     private static void registerToListenNotification(final NotificationListenerAdapter listener,
             final NotificationServiceHandler notificationServiceHandler) {
         if (listener.isListening()) {
@@ -222,7 +266,9 @@ public final class SubscribeToStreamUtil {
     }
 
     /**
-     * Register listener by streamName in identifier to listen to yang notifications
+     * Register listener by streamName in identifier to listen to data change
+     * notifications, put or delete info about listener to DS according to
+     * ietf-restconf-monitoring
      *
      * @param identifier
      *            - identifier as stream name
@@ -236,10 +282,16 @@ public final class SubscribeToStreamUtil {
      *            - DOMDataBroker handler for register listener
      * @param filter
      *            - indicate which subset of all possible events are of interest
+     * @param schemaHandler
+     *            - for getting schema context
+     * @param transactionChainHandler
+     *            - to put new data about stream to DS and delete after close
+     *            listener
      * @return location for listening
      */
-    public static URI dataSubs(final String identifier, final UriInfo uriInfo, final Date start, final Date stop,
-            final DOMDataBrokerHandler domDataBrokerHandler, final String filter) {
+    public static URI notifiDataStream(final String identifier, final UriInfo uriInfo, Date start, final Date stop,
+            final DOMDataBrokerHandler domDataBrokerHandler, final String filter,
+            final TransactionChainHandler transactionChainHandler, final SchemaContextHandler schemaHandler) {
         final Map<String, String> mapOfValues = SubscribeToStreamUtil.mapValuesFromUri(identifier);
 
         final LogicalDatastoreType ds = SubscribeToStreamUtil.parseURIEnum(LogicalDatastoreType.class,
@@ -263,7 +315,12 @@ public final class SubscribeToStreamUtil {
         final ListenerAdapter listener = Notificator.getListenerFor(streamName);
         Preconditions.checkNotNull(listener, "Listener doesn't exist : " + streamName);
 
+        if (start == null) {
+            start = new Date();
+        }
+
         listener.setQueryParams(start, stop, filter);
+        listener.setCloseVars(transactionChainHandler, schemaHandler);
 
         SubscribeToStreamUtil.registration(ds, scope, listener, domDataBrokerHandler.get());
 
@@ -272,9 +329,50 @@ public final class SubscribeToStreamUtil {
         final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
         final UriBuilder uriToWebSocketServer =
                 uriBuilder.port(port).scheme(RestconfStreamsConstants.SCHEMA_SUBSCIBRE_URI);
-        return uriToWebSocketServer.replacePath(streamName).build();
+        final URI uri = uriToWebSocketServer.replacePath(streamName).build();
+
+        final Module monitoringModule = schemaHandler.get()
+                .findModuleByNamespaceAndRevision(MonitoringModule.URI_MODULE, MonitoringModule.DATE);
+        final DOMDataReadWriteTransaction wTx = transactionChainHandler.get().newReadWriteTransaction();
+        final boolean exist = checkExist(schemaHandler, wTx);
+
+        final NormalizedNode mapToStreams = RestconfMappingNodeUtil.mapDataChangeNotificationStreamByIetfRestconfMonitoring(listener.getPath(), start,
+                listener.getOutputType(), uri, monitoringModule, exist, schemaHandler.get());
+        writeDataToDS(schemaHandler, listener.getPath().getLastPathArgument().getNodeType().getLocalName(), wTx, exist,
+                mapToStreams);
+        submitData(wTx);
+        return uri;
+    }
+
+    private static void writeDataToDS(final SchemaContextHandler schemaHandler, final String name,
+            final DOMDataReadWriteTransaction wTx, final boolean exist, final NormalizedNode mapToStreams) {
+        String pathId = "";
+        if (exist) {
+            pathId = MonitoringModule.PATH_TO_STREAM_WITHOUT_KEY + name;
+        } else {
+            pathId = MonitoringModule.PATH_TO_STREAMS;
+        }
+        wTx.merge(LogicalDatastoreType.OPERATIONAL, IdentifierCodec.deserialize(pathId, schemaHandler.get()),
+                mapToStreams);
     }
 
+    private static void submitData(final DOMDataReadWriteTransaction wTx) {
+        try {
+            wTx.submit().checkedGet();
+        } catch (final TransactionCommitFailedException e) {
+            throw new RestconfDocumentedException("Problem while putting data to DS.", e);
+        }
+    }
+
+    /**
+     * Parse input of query parameters - start-time or stop-time - from
+     * {@link DateAndTime} format to {@link Date} format
+     *
+     * @param entry
+     *            - start-time or stop-time as string in {@link DateAndTime}
+     *            format
+     * @return parsed {@link Date} by entry
+     */
     public static Date parseDateFromQueryParam(final Entry<String, List<String>> entry) {
         final DateAndTime event = new DateAndTime(entry.getValue().iterator().next());
         String numOf_ms = "";
index 4b92d5549d9cad4f09f1f45fd3a0ef579b84df81..a081989469eebb9e7e24b827b1ba333dd65a5d41 100644 (file)
@@ -7,17 +7,19 @@
  */
 package org.opendaylight.restconf.utils.mapping;
 
-import com.google.common.base.Preconditions;
+import java.net.URI;
 import java.text.SimpleDateFormat;
 import java.util.Collection;
+import java.util.Date;
 import java.util.Set;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.restconf.Draft18.IetfYangLibrary;
 import org.opendaylight.restconf.Draft18.MonitoringModule;
 import org.opendaylight.restconf.Draft18.MonitoringModule.QueryParams;
-import org.opendaylight.restconf.utils.schema.context.RestconfSchemaUtil;
+import org.opendaylight.restconf.utils.parser.ParserIdentifier;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160621.module.list.Module.ConformanceType;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
@@ -41,7 +43,9 @@ import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 
 /**
  * Util class for mapping nodes
@@ -365,66 +369,6 @@ public final class RestconfMappingNodeUtil {
         throw new RestconfDocumentedException(qnameOfSchema.getLocalName() + " doesn't exist.");
     }
 
-    /**
-     * Mapping {@link MapEntryNode} stream entries of stream to
-     * {@link ListSchemaNode}
-     *
-     * @param streamName
-     *            - stream name
-     * @param streamListSchemaNode
-     *            - mapped {@link DataSchemaNode}
-     * @return {@link MapEntryNode}
-     */
-    public static MapEntryNode toStreamEntryNode(final String streamName, final DataSchemaNode streamListSchemaNode) {
-        Preconditions.checkState(streamListSchemaNode instanceof ListSchemaNode);
-        final ListSchemaNode listStreamSchemaNode = (ListSchemaNode) streamListSchemaNode;
-        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamNodeValues = Builders
-                .mapEntryBuilder(listStreamSchemaNode);
-
-        // STREAM NAME
-        fillListWithLeaf(listStreamSchemaNode, streamNodeValues, RestconfMappingNodeConstants.NAME, streamName);
-
-        // STREAM DESCRIPTION
-        fillListWithLeaf(listStreamSchemaNode, streamNodeValues, RestconfMappingNodeConstants.DESCRIPTION,
-                RestconfMappingStreamConstants.DESCRIPTION);
-
-        // STREAM REPLAY_SUPPORT
-        fillListWithLeaf(listStreamSchemaNode, streamNodeValues, RestconfMappingNodeConstants.REPLAY_SUPPORT,
-                RestconfMappingStreamConstants.REPLAY_SUPPORT);
-
-        // STREAM REPLAY_LOG
-        fillListWithLeaf(listStreamSchemaNode, streamNodeValues, RestconfMappingNodeConstants.REPLAY_LOG,
-                RestconfMappingStreamConstants.REPLAY_LOG);
-
-        // STREAM EVENTS
-        fillListWithLeaf(listStreamSchemaNode, streamNodeValues, RestconfMappingNodeConstants.EVENTS,
-                RestconfMappingStreamConstants.EVENTS);
-
-        return streamNodeValues.build();
-    }
-
-    /**
-     * Method for filling {@link ListSchemaNode} with {@link LeafSchemaNode}
-     *
-     * @param listStreamSchemaNode
-     *            - {@link ListSchemaNode}
-     * @param streamNodeValues
-     *            - filled {@link DataContainerNodeAttrBuilder}
-     * @param nameSchemaNode
-     *            - name of mapped leaf
-     * @param value
-     *            - value for mapped node
-     */
-    private static void fillListWithLeaf(
-            final ListSchemaNode listStreamSchemaNode,
-            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamNodeValues,
-            final String nameSchemaNode, final Object value) {
-        final DataSchemaNode schemaNode = RestconfSchemaUtil
-                .findSchemaNodeInCollection(listStreamSchemaNode.getChildNodes(), nameSchemaNode);
-        Preconditions.checkState(schemaNode instanceof LeafSchemaNode);
-        streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) schemaNode).withValue(value).build());
-    }
-
     /**
      * Map capabilites by ietf-restconf-monitoring
      *
@@ -459,7 +403,7 @@ public final class RestconfMappingNodeUtil {
      *            - builder of parent for children
      * @param leafListSchema
      */
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings({ "unchecked", "rawtypes" })
     private static void fillLeafListCapa(final ListNodeBuilder builder, final LeafListSchemaNode leafListSchema) {
         builder.withChild(leafListEntryBuild(leafListSchema, QueryParams.DEPTH));
         builder.withChild(leafListEntryBuild(leafListSchema, QueryParams.FIELDS));
@@ -500,4 +444,170 @@ public final class RestconfMappingNodeUtil {
         throw new RestconfDocumentedException(
                 childQName.getLocalName() + " doesn't exist in container " + MonitoringModule.CONT_RESTCONF_STATE_NAME);
     }
+
+    /**
+     * Map data of yang notification to normalized node according to
+     * ietf-restconf-monitoring
+     *
+     * @param notifiQName
+     *            - qname of notification from listener
+     * @param notifications
+     *            - list of notifications for find schema of notification by
+     *            notifiQName
+     * @param start
+     *            - start-time query parameter of notification
+     * @param outputType
+     *            - output type of notification
+     * @param uri
+     *            - location of registered listener for sending data of
+     *            notification
+     * @param monitoringModule
+     *            - ietf-restconf-monitoring module
+     * @param existParent
+     *            - true if data of parent -
+     *            ietf-restconf-monitoring:restconf-state/streams - exist in DS
+     * @return mapped data of notification - map entry node if parent exists,
+     *         container streams with list and map entry node if not
+     */
+    @SuppressWarnings("rawtypes")
+    public static NormalizedNode mapYangNotificationStreamByIetfRestconfMonitoring(final QName notifiQName,
+            final Set<NotificationDefinition> notifications, final Date start, final String outputType,
+            final URI uri, final Module monitoringModule, final boolean existParent) {
+        for (final NotificationDefinition notificationDefinition : notifications) {
+            if (notificationDefinition.getQName().equals(notifiQName)) {
+                final DataSchemaNode streamListSchema = ((ContainerSchemaNode) ((ContainerSchemaNode) monitoringModule
+                        .getDataChildByName(MonitoringModule.CONT_RESTCONF_STATE_QNAME))
+                                .getDataChildByName(MonitoringModule.CONT_STREAMS_QNAME))
+                                        .getDataChildByName(MonitoringModule.LIST_STREAM_QNAME);
+                final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamEntry =
+                        Builders.mapEntryBuilder((ListSchemaNode) streamListSchema);
+
+                final ListSchemaNode listSchema = ((ListSchemaNode) streamListSchema);
+                prepareLeafAndFillEntryBuilder(streamEntry,
+                        listSchema.getDataChildByName(MonitoringModule.LEAF_NAME_STREAM_QNAME),
+                        notificationDefinition.getQName().getLocalName());
+                if ((notificationDefinition.getDescription() != null)
+                        && !notificationDefinition.getDescription().equals("")) {
+                    prepareLeafAndFillEntryBuilder(streamEntry,
+                            listSchema.getDataChildByName(MonitoringModule.LEAF_DESCR_STREAM_QNAME),
+                            notificationDefinition.getDescription());
+                }
+                prepareLeafAndFillEntryBuilder(streamEntry,
+                        listSchema.getDataChildByName(MonitoringModule.LEAF_REPLAY_SUPP_STREAM_QNAME), true);
+                prepareLeafAndFillEntryBuilder(streamEntry,
+                        listSchema.getDataChildByName(MonitoringModule.LEAF_START_TIME_STREAM_QNAME),
+                        new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'XXX").format(start));
+                prepareListAndFillEntryBuilder(streamEntry,
+                        (ListSchemaNode) listSchema.getDataChildByName(MonitoringModule.LIST_ACCESS_STREAM_QNAME),
+                        outputType, uri);
+
+                if (!existParent) {
+                    final DataSchemaNode contStreamsSchema = ((ContainerSchemaNode) monitoringModule
+                            .getDataChildByName(MonitoringModule.CONT_RESTCONF_STATE_QNAME))
+                                    .getDataChildByName(MonitoringModule.CONT_STREAMS_QNAME);
+                    return Builders.containerBuilder((ContainerSchemaNode) contStreamsSchema).withChild(Builders
+                            .mapBuilder((ListSchemaNode) streamListSchema).withChild(streamEntry.build())
+                            .build()).build();
+                }
+                return streamEntry.build();
+            }
+        }
+
+        throw new RestconfDocumentedException(notifiQName + " doesn't exist in any modul");
+    }
+
+    private static void prepareListAndFillEntryBuilder(
+            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamEntry,
+            final ListSchemaNode listSchemaNode, final String outputType, final URI uriToWebsocketServer) {
+        final CollectionNodeBuilder<MapEntryNode, MapNode> accessListBuilder = Builders.mapBuilder(listSchemaNode);
+        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> entryAccessList =
+                Builders.mapEntryBuilder(listSchemaNode);
+        prepareLeafAndFillEntryBuilder(entryAccessList,
+                listSchemaNode.getDataChildByName(MonitoringModule.LEAF_ENCODING_ACCESS_QNAME), outputType);
+        prepareLeafAndFillEntryBuilder(entryAccessList,
+                listSchemaNode.getDataChildByName(MonitoringModule.LEAF_LOCATION_ACCESS_QNAME),
+                uriToWebsocketServer.toString());
+        streamEntry.withChild(accessListBuilder.withChild(entryAccessList.build()).build());
+    }
+
+    /**
+     * @param streamEntry
+     * @param dataChildByName
+     * @param localName
+     */
+    private static void prepareLeafAndFillEntryBuilder(
+            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamEntry,
+            final DataSchemaNode leafSchema, final Object value) {
+        streamEntry.withChild(Builders.leafBuilder((LeafSchemaNode) leafSchema).withValue(value).build());
+    }
+
+    /**
+     * Map data of data change notification to normalized node according to
+     * ietf-restconf-monitoring
+     *
+     * @param path
+     *            - path of data to listen on
+     * @param start
+     *            - start-time query parameter of notification
+     * @param outputType
+     *            - output type of notification
+     * @param uri
+     *            - location of registered listener for sending data of
+     *            notification
+     * @param monitoringModule
+     *            - ietf-restconf-monitoring module
+     * @param existParent
+     *            - true if data of parent -
+     *            ietf-restconf-monitoring:restconf-state/streams - exist in DS
+     * @param schemaContext
+     *            - schemaContext for parsing instance identifier to get schema
+     *            node of data
+     * @return mapped data of notification - map entry node if parent exists,
+     *         container streams with list and map entry node if not
+     */
+    @SuppressWarnings("rawtypes")
+    public static NormalizedNode mapDataChangeNotificationStreamByIetfRestconfMonitoring(
+            final YangInstanceIdentifier path, final Date start,
+            final String outputType, final URI uri, final Module monitoringModule, final boolean existParent,
+            final SchemaContext schemaContext) {
+        final SchemaNode schemaNode = ParserIdentifier
+                .toInstanceIdentifier(ParserIdentifier.stringFromYangInstanceIdentifier(path, schemaContext),
+                        schemaContext, null)
+                .getSchemaNode();
+        final DataSchemaNode streamListSchema = ((ContainerSchemaNode) ((ContainerSchemaNode) monitoringModule
+                .getDataChildByName(MonitoringModule.CONT_RESTCONF_STATE_QNAME))
+                        .getDataChildByName(MonitoringModule.CONT_STREAMS_QNAME))
+                                .getDataChildByName(MonitoringModule.LIST_STREAM_QNAME);
+        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamEntry =
+                Builders.mapEntryBuilder((ListSchemaNode) streamListSchema);
+
+        final ListSchemaNode listSchema = ((ListSchemaNode) streamListSchema);
+        prepareLeafAndFillEntryBuilder(streamEntry,
+                listSchema.getDataChildByName(MonitoringModule.LEAF_NAME_STREAM_QNAME),
+                schemaNode.getQName().getLocalName());
+        if ((schemaNode.getDescription() != null) && !schemaNode.getDescription().equals("")) {
+            prepareLeafAndFillEntryBuilder(streamEntry,
+                    listSchema.getDataChildByName(MonitoringModule.LEAF_DESCR_STREAM_QNAME),
+                    schemaNode.getDescription());
+        }
+        prepareLeafAndFillEntryBuilder(streamEntry,
+                listSchema.getDataChildByName(MonitoringModule.LEAF_REPLAY_SUPP_STREAM_QNAME), true);
+        prepareLeafAndFillEntryBuilder(streamEntry,
+                listSchema.getDataChildByName(MonitoringModule.LEAF_START_TIME_STREAM_QNAME),
+                new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'XXX").format(start));
+        prepareListAndFillEntryBuilder(streamEntry,
+                (ListSchemaNode) listSchema.getDataChildByName(MonitoringModule.LIST_ACCESS_STREAM_QNAME), outputType,
+                uri);
+
+        if (!existParent) {
+            final DataSchemaNode contStreamsSchema = ((ContainerSchemaNode) monitoringModule
+                    .getDataChildByName(MonitoringModule.CONT_RESTCONF_STATE_QNAME))
+                            .getDataChildByName(MonitoringModule.CONT_STREAMS_QNAME);
+            return Builders
+                    .containerBuilder((ContainerSchemaNode) contStreamsSchema).withChild(Builders
+                            .mapBuilder((ListSchemaNode) streamListSchema).withChild(streamEntry.build()).build())
+                    .build();
+        }
+        return streamEntry.build();
+    }
 }
index 2b7e7559e3e30b64a458df493858b7103de415f3..18faa8c388ecd563c51f43eea2644df30b0c3b12 100644 (file)
@@ -7,17 +7,11 @@
  */
 package org.opendaylight.restconf.utils.schema.context;
 
-import com.google.common.base.Preconditions;
 import java.util.Collection;
-import java.util.Set;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
-import org.opendaylight.restconf.Draft18;
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
-import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 
 /**
@@ -30,73 +24,6 @@ public final class RestconfSchemaUtil {
         throw new UnsupportedOperationException("Util class");
     }
 
-    /**
-     * Get {@link DataSchemaNode} from {@link Module} Restconf module by
-     * {@link String} schema node name.
-     *
-     * @param restconfModule
-     *            - restconf module
-     * @param schemaNodeName
-     *            - schema node name
-     * @return {@link DataSchemaNode}
-     */
-    public static DataSchemaNode getRestconfSchemaNode(final Module restconfModule, final String schemaNodeName) {
-
-        final Set<GroupingDefinition> groupings = restconfModule.getGroupings();
-        final GroupingDefinition restGroup = findSchemaNodeInCollection(groupings,
-                Draft18.RestconfModule.RESTCONF_GROUPING_SCHEMA_NODE);
-        final Collection<DataSchemaNode> childNodes = restGroup.getChildNodes();
-        final DataSchemaNode restCont = childNodes.iterator().next();
-
-        return findSchemaNode(restCont, schemaNodeName);
-    }
-
-    /**
-     * Find specific {@link DataSchemaNode} child in {@link DataNodeContainer}
-     * by {@link String} schema node name.
-     *
-     * @param restCont
-     *            - restconf container
-     * @param schemaNodeName
-     *            - schema node name
-     * @return {@link DataSchemaNode}
-     */
-    private static DataSchemaNode findSchemaNode(final DataSchemaNode restCont, final String schemaNodeName) {
-        switch (schemaNodeName) {
-            //MODULES
-            case Draft18.RestconfModule.MODULE_LIST_SCHEMA_NODE:
-                final DataSchemaNode moduleListSchNode = findSchemaNodeInCollection(
-                        ((DataNodeContainer) findSchemaNode(restCont,
-                                Draft18.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE)).getChildNodes(),
-                        Draft18.RestconfModule.MODULE_LIST_SCHEMA_NODE);
-                Preconditions.checkNotNull(moduleListSchNode);
-                return moduleListSchNode;
-            case Draft18.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE:
-                final DataSchemaNode modulesContSchNode = findSchemaNodeInCollection(((DataNodeContainer) restCont).getChildNodes(),
-                        Draft18.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE);
-                Preconditions.checkNotNull(modulesContSchNode);
-                return modulesContSchNode;
-
-            //STREAMS
-            case Draft18.MonitoringModule.STREAM_LIST_SCHEMA_NODE:
-                final DataSchemaNode streamListSchNode = findSchemaNodeInCollection(
-                        ((DataNodeContainer) findSchemaNode(restCont,
-                                Draft18.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE)).getChildNodes(),
-                        Draft18.MonitoringModule.STREAM_LIST_SCHEMA_NODE);
-                Preconditions.checkNotNull(streamListSchNode);
-                return streamListSchNode;
-            case Draft18.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE:
-                final DataSchemaNode streamsContSchNode = findSchemaNodeInCollection(
-                        ((DataNodeContainer) restCont).getChildNodes(),
-                        Draft18.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE);
-                Preconditions.checkNotNull(streamsContSchNode);
-                return streamsContSchNode;
-            default:
-                throw new RestconfDocumentedException("Schema node " + schemaNodeName + " does not exist in module.",
-                        ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
-        }
-    }
-
     /**
      * Find child of {@link SchemaNode} in {@link Collection} by {@link String}
      * schema node name.
index 70f854973a9bc7ed023190b7b99e53251fa3ec49..35c6e1de33c609f56312c5416dac7f19362e4b37 100644 (file)
@@ -31,6 +31,7 @@ import org.junit.Before;
 import org.junit.Test;
 import org.mockito.InOrder;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
@@ -46,6 +47,7 @@ import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
 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.api.DOMTransactionChain;
 import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
 import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
@@ -60,6 +62,8 @@ import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
 import org.opendaylight.netconf.sal.streams.listeners.Notificator;
+import org.opendaylight.restconf.handlers.SchemaContextHandler;
+import org.opendaylight.restconf.handlers.TransactionChainHandler;
 import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -326,7 +330,7 @@ public class BrokerFacadeTest {
      * Create, register, close and remove notification listener.
      */
     @Test
-    public void testRegisterToListenNotificationChanges() {
+    public void testRegisterToListenNotificationChanges() throws Exception {
         // create test notification listener
         final String identifier = "create-notification-stream/toaster:toastDone";
         final SchemaPath path = SchemaPath.create(true,
@@ -350,6 +354,16 @@ public class BrokerFacadeTest {
         // registrations should be invoked only once
         verify(this.domNotification, times(1)).registerNotificationListener(listener, listener.getSchemaPath());
 
+        final DOMTransactionChain transactionChain = mock(DOMTransactionChain.class);
+        final DOMDataWriteTransaction wTx = mock(DOMDataWriteTransaction.class);
+        final CheckedFuture checked = Futures.immediateCheckedFuture("");
+        when(wTx.submit()).thenReturn(checked);
+        when(transactionChain.newWriteOnlyTransaction()).thenReturn(wTx);
+        final TransactionChainHandler transactionChainHandler = new TransactionChainHandler(transactionChain);
+        final SchemaContextHandler schemaHandler = Mockito.mock(SchemaContextHandler.class);
+        final SchemaContext schCtx = TestUtils.loadSchemaContext("/modules");
+        when(schemaHandler.get()).thenReturn(schCtx);
+        listener.setCloseVars(transactionChainHandler, schemaHandler);
         // close and remove test notification listener
         listener.close();
         Notificator.removeNotificationListenerIfNoSubscriberExists(listener);
diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/rest/services/impl/RestconfStreamsServiceTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/rest/services/impl/RestconfStreamsServiceTest.java
deleted file mode 100644 (file)
index 2ceacd9..0000000
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.restconf.rest.services.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.when;
-import static org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.EMPTY;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import java.util.AbstractMap;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
-import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
-import org.opendaylight.netconf.sal.streams.listeners.Notificator;
-import org.opendaylight.restconf.Draft18;
-import org.opendaylight.restconf.base.services.api.RestconfStreamsService;
-import org.opendaylight.restconf.base.services.impl.RestconfStreamsServiceImpl;
-import org.opendaylight.restconf.handlers.SchemaContextHandler;
-import org.opendaylight.restconf.utils.mapping.RestconfMappingNodeConstants;
-import org.opendaylight.restconf.utils.mapping.RestconfMappingStreamConstants;
-import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
-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.DataContainerChild;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
-import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableDataContainerAttrNode;
-import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-
-/**
- * Unit tests for {@link RestconfStreamsServiceImpl}
- */
-public class RestconfStreamsServiceTest {
-    private static final List<String> expectedStreams = Arrays.asList(new String[] {"stream-1", "stream-2", "stream-3"});
-
-    @Rule public ExpectedException thrown = ExpectedException.none();
-
-    @Mock private SchemaContextHandler contextHandler;
-    @Mock private SchemaContext mockSchemaContext;
-
-    // service under test
-    private RestconfStreamsService streamsService;
-
-    // schema context with testing Restconf modules
-    private SchemaContext schemaContext;
-
-    @Before
-    public void setup() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        this.schemaContext = TestRestconfUtils.loadSchemaContext("/modules/restconf-module-testing");
-        this.streamsService = new RestconfStreamsServiceImpl(this.contextHandler);
-    }
-
-    @BeforeClass
-    public static void setupTestStreams() {
-        // clean
-        Notificator.removeAllListeners();
-
-        // put test streams
-        Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(0),
-                NotificationOutputType.XML);
-        Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(1),
-                NotificationOutputType.XML);
-        Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(2),
-                NotificationOutputType.XML);
-    }
-
-    @AfterClass
-    public static void removeTestStreams() {
-        Notificator.removeAllListeners();
-    }
-
-    /**
-     * Test of successful initialization of streams service.
-     */
-    @Test
-    public void restconfStreamsServiceImplInitTest() {
-        assertNotNull("Streams service should be initialized and not null", this.streamsService);
-    }
-
-    /**
-     * Positive test to get all available streams supported by the server. Loaded streams are compared to expected
-     * streams.
-     */
-    @Test
-    public void getAvailableStreamsTest() throws Exception {
-        // prepare conditions - get correct Restconf module
-        when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
-        when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft18.RestconfModule.IETF_RESTCONF_QNAME
-                .getNamespace(), Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
-                .thenReturn(getTestingRestconfModule("ietf-restconf"));
-
-        // make test
-        final NormalizedNodeContext nodeContext = this.streamsService.getAvailableStreams(null);
-
-        // verify loaded streams
-        assertNotNull("Normalized node context should not be null", nodeContext);
-        verifyStreams(((ContainerNode) nodeContext.getData()).getValue());
-    }
-
-    /**
-     * Try to get all available streams supported by the server when current <code>SchemaContext</code> is
-     * <code>null</code> expecting <code>NullPointerException</code>.
-     */
-    @Test
-    public void getAvailableStreamsNullSchemaContextNegativeTest() {
-        // prepare conditions - returned SchemaContext is null
-        when(this.contextHandler.get()).thenReturn(null);
-
-        // make test
-        this.thrown.expect(NullPointerException.class);
-        this.streamsService.getAvailableStreams(null);
-    }
-
-    /**
-     * Try to get all available streams supported by the server when Restconf module is missing in
-     * <code>SchemaContext</code> expecting <code>NullPointerException</code>.
-     */
-    @Test
-    public void getAvailableStreamsMissingRestconfModuleNegativeTest() {
-        // prepare conditions - get null Restconf module
-        when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
-        when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft18.RestconfModule.IETF_RESTCONF_QNAME
-                .getNamespace(), Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision())).thenReturn(null);
-
-        // make test
-        this.thrown.expect(NullPointerException.class);
-        this.streamsService.getAvailableStreams(null);
-    }
-
-    /**
-     * There are multiple testing Restconf modules for different test cases. It is possible to distinguish them by
-     * name or by namespace. This method is looking for Restconf test module by its name.
-     * @param s Testing Restconf module name
-     * @return Restconf module
-     */
-    private Module getTestingRestconfModule(final String s) {
-        return this.schemaContext.findModuleByName(s, Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
-    }
-
-    /**
-     * Verify loaded streams
-     * @param streams Streams to be verified
-     */
-    private void verifyStreams(final Collection<DataContainerChild<? extends PathArgument, ?>> streams) {
-        assertNotNull("Collection of streams should not be empty", streams);
-        assertFalse("Collection of streams should not be empty", Iterables.isEmpty(streams));
-        final Iterator<DataContainerChild<? extends PathArgument, ?>> iterator = streams.iterator();
-
-        final List<String> loadedStreams = new ArrayList<>();
-        for (final Object stream : (Collection<?>) iterator.next().getValue()) {
-            final Iterator mapEntries = ((AbstractImmutableDataContainerAttrNode) stream)
-                    .getChildren().entrySet().iterator();
-
-            final List<String> allowedKeys = Lists.newArrayList(
-                    RestconfMappingNodeConstants.NAME,
-                    RestconfMappingNodeConstants.DESCRIPTION,
-                    RestconfMappingNodeConstants.REPLAY_SUPPORT,
-                    RestconfMappingNodeConstants.REPLAY_LOG,
-                    RestconfMappingNodeConstants.EVENTS);
-
-            while (mapEntries.hasNext()) {
-                final Map.Entry e = ((AbstractMap.SimpleImmutableEntry) mapEntries.next());
-                final String key = ((NodeIdentifier) e.getKey()).getNodeType().getLocalName();
-
-                assertTrue("Not allowed key", allowedKeys.contains(key));
-
-                switch (key) {
-                    case RestconfMappingNodeConstants.NAME :
-                        loadedStreams.add((String) ((LeafNode) e.getValue()).getValue());
-                        break;
-                    case RestconfMappingNodeConstants.DESCRIPTION :
-                        assertEquals("Stream description value is not as expected",
-                                RestconfMappingStreamConstants.DESCRIPTION, ((LeafNode) e.getValue()).getValue());
-                        break;
-                    case RestconfMappingNodeConstants.REPLAY_SUPPORT :
-                        assertEquals("Stream replay support value is not as expected",
-                                RestconfMappingStreamConstants.REPLAY_SUPPORT, ((LeafNode) e.getValue()).getValue());
-                        break;
-                    case RestconfMappingNodeConstants.REPLAY_LOG :
-                        assertEquals("Stream replay log value is not as expected",
-                                RestconfMappingStreamConstants.REPLAY_LOG, ((LeafNode) e.getValue()).getValue());
-                        break;
-                    case RestconfMappingNodeConstants.EVENTS :
-                        assertEquals("Stream events value is not as expected",
-                                RestconfMappingStreamConstants.EVENTS, ((LeafNode) e.getValue()).getValue());
-                        break;
-                }
-            }
-        }
-
-        // sort and compare
-        loadedStreams.sort((s1, s2) -> s1.compareTo(s2));
-        assertEquals("Returned streams are not as expected", expectedStreams, loadedStreams);
-    }
-}
index 323198fe549d448e1be42ad8d898222e995044a7..619cab5013538f09ec4588af57e0425f63528db7 100644 (file)
@@ -13,6 +13,7 @@ import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
 import java.lang.reflect.Field;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -33,6 +34,7 @@ import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
@@ -44,6 +46,8 @@ import org.opendaylight.restconf.handlers.DOMDataBrokerHandler;
 import org.opendaylight.restconf.handlers.NotificationServiceHandler;
 import org.opendaylight.restconf.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.handlers.TransactionChainHandler;
+import org.opendaylight.restconf.parser.IdentifierCodec;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 
 public class RestconfStreamsSubscriptionServiceImplTest {
@@ -58,6 +62,8 @@ public class RestconfStreamsSubscriptionServiceImplTest {
     private UriInfo uriInfo;
     @Mock
     private NotificationServiceHandler notificationServiceHandler;
+    @Mock
+    private TransactionChainHandler transactionHandler;
 
     private SchemaContextHandler schemaHandler;
 
@@ -67,9 +73,16 @@ public class RestconfStreamsSubscriptionServiceImplTest {
 
         final TransactionChainHandler txHandler = Mockito.mock(TransactionChainHandler.class);
         final DOMTransactionChain domTx = Mockito.mock(DOMTransactionChain.class);
+        Mockito.when(this.transactionHandler.get()).thenReturn(domTx);
         Mockito.when(txHandler.get()).thenReturn(domTx);
         final DOMDataWriteTransaction wTx = Mockito.mock(DOMDataWriteTransaction.class);
         Mockito.when(domTx.newWriteOnlyTransaction()).thenReturn(wTx);
+        final DOMDataReadWriteTransaction rwTx = Mockito.mock(DOMDataReadWriteTransaction.class);
+        final CheckedFuture checkedFuture = Futures.immediateCheckedFuture(true);
+        Mockito.when(rwTx.exists(Mockito.any(), Mockito.any())).thenReturn(checkedFuture);
+        final CheckedFuture checkedFutureEmpty = Futures.immediateCheckedFuture("");
+        Mockito.when(rwTx.submit()).thenReturn(checkedFutureEmpty);
+        Mockito.when(domTx.newReadWriteTransaction()).thenReturn(rwTx);
         final CheckedFuture checked = Mockito.mock(CheckedFuture.class);
         Mockito.when(wTx.submit()).thenReturn(checked);
         final Object valueObj = null;
@@ -109,12 +122,16 @@ public class RestconfStreamsSubscriptionServiceImplTest {
     }
 
     @Test
-    public void testSubscribeToStream() {
+    public void testSubscribeToStream() throws Exception {
         final UriBuilder uriBuilder = UriBuilder.fromUri(uri);
+        final ListenerAdapter createListener = Notificator.createListener(
+                IdentifierCodec.deserialize("toaster:toaster/toasterStatus", this.schemaHandler.get()),
+                "data-change-event-subscription/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE",
+                NotificationOutputType.XML);
         doReturn(uriBuilder).when(this.uriInfo).getAbsolutePathBuilder();
         final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService =
                 new RestconfStreamsSubscriptionServiceImpl(this.dataBrokerHandler, this.notificationServiceHandler,
-                        this.schemaHandler);
+                        this.schemaHandler, this.transactionHandler);
         final NormalizedNodeContext response = streamsSubscriptionService
                 .subscribeToStream(
                         "data-change-event-subscription/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE",
@@ -130,7 +147,7 @@ public class RestconfStreamsSubscriptionServiceImplTest {
         doReturn(uriBuilder).when(this.uriInfo).getAbsolutePathBuilder();
         final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService =
                 new RestconfStreamsSubscriptionServiceImpl(this.dataBrokerHandler, this.notificationServiceHandler,
-                        this.schemaHandler);
+                        this.schemaHandler, this.transactionHandler);
         streamsSubscriptionService.subscribeToStream("toaster:toaster/toasterStatus/scope=ONE", this.uriInfo);
     }
 
@@ -140,7 +157,7 @@ public class RestconfStreamsSubscriptionServiceImplTest {
         doReturn(uriBuilder).when(this.uriInfo).getAbsolutePathBuilder();
         final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService =
                 new RestconfStreamsSubscriptionServiceImpl(this.dataBrokerHandler, this.notificationServiceHandler,
-                        this.schemaHandler);
+                        this.schemaHandler, this.transactionHandler);
         streamsSubscriptionService.subscribeToStream("toaster:toaster/toasterStatus/datastore=OPERATIONAL",
                 this.uriInfo);
     }
index c078a8468fb44a948007c4e1057fcc1be9e41ace..f650f85a567426cce83910cff4a9b8b03fec7ebc 100644 (file)
@@ -8,22 +8,16 @@
 package org.opendaylight.restconf.utils.mapping;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
-import com.google.common.collect.Sets;
-import java.util.AbstractMap;
+import java.net.URI;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
+import java.util.Date;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
 import org.junit.Assert;
 import org.junit.Before;
@@ -34,15 +28,11 @@ import org.junit.rules.ExpectedException;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
-import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
-import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
-import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
-import org.opendaylight.restconf.Draft18;
 import org.opendaylight.restconf.Draft18.IetfYangLibrary;
 import org.opendaylight.restconf.Draft18.MonitoringModule;
 import org.opendaylight.restconf.Draft18.MonitoringModule.QueryParams;
 import org.opendaylight.restconf.Draft18.RestconfModule;
-import org.opendaylight.restconf.utils.schema.context.RestconfSchemaUtil;
+import org.opendaylight.restconf.utils.parser.ParserIdentifier;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -56,9 +46,6 @@ import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableDataContainerAttrNode;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
@@ -80,20 +67,17 @@ public class RestconfMappingNodeUtilTest {
 
     private static Set<Module> modules;
     private static SchemaContext schemaContext;
-    private static SchemaContext schemaContextCapabilites;
+    private static SchemaContext schemaContextMonitoring;
 
     private static Set<Module> modulesRest;
 
-    private Set<DataSchemaNode> allStreamChildNodes;
-
     @BeforeClass
     public static void loadTestSchemaContextAndModules() throws Exception {
-        RestconfMappingNodeUtilTest.schemaContext = TestRestconfUtils.loadSchemaContext(
+        schemaContext = TestRestconfUtils.loadSchemaContext(
                 "/modules/restconf-module-testing");
-        RestconfMappingNodeUtilTest.schemaContextCapabilites = TestRestconfUtils.loadSchemaContext("/modules");
-        RestconfMappingNodeUtilTest.modules = schemaContextCapabilites.getModules();
-        RestconfMappingNodeUtilTest.modulesRest =
-                TestRestconfUtils.loadSchemaContext("/modules/restconf-module-testing").getModules();
+        schemaContextMonitoring = TestRestconfUtils.loadSchemaContext("/modules");
+        modules = schemaContextMonitoring.getModules();
+        modulesRest = TestRestconfUtils.loadSchemaContext("/modules/restconf-module-testing").getModules();
     }
 
     @Before
@@ -106,9 +90,6 @@ public class RestconfMappingNodeUtilTest {
                 QName.create("", RestconfMappingNodeConstants.REPLAY_SUPPORT));
         when(this.leafReplayLog.getQName()).thenReturn(QName.create(RestconfMappingNodeConstants.REPLAY_LOG));
         when(this.leafEvents.getQName()).thenReturn(QName.create("", RestconfMappingNodeConstants.EVENTS));
-
-        this.allStreamChildNodes = Sets.newHashSet(
-                this.leafName, this.leafDescription, this.leafReplaySupport, this.leafReplayLog, this.leafEvents);
     }
 
     /**
@@ -130,7 +111,7 @@ public class RestconfMappingNodeUtilTest {
 
     @Test
     public void restconfStateCapabilitesTest() {
-        final Module monitoringModule = schemaContextCapabilites
+        final Module monitoringModule = schemaContextMonitoring
                 .findModuleByNamespaceAndRevision(MonitoringModule.URI_MODULE, MonitoringModule.DATE);
         final NormalizedNode<NodeIdentifier, Collection<DataContainerChild<? extends PathArgument, ?>>> normNode =
                 RestconfMappingNodeUtil.mapCapabilites(monitoringModule);
@@ -154,251 +135,67 @@ public class RestconfMappingNodeUtilTest {
         Assert.assertTrue(listOfValues.contains(QueryParams.WITH_DEFAULTS));
     }
 
-    /**
-     * Positive test of writing one stream to {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} and checking if stream
-     * was correctly written.
-     */
-    @Test
-    public void toStreamEntryNodeTest() {
-        // test stream name
-        final String stream1 = "stream-1";
-
-        // get list stream node from Restconf module
-        final ListSchemaNode listStream = (ListSchemaNode) RestconfSchemaUtil.getRestconfSchemaNode(
-                getTestingRestconfModule("ietf-restconf"), MonitoringModule.STREAM_LIST_SCHEMA_NODE);
-
-        // write stream to list stream node
-        final MapEntryNode mapEntryNode = RestconfMappingNodeUtil.toStreamEntryNode(stream1, listStream);
-
-        // verify
-        verifyStream(stream1, mapEntryNode);
-    }
-
-    /**
-     * Try to map streams when {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} is <code>null</code>.
-     * Test is expected to fail catching <code>IllegalStateException</code>.
-     */
-    @Test
-    public void toStreamEntryNodeNullListStreamNegativeTest() {
-        this.thrown.expect(IllegalStateException.class);
-        RestconfMappingNodeUtil.toStreamEntryNode("stream-1", null);
-    }
-
-    /**
-     * Test trying to map streams to {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} which is not of type list.
-     * Test is expected to fail with <code>IllegalStateException</code>.
-     */
     @Test
-    public void toStreamEntryNodeIllegalListStreamNegativeTest() {
-        this.thrown.expect(IllegalStateException.class);
-        RestconfMappingNodeUtil.toStreamEntryNode("stream-1", mock(LeafSchemaNode.class));
-    }
-
-    /**
-     * Test case with target {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} which does not contain any child nodes.
-     * Test is catching <code>RestconfDocumentedException</code> and error type, error tag and error status code are
-     * compared to expected values.
-     */
-    @Test
-    public void toStreamEntryNodeSchemaNodeWithoutChildsNegativeTest() {
-        final ListSchemaNode mockListNode = mock(ListSchemaNode.class);
-        when(mockListNode.getChildNodes()).thenReturn(Collections.EMPTY_SET);
-
-       try {
-           RestconfMappingNodeUtil.toStreamEntryNode("stream-1", mockListNode);
-           fail("Test should fail due to no child nodes in"
-                   + MonitoringModule.STREAM_LIST_SCHEMA_NODE
-                   + " node");
-       } catch (final RestconfDocumentedException e) {
-           assertEquals("Error type is not correct",
-                   ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
-           assertEquals("Error tag is not correct",
-                   ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
-           assertEquals("Error status code is not correct",
-                   404, e.getErrors().get(0).getErrorTag().getStatusCode());
-       }
-    }
+    public void toStreamEntryNodeTest() throws Exception {
+        final YangInstanceIdentifier path =
+                ParserIdentifier.toInstanceIdentifier("nested-module:depth1-cont/depth2-leaf1", schemaContextMonitoring, null).getInstanceIdentifier();
+        final Date start = new Date();
+        final String outputType = "XML";
+        final URI uri = new URI("uri");
+        final Module monitoringModule = schemaContextMonitoring
+                .findModuleByNamespaceAndRevision(MonitoringModule.URI_MODULE, MonitoringModule.DATE);
+        final boolean exist = true;
 
-    /**
-     * Test case when target list stream does not contain child with name {@link RestconfMappingNodeConstants#NAME}.
-     * Test is catching <code>RestconfDocumentedException</code> and error type, error tag and error status code are
-     * compared to expected values.
-     */
-    @Test
-    public void toStreamEntryNodeMissingStreamNameNegativeTest() {
-        prepareMockListWithMissingLeaf(this.leafName);
+        final Map<QName, Object> map =
+                prepareMap(path.getLastPathArgument().getNodeType().getLocalName(), uri, start, outputType);
 
-        try {
-            RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
-            fail("Test should fail due to missing "
-                    + RestconfMappingNodeConstants.NAME
-                    + " node in " + MonitoringModule.STREAM_LIST_SCHEMA_NODE);
-        } catch (final RestconfDocumentedException e) {
-            assertEquals("Error type is not correct",
-                    ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
-            assertEquals("Error tag is not correct",
-                    ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
-            assertEquals("Error status code is not correct",
-                    404, e.getErrors().get(0).getErrorTag().getStatusCode());
-        }
+        final NormalizedNode mappedData = RestconfMappingNodeUtil.mapDataChangeNotificationStreamByIetfRestconfMonitoring(path, start, outputType, uri,
+                monitoringModule, exist, schemaContextMonitoring);
+        assertNotNull(mappedData);
+        testData(map, mappedData);
     }
 
-    /**
-     * Test case when target list stream does not contain child with name
-     * {@link RestconfMappingNodeConstants#DESCRIPTION}. Test is catching <code>RestconfDocumentedException</code> and
-     * checking error type and error tag.
-     */
     @Test
-    public void toStreamEntryNodeMissingStreamDescriptionNegativeTest() {
-        prepareMockListWithMissingLeaf(this.leafDescription);
-
-        try {
-            RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
-            fail("Test should fail due to missing "
-                    + RestconfMappingNodeConstants.DESCRIPTION
-                    + " node in " + MonitoringModule.STREAM_LIST_SCHEMA_NODE);
-        } catch (final RestconfDocumentedException e) {
-            assertEquals("Error type is not correct",
-                    ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
-            assertEquals("Error tag is not correct",
-                    ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
-            assertEquals("Error status code is not correct",
-                    404, e.getErrors().get(0).getErrorTag().getStatusCode());
-        }
-    }
+    public void toStreamEntryNodeNotifiTest() throws Exception {
+        final Date start = new Date();
+        final String outputType = "JSON";
+        final URI uri = new URI("uri");
+        final Module monitoringModule = schemaContextMonitoring
+                .findModuleByNamespaceAndRevision(MonitoringModule.URI_MODULE, MonitoringModule.DATE);
+        final boolean exist = true;
 
-    /**
-     * Test case when target list stream does not contain child with name
-     * {@link RestconfMappingNodeConstants#REPLAY_SUPPORT}. Test is catching <code>RestconfDocumentedException</code>
-     * and checking error type and error tag.
-     */
-    @Test
-    public void toStreamEntryNodeMissingStreamReplaySupportNegativeTest() {
-        prepareMockListWithMissingLeaf(this.leafReplaySupport);
+        final Map<QName, Object> map = prepareMap("notifi", uri, start, outputType);
+        map.put(MonitoringModule.LEAF_DESCR_STREAM_QNAME, "Notifi");
 
-        try {
-            RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
-            fail("Test should fail due to missing "
-                    + RestconfMappingNodeConstants.REPLAY_SUPPORT
-                    + " node in " + MonitoringModule.STREAM_LIST_SCHEMA_NODE);
-        } catch (final RestconfDocumentedException e) {
-            assertEquals("Error type is not correct",
-                    ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
-            assertEquals("Error tag is not correct",
-                    ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
-            assertEquals("Error status code is not correct",
-                    404, e.getErrors().get(0).getErrorTag().getStatusCode());
-        }
+        final QName notifiQName = QName.create("urn:nested:module", "2014-06-3", "notifi");
+        final NormalizedNode mappedData =
+                RestconfMappingNodeUtil.mapYangNotificationStreamByIetfRestconfMonitoring(notifiQName, schemaContextMonitoring.getNotifications(), start,
+                        outputType, uri, monitoringModule, exist);
+        assertNotNull(mappedData);
+        testData(map, mappedData);
     }
 
-    /**
-     * Test case when target list stream does not contain child with name
-     * {@link RestconfMappingNodeConstants#REPLAY_LOG}. Test is catching <code>RestconfDocumentedException</code> and
-     * checking error type and error tag.
-     */
-    @Test
-    public void toStreamEntryNodeMissingStreamReplayLogNegativeTest() {
-        prepareMockListWithMissingLeaf(this.leafReplayLog);
-
-        try {
-            RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
-            fail("Test should fail due to missing "
-                    + RestconfMappingNodeConstants.REPLAY_LOG
-                    + " node in " + MonitoringModule.STREAM_LIST_SCHEMA_NODE);
-        } catch (final RestconfDocumentedException e) {
-            assertEquals("Error type is not correct",
-                    ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
-            assertEquals("Error tag is not correct",
-                    ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
-            assertEquals("Error status code is not correct",
-                    404, e.getErrors().get(0).getErrorTag().getStatusCode());
-        }
+    private Map<QName, Object> prepareMap(final String name, final URI uri, final Date start, final String outputType) {
+        final Map<QName, Object> map = new HashMap<>();
+        map.put(MonitoringModule.LEAF_NAME_STREAM_QNAME, name);
+        map.put(MonitoringModule.LEAF_LOCATION_ACCESS_QNAME, uri.toString());
+        map.put(MonitoringModule.LEAF_REPLAY_SUPP_STREAM_QNAME, true);
+        map.put(MonitoringModule.LEAF_START_TIME_STREAM_QNAME,
+                new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'XXX").format(start));
+        map.put(MonitoringModule.LEAF_ENCODING_ACCESS_QNAME, outputType);
+        return map;
     }
 
-    /**
-     * Test case when target list stream does not contain child with name {@link RestconfMappingNodeConstants#EVENTS}.
-     * Test is catching <code>RestconfDocumentedException</code> and checking error type, error tag and error status
-     * code.
-     */
-    @Test
-    public void toStreamEntryNodeMissingStreamEventsNegativeTest() {
-        prepareMockListWithMissingLeaf(this.leafEvents);
-
-        try {
-            RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
-            fail("Test should fail due to missing "
-                    + RestconfMappingNodeConstants.EVENTS
-                    + " node in " + MonitoringModule.STREAM_LIST_SCHEMA_NODE);
-        } catch (final RestconfDocumentedException e) {
-            assertEquals("Error type is not correct",
-                    ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
-            assertEquals("Error tag is not correct",
-                    ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
-            assertEquals("Error status code is not correct",
-                    404, e.getErrors().get(0).getErrorTag().getStatusCode());
+    private void testData(final Map<QName, Object> map, final NormalizedNode mappedData) {
+        for (final DataContainerChild<? extends PathArgument, ?> child : ((MapEntryNode) mappedData).getValue()) {
+            if (child instanceof LeafNode) {
+                final LeafNode leaf = ((LeafNode) child);
+                Assert.assertTrue(map.containsKey(leaf.getNodeType()));
+                Assert.assertEquals(map.get(leaf.getNodeType()), leaf.getValue());
+            }
         }
     }
 
-    /**
-     * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#NAME}. Test is
-     * expecting <code>IllegalStateException</code>.
-     */
-    @Test
-    public void toStreamEntryNodeStreamNameNegativeTest() {
-        prepareMockListWithIllegalLeaf(this.leafName);
-
-        this.thrown.expect(IllegalStateException.class);
-        RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
-    }
-
-    /**
-     * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#DESCRIPTION}.
-     * Test is expecting <code>IllegalStateException</code>.
-     */
-    @Test
-    public void toStreamEntryNodeStreamDescriptionNegativeTest() {
-        prepareMockListWithIllegalLeaf(this.leafDescription);
-
-        this.thrown.expect(IllegalStateException.class);
-        RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
-    }
-
-    /**
-     * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#REPLAY_SUPPORT}.
-     * Test is expecting <code>IllegalStateException</code>.
-     */
-    @Test
-    public void toStreamEntryNodeStreamReplaySupportNegativeTest() {
-        prepareMockListWithIllegalLeaf(this.leafReplaySupport);
-
-        this.thrown.expect(IllegalStateException.class);
-        RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
-    }
-
-    /**
-     * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#REPLAY_LOG}.
-     * Test is expecting <code>IllegalStateException</code>.
-     */
-    @Test
-    public void toStreamEntryNodeStreamReplayLogNegativeTest() {
-        prepareMockListWithIllegalLeaf(this.leafReplayLog);
-
-        this.thrown.expect(IllegalStateException.class);
-        RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
-    }
-
-    /**
-     * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#EVENTS}. Test is
-     * expecting <code>IllegalStateException</code>.
-     */
-    @Test
-    public void toStreamEntryNodeStreamEventsNegativeTest() {
-        prepareMockListWithIllegalLeaf(this.leafEvents);
-
-        this.thrown.expect(IllegalStateException.class);
-        RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList);
-    }
-
     /**
      * Verify loaded modules
      *
@@ -457,93 +254,4 @@ public class RestconfMappingNodeUtilTest {
             loadedModules.remove(name);
         }
     }
-
-    /**
-     * Verify if a stream was correctly written into {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} node in Restconf
-     * module.
-     * @param streamName Expected stream name
-     * @param streamNode Writetn strem node from Restconf module
-     */
-    private final void verifyStream(final String streamName, final MapEntryNode streamNode) {
-        assertNotNull("Stream node can not be null", streamNode);
-        final Iterator entries = ((AbstractImmutableDataContainerAttrNode) streamNode)
-                .getChildren().entrySet().iterator();
-        boolean notAllowedKey = false;
-
-        while (entries.hasNext()) {
-            final Entry e = ((AbstractMap.SimpleImmutableEntry) entries.next());
-            final String key = ((YangInstanceIdentifier.NodeIdentifier) e.getKey()).getNodeType().getLocalName();
-
-            switch (key) {
-                case RestconfMappingNodeConstants.NAME :
-                    assertEquals("Stream name value is not as expected",
-                            streamName, ((LeafNode) e.getValue()).getValue());
-                    break;
-                case RestconfMappingNodeConstants.DESCRIPTION :
-                    assertEquals("Stream description value is not as expected",
-                            RestconfMappingStreamConstants.DESCRIPTION, ((LeafNode) e.getValue()).getValue());
-                    break;
-                case RestconfMappingNodeConstants.REPLAY_SUPPORT :
-                    assertEquals("Stream replay support value is not as expected",
-                            RestconfMappingStreamConstants.REPLAY_SUPPORT, ((LeafNode) e.getValue()).getValue());
-                    break;
-                case RestconfMappingNodeConstants.REPLAY_LOG :
-                    assertEquals("Stream replay log value is not as expected",
-                            RestconfMappingStreamConstants.REPLAY_LOG, ((LeafNode) e.getValue()).getValue());
-                    break;
-                case RestconfMappingNodeConstants.EVENTS :
-                    assertEquals("Stream events value is not as expected",
-                            RestconfMappingStreamConstants.EVENTS, ((LeafNode) e.getValue()).getValue());
-                    break;
-                default:
-                    notAllowedKey = true;
-                    break;
-            }
-        }
-
-        assertFalse("Not allowed key in list stream found", notAllowedKey);
-    }
-
-    /**
-     * There are multiple testing Restconf modules for different test cases. It is possible to distinguish them by
-     * name or by namespace. This method is looking for Restconf test module by its name.
-     * @param s Testing Restconf module name
-     * @return Restconf module
-     */
-    private Module getTestingRestconfModule(final String s) {
-        return RestconfMappingNodeUtilTest.schemaContext.findModuleByName(
-                s, Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
-    }
-
-    /**
-     * Updates {@link this#mockStreamList} to NOT contains specified leaf.
-     * @param leaf Leaf to be missing
-     */
-    private void prepareMockListWithMissingLeaf(final LeafSchemaNode leaf) {
-        // prepare set of leaf without selected leaf
-        final Set childLeafs = new HashSet<>(this.allStreamChildNodes);
-        childLeafs.remove(leaf);
-
-        // mock list leaf nodes
-        when(this.mockStreamList.getChildNodes()).thenReturn(childLeafs);
-    }
-
-    /**
-     * Updates {@link this#mockStreamList} to contains specified leaf which is not of type {@link LeafSchemaNode}.
-     * @param leaf Leaf to be changes
-     */
-    private void prepareMockListWithIllegalLeaf(final LeafSchemaNode leaf) {
-        // prepare set of leaf without selected leaf
-        final Set childLeafs = new HashSet<>(this.allStreamChildNodes);
-        childLeafs.remove(leaf);
-
-        // add leaf-list with the same local name as removed leaf
-        final String localName = leaf.getQName().getLocalName();
-        final LeafListSchemaNode mockLeafList = mock(LeafListSchemaNode.class);
-        when(mockLeafList.getQName()).thenReturn(QName.create("", localName));
-        childLeafs.add(mockLeafList);
-
-        // mock list leaf nodes
-        when(this.mockStreamList.getChildNodes()).thenReturn(childLeafs);
-    }
 }
index 43bec1c518deb450d94abb1613254cabfd86ebc9..c572bb67ce9bfb3fdb1f88053d2e87dbf32285ee 100644 (file)
 
 package org.opendaylight.restconf.utils.schema.context;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-import com.google.common.collect.Sets;
-import org.junit.Before;
-import org.junit.Rule;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import org.junit.Assert;
 import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
+import org.mockito.Mockito;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
-import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
-import org.opendaylight.restconf.Draft18;
-import org.opendaylight.restconf.Draft18.MonitoringModule;
-import org.opendaylight.restconf.Draft18.RestconfModule;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 
 /**
  * Unit tests for {@link RestconfSchemaUtil}
  */
 public class RestconfSchemaUtilTest {
-    // schema with testing modules
-    private SchemaContext schemaContext;
 
-    @Rule
-    public ExpectedException thrown = ExpectedException.none();
-
-    @Before
-    public void setup() throws Exception {
-        this.schemaContext = TestRestconfUtils.loadSchemaContext("/modules/restconf-module-testing");
-    }
-
-    /**
-     * Positive test for getting <code>DataSchemaNode</code> from Restconf module for schema node name equals to
-     * {@link RestconfModule#MODULE_LIST_SCHEMA_NODE} when this node can be found.
-     */
-    @Test
-    public void getRestconfSchemaNodeListModuleTest() {
-        final DataSchemaNode dataSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode(
-                getTestingRestconfModule("ietf-restconf"),
-                RestconfModule.MODULE_LIST_SCHEMA_NODE);
-
-        assertNotNull("Existing schema node "+ RestconfModule.MODULE_LIST_SCHEMA_NODE + " should be found",
-                dataSchemaNode);
-        assertEquals("Incorrect schema node was returned",
-                dataSchemaNode.getQName().getLocalName(), RestconfModule.MODULE_LIST_SCHEMA_NODE);
-        assertEquals("Incorrect schema node was returned",
-                dataSchemaNode.getQName().getNamespace().toString(), RestconfModule.NAMESPACE);
-        assertEquals("Incorrect schema node was returned",
-                dataSchemaNode.getQName().getFormattedRevision(), RestconfModule.REVISION);
-    }
-
-    /**
-     * Positive test for getting <code>DataSchemaNode</code> from Restconf module for schema node name equals to
-     * {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} when this node can be found.
-     */
-    @Test
-    public void getRestconfSchemaNodeListStreamTest() {
-        final DataSchemaNode dataSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode(
-                getTestingRestconfModule("ietf-restconf"),
-                MonitoringModule.STREAM_LIST_SCHEMA_NODE);
-
-        assertNotNull("Existing schema node " + MonitoringModule.STREAM_LIST_SCHEMA_NODE + " should be found",
-                dataSchemaNode);
-        assertEquals("Incorrect schema node was returned",
-                dataSchemaNode.getQName().getLocalName(), MonitoringModule.STREAM_LIST_SCHEMA_NODE);
-        assertEquals("Incorrect schema node was returned",
-                dataSchemaNode.getQName().getNamespace().toString(), RestconfModule.NAMESPACE);
-        assertEquals("Incorrect schema node was returned",
-                dataSchemaNode.getQName().getFormattedRevision(), RestconfModule.REVISION);
-    }
-
-    /**
-     * Positive test for getting <code>DataSchemaNode</code> from Restconf module for schema node name equals to
-     * {@link RestconfModule#MODULES_CONTAINER_SCHEMA_NODE} when this node can be found.
-     */
-    @Test
-    public void getRestconfSchemaNodeContainerModulesTest() {
-        final DataSchemaNode dataSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode(
-                getTestingRestconfModule("ietf-restconf"),
-                RestconfModule.MODULES_CONTAINER_SCHEMA_NODE);
-
-        assertNotNull("Existing schema node " + RestconfModule.MODULES_CONTAINER_SCHEMA_NODE + "should be found",
-                dataSchemaNode);
-        assertEquals("Incorrect schema node was returned",
-                dataSchemaNode.getQName().getLocalName(), RestconfModule.MODULES_CONTAINER_SCHEMA_NODE);
-        assertEquals("Incorrect schema node was returned",
-                dataSchemaNode.getQName().getNamespace().toString(), RestconfModule.NAMESPACE);
-        assertEquals("Incorrect schema node was returned",
-                dataSchemaNode.getQName().getFormattedRevision(), RestconfModule.REVISION);
-    }
-
-    /**
-     * Positive test for getting <code>DataSchemaNode</code> from Restconf module for schema node name equals to
-     * {@link MonitoringModule#STREAMS_CONTAINER_SCHEMA_NODE} when this node can be found.
-     */
-    @Test
-    public void getRestconfSchemaNodeContainerStreamsTest() {
-        final DataSchemaNode dataSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode(
-                getTestingRestconfModule("ietf-restconf"),
-                MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE);
-
-        assertNotNull("Existing schema node " + MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE + " should be found",
-                dataSchemaNode);
-        assertEquals("Incorrect schema node was returned",
-                dataSchemaNode.getQName().getLocalName(), MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE);
-        assertEquals("Incorrect schema node was returned",
-                dataSchemaNode.getQName().getNamespace().toString(), RestconfModule.NAMESPACE);
-        assertEquals("Incorrect schema node was returned",
-                dataSchemaNode.getQName().getFormattedRevision(), RestconfModule.REVISION);
-    }
-
-    /**
-     * Negative test for getting <code>DataSchemaNode</code> from Restconf module when Restconf module is
-     * <code>null</code>. Test is expected to fail catching <code>NullPointerException</code>.
-     */
-    @Test
-    public void getRestconfSchemaNodeNullRestconfModuleNegativeTest() {
-        this.thrown.expect(NullPointerException.class);
-        RestconfSchemaUtil.getRestconfSchemaNode(null, RestconfModule.RESTCONF_CONTAINER_SCHEMA_NODE);
-    }
-
-    /**
-     * Negative test for getting <code>DataSchemaNode</code> from Restconf module when name of the schema node name is
-     * <code>null</code>. Test is expected to fail with <code>NullPointerException</code>.
-     */
-    @Test
-    public void getRestconfSchemaNodeNullSchemaNodeNameNegativeTest() {
-        this.thrown.expect(NullPointerException.class);
-        RestconfSchemaUtil.getRestconfSchemaNode(getTestingRestconfModule("ietf-restconf"), null);
-    }
-
-    /**
-     * Negative test for getting <code>DataSchemaNode</code> from Restconf module when name of the schema node name
-     * references to not existing node. Test is expected to fail catching code>RestconfDocumentedException</code> and
-     * checking expected error type, error tag and error status code.
-     */
-    @Test
-    public void getRestconfSchemaNodeNotExistingSchemaNodeNameNegativeTest() {
-        try {
-            RestconfSchemaUtil.getRestconfSchemaNode(getTestingRestconfModule("ietf-restconf"), "not-existing-node");
-            fail("Test should fail due to not-existing node name");
-        } catch (final RestconfDocumentedException e) {
-            assertEquals("Error type is not correct",
-                    RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
-            assertEquals("Error tag is not correct",
-                    RestconfError.ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
-            assertEquals("Error status code is not correct",
-                    404, e.getErrors().get(0).getErrorTag().getStatusCode());
-        }
-    }
-
-    /**
-     * Positive test trying to find <code>DataSchemaNode</code> of {@link RestconfModule#RESTCONF_GROUPING_SCHEMA_NODE}
-     * in Restconf module groupings collection.
-     */
-    @Test
-    public void findSchemaNodeInCollectionTest() {
-        final SchemaNode schemaNode = RestconfSchemaUtil.findSchemaNodeInCollection(
-                getTestingRestconfModule("ietf-restconf").getGroupings(),
-                RestconfModule.RESTCONF_GROUPING_SCHEMA_NODE);
-
-        assertNotNull("Restconf grouping schema node should be found", schemaNode);
-        assertEquals("Incorrect grouping was returned",
-                schemaNode.getQName().getLocalName(), RestconfModule.RESTCONF_GROUPING_SCHEMA_NODE);
-        assertEquals("Incorrect grouping was returned",
-                schemaNode.getQName().getNamespace().toString(), RestconfModule.NAMESPACE);
-        assertEquals("Incorrect grouping was returned",
-                schemaNode.getQName().getFormattedRevision(), RestconfModule.REVISION);
-    }
-
-    /**
-     * Negative test trying to find <code>DataSchemaNode</code> of not existing groupings schema node name in Restconf
-     * module grouping collection. Test is expected to fail catching <code>RestconfDocumentedException</code> and
-     * checking for correct error type, error tag and error status code.
-     */
-    @Test
-    public void findSchemaNodeInCollectionNegativeTest() {
-        try {
-            RestconfSchemaUtil.findSchemaNodeInCollection(
-                    getTestingRestconfModule("ietf-restconf").getGroupings(), "not-existing-grouping");
-            fail("Test should fail due to missing not-existing grouping in Restconf grouping collection");
-        } catch (final RestconfDocumentedException e) {
-            assertEquals("Error type is not correct",
-                    RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
-            assertEquals("Error tag is not correct",
-                    RestconfError.ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
-            assertEquals("Error status code is not correct",
-                    404, e.getErrors().get(0).getErrorTag().getStatusCode());
-        }
-    }
-
-    /**
-     * Negative test trying to find <code>DataSchemaNode</code> of existing schema node name in <code>null</code>
-     * collection. Test is expected to fail with <code>NullPointerException</code>.
-     */
     @Test
-    public void findSchemaNodeInCollectionNullCollectionNegativeTest() {
-        this.thrown.expect(NullPointerException.class);
-        RestconfSchemaUtil.findSchemaNodeInCollection(null, RestconfModule.MODULES_CONTAINER_SCHEMA_NODE);
+    public void findInCollectionTest() {
+        final SchemaNode origSchNode = mockSchemaNode("key");
+        final SchemaNode actualSch = findSchemaNodeInCollection("key", origSchNode);
+        Assert.assertEquals(origSchNode, actualSch);
     }
 
-    /**
-     * Negative test trying to find <code>DataSchemaNode</code> for schema node name in empty collection. Test is
-     * expected to fail with <code>RestconfDocumentedException</code>. Error type, error tag and error status code
-     * are compared to expected values.
-     */
-    @Test
-    public void findSchemaNodeInCollectionEmptyCollectionNegativeTest() {
-        try {
-            RestconfSchemaUtil.findSchemaNodeInCollection(
-                    Sets.newHashSet(), RestconfModule.MODULES_CONTAINER_SCHEMA_NODE);
-            fail("Test should fail due to empty schema nodes collection");
-        } catch (final RestconfDocumentedException e) {
-            assertEquals("Error type is not correct",
-                    RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
-            assertEquals("Error tag is not correct",
-                    RestconfError.ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
-            assertEquals("Error status code is not correct",
-                    404, e.getErrors().get(0).getErrorTag().getStatusCode());
-        }
+    @Test(expected = RestconfDocumentedException.class)
+    public void findInCollectionFailedTest() {
+        final SchemaNode origSchNode = mockSchemaNode("key");
+        findSchemaNodeInCollection("bad_key", origSchNode);
     }
 
-    /**
-     * Negative test trying to find <code>DataSchemaNode</code> of <code>null</code> schema node name in Restconf module
-     * groupings collection. Test is expected to fail with <code>RestconfDocumentedException</code>. Error type, error
-     * tag and error status code are compared to expected values.
-     */
-    @Test
-    public void findSchemaNodeInCollectionNullSchemaNodeName() {
-        try {
-            RestconfSchemaUtil.findSchemaNodeInCollection(
-                    getTestingRestconfModule("ietf-restconf").getGroupings(), null);
-            fail("Test should fail due to null schema node name");
-        } catch (final RestconfDocumentedException e) {
-            assertEquals("Error type is not correct",
-                    RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
-            assertEquals("Error tag is not correct",
-                    RestconfError.ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
-            assertEquals("Error status code is not correct",
-                    404, e.getErrors().get(0).getErrorTag().getStatusCode());
+    private SchemaNode findSchemaNodeInCollection(final String key, final SchemaNode... origSchNode) {
+        final List<SchemaNode> collection = new ArrayList<>();
+        for (int i = 0; i < origSchNode.length; i++) {
+            collection.add(origSchNode[i]);
         }
+        return RestconfSchemaUtil.findSchemaNodeInCollection(collection, key);
     }
 
-    /**
-     * There are multiple testing Restconf modules for different test cases. It is possible to distinguish them by
-     * name or by namespace. This method is looking for Restconf test module by its name.
-     * @param s Testing Restconf module name
-     * @return Restconf module
-     */
-    private Module getTestingRestconfModule(final String s) {
-        return this.schemaContext.findModuleByName(s, Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
+    private SchemaNode mockSchemaNode(final String origKey) {
+        final SchemaNode mockSchNode = Mockito.mock(SchemaNode.class);
+        Mockito.when(mockSchNode.getQName())
+                .thenReturn(QName.create("ns", new SimpleDateFormat("yyyy-MM-dd").format(new Date()), origKey));
+        return mockSchNode;
     }
 }
index 726b8d9a5b9d11052385fefc217f08f4bca6b879..aa2795afebbe0cf0c8a739a382dfd2d4c5c26eef 100644 (file)
@@ -62,4 +62,8 @@ module nested-module {
             type string;
         }
     }
+
+    notification notifi{
+        description "Notifi";
+    }
 }
diff --git a/restconf/sal-rest-connector/src/test/resources/notifications/toaster.yang b/restconf/sal-rest-connector/src/test/resources/notifications/toaster.yang
new file mode 100644 (file)
index 0000000..da68016
--- /dev/null
@@ -0,0 +1,200 @@
+module toaster {
+
+    yang-version 1;
+
+    namespace
+      "http://netconfcentral.org/ns/toaster";
+
+    prefix toast;
+
+    organization "Netconf Central";
+
+    contact
+      "Andy Bierman <andy@netconfcentral.org>";
+
+    description
+      "YANG version of the TOASTER-MIB.";
+
+    revision "2009-11-20" {
+      description
+        "Toaster module in progress.";
+    }
+
+
+    identity toast-type {
+      description
+        "Base for all bread types supported by the toaster.
+           New bread types not listed here nay be added in the
+           future.";
+    }
+
+    identity white-bread {
+      base toast:toast-type;
+      description "White bread.";
+    }
+
+    identity wheat-bread {
+      base toast-type;
+      description "Wheat bread.";
+    }
+
+    identity wonder-bread {
+      base toast-type;
+      description "Wonder bread.";
+    }
+
+    identity frozen-waffle {
+      base toast-type;
+      description "Frozen waffle.";
+    }
+
+    identity frozen-bagel {
+      base toast-type;
+      description "Frozen bagel.";
+    }
+
+    identity hash-brown {
+      base toast-type;
+      description "Hash browned potatos.";
+    }
+
+    typedef DisplayString {
+      type string {
+        length "0 .. 255";
+      }
+      description
+        "YANG version of the SMIv2 DisplayString TEXTUAL-CONVENTION.";
+      reference
+        "RFC 2579, section 2.";
+
+    }
+
+    container toaster {
+      presence
+        "Indicates the toaster service is available";
+      description
+        "Top-level container for all toaster database objects.";
+      leaf toasterManufacturer {
+        type DisplayString;
+        config false;
+        mandatory true;
+        description
+          "The name of the toaster's manufacturer. For instance,
+                Microsoft Toaster.";
+      }
+
+      leaf toasterModelNumber {
+        type DisplayString;
+        config false;
+        mandatory true;
+        description
+          "The name of the toaster's model. For instance,
+               Radiant Automatic.";
+      }
+
+      leaf toasterStatus {
+        type enumeration {
+          enum "up" {
+            value 1;
+            description
+              "The toaster knob position is up.
+                      No toast is being made now.";
+          }
+          enum "down" {
+            value 2;
+            description
+              "The toaster knob position is down.
+                      Toast is being made now.";
+          }
+        }
+        config false;
+        mandatory true;
+        description
+          "This variable indicates the current state of
+               the toaster.";
+      }
+
+      leaf darknessFactor {
+        type uint32;
+        config true;
+        default 1000;
+        description
+          "The darkness factor. Basically, the number of ms to multiple the doneness value by.";
+      }
+    }  // container toaster
+
+    rpc make-toast {
+      description
+        "Make some toast.
+           The toastDone notification will be sent when
+           the toast is finished.
+           An 'in-use' error will be returned if toast
+           is already being made.
+           A 'resource-denied' error will be returned
+           if the toaster service is disabled.";
+      input {
+        leaf toasterDoneness {
+          type uint32 {
+            range "1 .. 10";
+          }
+          default '5';
+          description
+            "This variable controls how well-done is the
+                   ensuing toast. It should be on a scale of 1 to 10.
+                   Toast made at 10 generally is considered unfit
+                   for human consumption; toast made at 1 is warmed
+                   lightly.";
+        }
+
+        leaf toasterToastType {
+          type identityref {
+            base toast:toast-type;
+          }
+          default 'wheat-bread';
+          description
+            "This variable informs the toaster of the type of
+                   material that is being toasted. The toaster
+                   uses this information, combined with
+                   toasterDoneness, to compute for how
+                   long the material must be toasted to achieve
+                   the required doneness.";
+        }
+      }
+    }  // rpc make-toast
+
+    rpc cancel-toast {
+      description
+        "Stop making toast, if any is being made.
+           A 'resource-denied' error will be returned
+           if the toaster service is disabled.";
+    }  // rpc cancel-toast
+
+    rpc restock-toaster {
+        description
+          "Restocks the toaster with the amount of bread specified.";
+
+        input {
+            leaf amountOfBreadToStock {
+                type uint32;
+                description
+                  "Indicates the amount of bread to re-stock";
+            }
+        }
+    }
+
+    notification toasterOutOfBread {
+      description
+        "Indicates that the toaster has run of out bread.";
+    }  // notification toasterOutOfStock
+
+    notification toasterRestocked {
+      description
+        "Indicates that the toaster has run of out bread.";
+      leaf amountOfBread {
+        type uint32;
+        description
+          "Indicates the amount of bread that was re-stocked";
+      }
+    }  // notification toasterOutOfStock
+
+  }  // module toaster
\ No newline at end of file