/* * Copyright 2016 higherfrequencytrading.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package net.openhft.chronicle.engine.queue; import net.openhft.chronicle.core.Jvm; import net.openhft.chronicle.core.io.Closeable; import net.openhft.chronicle.core.pool.ClassAliasPool; import net.openhft.chronicle.engine.ThreadMonitoringTest; import net.openhft.chronicle.engine.api.pubsub.Publisher; import net.openhft.chronicle.engine.api.pubsub.Subscriber; import net.openhft.chronicle.engine.api.pubsub.TopicPublisher; import net.openhft.chronicle.engine.api.pubsub.TopicSubscriber; import net.openhft.chronicle.engine.api.tree.AssetTree; import net.openhft.chronicle.engine.api.tree.RequestContext; import net.openhft.chronicle.engine.server.ServerEndpoint; import net.openhft.chronicle.engine.tree.QueueView; import net.openhft.chronicle.engine.tree.QueueView.Excerpt; import net.openhft.chronicle.engine.tree.VanillaAssetTree; import net.openhft.chronicle.network.TCPRegistry; import net.openhft.chronicle.network.connection.TcpChannelHub; import net.openhft.chronicle.wire.WireType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.junit.*; import org.junit.rules.TestName; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; import static net.openhft.chronicle.engine.Utils.methodName; import static net.openhft.chronicle.engine.api.tree.RequestContext.requestContext; import static org.junit.Assert.assertEquals; /** * @author Rob Austin. */ @RunWith(value = Parameterized.class) public class SimpleQueueViewTest extends ThreadMonitoringTest { private static final String DELETE_CHRONICLE_FILE = "?dontPersist=true"; static { ClassAliasPool.CLASS_ALIASES.addAlias(MyMarshallable.class, "MyMarshallable"); } private final boolean isRemote; @NotNull private final WireType wireType; @NotNull @Rule public TestName name = new TestName(); @NotNull String methodName = ""; private AssetTree assetTree; @Nullable private ServerEndpoint serverEndpoint; @Nullable private AssetTree serverAssetTree; public SimpleQueueViewTest(Boolean isRemote) { this.isRemote = isRemote; this.wireType = WireType.BINARY; } @Parameterized.Parameters public static Collection<Object[]> data() { return Arrays.asList(new Boolean[][]{ {true}, {false} }); } public static void deleteFiles(@NotNull File element) { if (element.isDirectory()) { for (@NotNull File sub : element.listFiles()) { deleteFiles(sub); } } element.delete(); } @Before public void before() throws IOException { methodName(name.getMethodName()); methodName = name.getMethodName() .substring(0, name.getMethodName().indexOf('[')) + "-" + System.nanoTime(); if (isRemote) { serverAssetTree = new VanillaAssetTree().forTesting(); @NotNull String hostPortDescription = "SimpleQueueViewTest-methodName" + methodName + wireType; TCPRegistry.createServerSocketChannelFor(hostPortDescription); serverEndpoint = new ServerEndpoint(hostPortDescription, serverAssetTree); @NotNull final VanillaAssetTree client = new VanillaAssetTree(); assetTree = client.forRemoteAccess(hostPortDescription, WireType.BINARY); } else { assetTree = (new VanillaAssetTree(1)).forTesting(); serverEndpoint = null; serverAssetTree = null; } } @After public void preAfter() { threadDump.ignore("ChronicleMapKeyValueStore Closer"); Closeable.closeQuietly(serverAssetTree); Closeable.closeQuietly(serverEndpoint); Closeable.closeQuietly(assetTree); methodName = ""; TcpChannelHub.closeAllHubs(); TCPRegistry.reset(); } @Test public void testStringTopicPublisherWithSubscribe() throws InterruptedException { //YamlLogging.setAll(true); @NotNull String uri = "/queue/" + methodName; @NotNull String messageType = "topic"; @NotNull TopicPublisher<String, String> publisher = assetTree.acquireTopicPublisher(uri + DELETE_CHRONICLE_FILE, String.class, String.class); @NotNull BlockingQueue<String> values0 = new LinkedBlockingQueue<>(); @Nullable Subscriber<String> subscriber = e -> { if (e != null) { values0.add(e); } }; publisher.publish(messageType, "Message-1"); publisher.publish(messageType, "Message-2"); Jvm.pause(500); assetTree.registerSubscriber(uri + "/" + messageType + DELETE_CHRONICLE_FILE, String.class, subscriber); assertEquals("Message-1", values0.poll(3, SECONDS)); assertEquals("Message-2", values0.poll(3, SECONDS)); Jvm.pause(100); assertEquals("[]", values0.toString()); } @Test public void testPublishAtIndexCheckIndex() throws InterruptedException { @Nullable QueueView<String, String> queueView = null; @NotNull String uri = "/queue/" + methodName + DELETE_CHRONICLE_FILE; @NotNull String messageType = "topic"; @NotNull final RequestContext requestContext = requestContext(uri); if (requestContext.bootstrap() != null) throw new UnsupportedOperationException("Its not possible to set the bootstrap when " + "acquiring a queue"); queueView = assetTree.acquireView(requestContext.view("queue").type(String.class).type2(String.class) .cluster("")); Jvm.pause(500); final long index = queueView.publishAndIndex(messageType, "Message-1"); @Nullable final Excerpt<String, String> actual = queueView.getExcerpt(index); assertEquals(index, actual.index()); final long index2 = queueView.publishAndIndex(messageType, "Message-2"); @Nullable final Excerpt<String, String> actual2 = queueView.getExcerpt(index2); assertEquals(index2, actual2.index()); } @Test public void testStringPublish() throws InterruptedException { @Nullable Publisher<String> publisher = null; @NotNull String uri = "/queue/" + methodName + DELETE_CHRONICLE_FILE; publisher = assetTree.acquirePublisher(uri, String.class); @NotNull BlockingQueue<String> values = new LinkedBlockingQueue<>(); Subscriber<String> subscriber = values::add; assetTree.registerSubscriber(uri, String.class, subscriber); Jvm.pause(500); publisher.publish("Message-1"); assertEquals("Message-1", values.poll(2, SECONDS)); publisher.publish("Message-2"); assertEquals("Message-2", values.poll(2, SECONDS)); Jvm.pause(100); assertEquals("[]", values.toString()); } @Test public void testStringPublishToAKeyTopic() throws InterruptedException { Publisher<String> publisher; //YamlLogging.setAll(true); @NotNull String uri = "/queue/" + methodName + System.nanoTime() + "/key" + DELETE_CHRONICLE_FILE; publisher = assetTree.acquirePublisher(uri, String.class); @NotNull BlockingQueue<String> values = new ArrayBlockingQueue<>(2); Subscriber<String> subscriber = values::add; assetTree.registerSubscriber(uri, String.class, subscriber); publisher.publish("Message-1"); assertEquals("Message-1", values.poll(2, SECONDS)); publisher.publish("Message-2"); assertEquals("Message-2", values.poll(5, SECONDS)); assertEquals("[]", values.toString()); } @Test public void testStringPublishToAKeyTopicNotForMe() throws InterruptedException { @Nullable Publisher<String> publisher = null; @NotNull String uri = "/queue/" + methodName + "/key" + DELETE_CHRONICLE_FILE; publisher = assetTree.acquirePublisher(uri, String.class); @NotNull BlockingQueue<String> values = new ArrayBlockingQueue<>(1); Subscriber<String> subscriber = values::add; assetTree.registerSubscriber("/queue/" + methodName + "/keyNotForMe", String.class, subscriber); Jvm.pause(200); publisher.publish("Message-1"); assertEquals(null, values.poll(200, MILLISECONDS)); publisher.publish("Message-2"); assertEquals(null, values.poll(200, MILLISECONDS)); } @Test @Ignore("TODO FIX Too many results") public void testStringTopicPublisherString() throws InterruptedException { TopicPublisher<String, String> publisher; @NotNull String uri = "/queue/" + methodName + DELETE_CHRONICLE_FILE; @NotNull String messageType = "topic"; publisher = assetTree.acquireTopicPublisher(uri, String.class, String.class); @NotNull BlockingQueue<String> values = new LinkedBlockingQueue<>(); @NotNull TopicSubscriber<String, String> subscriber = (topic, message) -> values.add(topic + " " + message); assetTree.registerTopicSubscriber(uri, String.class, String.class, subscriber); Jvm.pause(200); publisher.publish(messageType, "Message-1"); assertEquals("topic Message-1", values.poll(2, SECONDS)); publisher.publish(messageType, "Message-2"); assertEquals("topic Message-2", values.poll(2, SECONDS)); Jvm.pause(200); assertEquals("[]", values.toString()); } @Test @Ignore("TODO FIX Too many results") public void testStringPublishWithTopicSubscribe() throws InterruptedException { // YamlLogging.showClientReads(true); @Nullable Publisher<String> publisher = null; @NotNull String uri = "/queue/" + methodName + DELETE_CHRONICLE_FILE; @NotNull String messageType = "topic"; // todo - fix if (!isRemote) { @NotNull final RequestContext requestContext = requestContext(uri); if (requestContext.bootstrap() != null) throw new UnsupportedOperationException("Its not possible to set the bootstrap when " + "acquiring a queue"); assetTree.acquireView(requestContext.view("queue").type(String.class).type2(String.class) .cluster("")); } publisher = assetTree.acquirePublisher(uri + "/" + messageType, String.class); @NotNull BlockingQueue<String> values = new LinkedBlockingQueue<>(); @NotNull TopicSubscriber<String, String> subscriber = (topic, message) -> values.add(topic + " " + message); assetTree.registerTopicSubscriber(uri, String.class, String.class, subscriber); publisher.publish("Message-1"); publisher.publish("Message-2"); assertEquals("topic Message-1", values.poll(2, SECONDS)); assertEquals("topic Message-2", values.poll(2, SECONDS)); Jvm.pause(100); assertEquals("", values.toString()); } @Test public void testStringPublishWithIndex() throws InterruptedException, IOException { @Nullable QueueView<String, String> publisher = null; @NotNull String uri = "/queue/" + methodName + DELETE_CHRONICLE_FILE; @NotNull final RequestContext requestContext1 = requestContext(uri); if (requestContext1.bootstrap() != null) throw new UnsupportedOperationException("Its not possible to set the bootstrap when " + "acquiring a queue"); publisher = assetTree.acquireView(requestContext1.view("queue").type(String.class).type2(String .class) .cluster("")); final long index = publisher.publishAndIndex(methodName, "Message-1"); @NotNull final RequestContext requestContext = requestContext(uri); if (requestContext.bootstrap() != null) throw new UnsupportedOperationException("Its not possible to set the bootstrap when " + "acquiring a queue"); @NotNull QueueView<String, String> queue = assetTree.acquireView(requestContext.view("queue").type(String.class).type2(String.class) .cluster("")); @Nullable final Excerpt<String, String> excerpt = queue.getExcerpt(index); assertEquals(methodName, excerpt.topic()); assertEquals("Message-1", excerpt.message()); } @Test public void testMarshablePublishToATopic() throws InterruptedException { @Nullable Publisher<MyMarshallable> publisher = null; @NotNull String uri = "/queue/" + methodName + DELETE_CHRONICLE_FILE; publisher = assetTree.acquirePublisher(uri, MyMarshallable.class); @NotNull BlockingQueue<MyMarshallable> values2 = new LinkedBlockingQueue<>(); assetTree.registerSubscriber(uri, MyMarshallable.class, values2::add); publisher.publish(new MyMarshallable("Message-1")); publisher.publish(new MyMarshallable("Message-2")); assertEquals("Message-1", values2.poll(2, SECONDS).toString()); assertEquals("Message-2", values2.poll(2, SECONDS).toString()); Jvm.pause(100); assertEquals("[]", values2.toString()); } }