+/*
+ * Copyright (c) 2017 Red Hat, 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.nb.rfc8040.streams.listeners;
+
+import static java.time.Instant.EPOCH;
+import static org.junit.Assert.fail;
+
+import com.google.common.util.concurrent.Uninterruptibles;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Optional;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.binding.test.AbstractConcurrentDataBrokerTest;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
+import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.PatchCont;
+import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.patch.cont.MyList1;
+import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.patch.cont.MyList1Builder;
+import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.patch.cont.MyList1Key;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.skyscreamer.jsonassert.JSONAssert;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
+ private static final Logger LOG = LoggerFactory.getLogger(ListenerAdapterTest.class);
+
+ private static final String JSON_NOTIF_LEAVES_CREATE = "/listener-adapter-test/notif-leaves-create.json";
+ private static final String JSON_NOTIF_LEAVES_UPDATE = "/listener-adapter-test/notif-leaves-update.json";
+ private static final String JSON_NOTIF_LEAVES_DEL = "/listener-adapter-test/notif-leaves-del.json";
+ private static final String JSON_NOTIF_CREATE = "/listener-adapter-test/notif-create.json";
+ private static final String JSON_NOTIF_UPDATE = "/listener-adapter-test/notif-update.json";
+ private static final String JSON_NOTIF_DEL = "/listener-adapter-test/notif-del.json";
+
+ private static YangInstanceIdentifier PATCH_CONT_YIID =
+ YangInstanceIdentifier.create(new YangInstanceIdentifier.NodeIdentifier(PatchCont.QNAME));
+
+ private DataBroker dataBroker;
+ private DOMDataBroker domDataBroker;
+ private TransactionChainHandler transactionChainHandler;
+ private SchemaContextHandler schemaContextHandler;
+
+ @Before
+ public void setUp() throws Exception {
+ dataBroker = getDataBroker();
+ domDataBroker = getDomBroker();
+ SchemaContext sc = YangParserTestUtils.parseYangResource(
+ "/instanceidentifier/yang/instance-identifier-patch-module.yang");
+
+ transactionChainHandler = new TransactionChainHandler(domDataBroker.createTransactionChain(
+ Mockito.mock(TransactionChainListener.class)));
+ schemaContextHandler = new SchemaContextHandler(transactionChainHandler);
+ SchemaContextHandler.setSchemaContext(sc);
+ }
+
+ class ListenerAdapterTester extends ListenerAdapter {
+
+ private volatile String lastNotification;
+ private CountDownLatch notificationLatch = new CountDownLatch(1);
+
+ ListenerAdapterTester(final YangInstanceIdentifier path, final String streamName,
+ final NotificationOutputTypeGrouping.NotificationOutputType outputType,
+ final boolean leafNodesOnly) {
+ super(path, streamName, outputType);
+ setQueryParams(EPOCH, Optional.empty(), Optional.empty(), leafNodesOnly);
+ }
+
+ @Override
+ protected void post(final Event event) {
+ this.lastNotification = event.getData();
+ notificationLatch.countDown();
+ }
+
+ public void assertGot(final String json) {
+ if (!Uninterruptibles.awaitUninterruptibly(notificationLatch, 5, TimeUnit.SECONDS)) {
+ fail("Timed out waiting for notification for: " + json);
+ }
+
+ LOG.info("lastNotification: {}", lastNotification);
+ String withFakeDate = withFakeDate(lastNotification);
+ LOG.info("Comparing: \n{}\n{}", json, withFakeDate);
+
+ JSONAssert.assertEquals(json, withFakeDate, false);
+ this.lastNotification = null;
+ notificationLatch = new CountDownLatch(1);
+ }
+ }
+
+ static String withFakeDate(final String in) {
+ JSONObject doc = new JSONObject(in);
+ JSONObject notification = doc.getJSONObject("notification");
+ if (notification == null) {
+ return in;
+ }
+ notification.put("eventTime", "someDate");
+ return doc.toString();
+ }
+
+ private String getNotifJson(final String path) throws IOException, URISyntaxException {
+ URL url = getClass().getResource(path);
+ byte[] bytes = Files.readAllBytes(Paths.get(url.toURI()));
+ return withFakeDate(new String(bytes, StandardCharsets.UTF_8));
+ }
+
+ @Test
+ public void testJsonNotifsLeaves() throws Exception {
+ ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey",
+ NotificationOutputTypeGrouping.NotificationOutputType.JSON, true);
+ adapter.setCloseVars(transactionChainHandler, schemaContextHandler);
+
+ DOMDataTreeChangeService changeService = (DOMDataTreeChangeService)
+ domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
+ DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
+ changeService.registerDataTreeChangeListener(root, adapter);
+
+ WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
+ MyList1Builder builder = new MyList1Builder().setMyLeaf11("Jed").setName("Althea");
+ InstanceIdentifier<MyList1> iid = InstanceIdentifier.create(PatchCont.class)
+ .child(MyList1.class, new MyList1Key("Althea"));
+ writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, builder.build(), true);
+ writeTransaction.submit();
+ adapter.assertGot(getNotifJson(JSON_NOTIF_LEAVES_CREATE));
+
+ writeTransaction = dataBroker.newWriteOnlyTransaction();
+ builder = new MyList1Builder().setKey(new MyList1Key("Althea")).setMyLeaf12("Bertha");
+ writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, builder.build(), true);
+ writeTransaction.submit();
+ adapter.assertGot(getNotifJson(JSON_NOTIF_LEAVES_UPDATE));
+
+ writeTransaction = dataBroker.newWriteOnlyTransaction();
+ writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
+ writeTransaction.submit();
+ adapter.assertGot(getNotifJson(JSON_NOTIF_LEAVES_DEL));
+ }
+
+ @Test
+ public void testJsonNotifs() throws Exception {
+ ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey",
+ NotificationOutputTypeGrouping.NotificationOutputType.JSON, false);
+ adapter.setCloseVars(transactionChainHandler, schemaContextHandler);
+
+ DOMDataTreeChangeService changeService = (DOMDataTreeChangeService)
+ domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
+ DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
+ changeService.registerDataTreeChangeListener(root, adapter);
+
+ WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
+ MyList1Builder builder = new MyList1Builder().setMyLeaf11("Jed").setName("Althea");
+ InstanceIdentifier<MyList1> iid = InstanceIdentifier.create(PatchCont.class)
+ .child(MyList1.class, new MyList1Key("Althea"));
+ writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, builder.build(), true);
+ writeTransaction.submit();
+ adapter.assertGot(getNotifJson(JSON_NOTIF_CREATE));
+
+ writeTransaction = dataBroker.newWriteOnlyTransaction();
+ builder = new MyList1Builder().setKey(new MyList1Key("Althea")).setMyLeaf12("Bertha");
+ writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, builder.build(), true);
+ writeTransaction.submit();
+ adapter.assertGot(getNotifJson(JSON_NOTIF_UPDATE));
+
+ writeTransaction = dataBroker.newWriteOnlyTransaction();
+ writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
+ writeTransaction.submit();
+ adapter.assertGot(getNotifJson(JSON_NOTIF_DEL));
+ }
+}