/*
* 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.map;
import net.openhft.chronicle.core.Jvm;
import net.openhft.chronicle.core.threads.ThreadDump;
import net.openhft.chronicle.engine.api.pubsub.Reference;
import net.openhft.chronicle.engine.api.pubsub.Subscriber;
import net.openhft.chronicle.engine.api.pubsub.SubscriptionCollection;
import net.openhft.chronicle.engine.api.tree.Asset;
import net.openhft.chronicle.engine.api.tree.AssetTree;
import net.openhft.chronicle.engine.server.ServerEndpoint;
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 net.openhft.chronicle.wire.YamlLogging;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import static net.openhft.chronicle.engine.Utils.methodName;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* Created by daniel on 13/07/2015.
*/
@RunWith(value = Parameterized.class)
public class ReferenceTest {
@NotNull
private static AtomicReference<Throwable> t = new AtomicReference<>();
@NotNull
@Rule
public TestName name = new TestName();
WireType wireType;
VanillaAssetTree serverAssetTree;
AssetTree assetTree;
private boolean isRemote;
private ServerEndpoint serverEndpoint;
private String hostPortToken;
private ThreadDump threadDump;
public ReferenceTest(boolean isRemote, @Nullable WireType wireType) {
this.wireType = wireType;
this.isRemote = isRemote;
YamlLogging.setAll(false);
}
@Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(
new Object[]{false, null}
, new Object[]{true, WireType.TEXT}
, new Object[]{true, WireType.BINARY}
);
}
@Before
public void threadDump() {
threadDump = new ThreadDump();
}
@After
public void afterMethod() {
final Throwable th = t.getAndSet(null);
if (th != null) throw Jvm.rethrow(th);
}
@Before
public void before() throws IOException {
hostPortToken = "ReferenceTest.host.port";
serverAssetTree = new VanillaAssetTree().forTesting();
if (isRemote) {
methodName(name.getMethodName());
TCPRegistry.createServerSocketChannelFor(hostPortToken);
serverEndpoint = new ServerEndpoint(hostPortToken, serverAssetTree);
assetTree = new VanillaAssetTree()
.forRemoteAccess(hostPortToken, wireType);
} else {
assetTree = serverAssetTree;
}
}
@After
public void after() {
assetTree.close();
if (serverEndpoint != null)
serverEndpoint.close();
serverAssetTree.close();
TcpChannelHub.closeAllHubs();
TCPRegistry.reset();
final Throwable th = t.getAndSet(null);
if (th != null) throw Jvm.rethrow(th);
threadDump.assertNoNewThreads();
}
@Test
public void testRemoteReference() throws IOException {
@NotNull Map map = assetTree.acquireMap("group", String.class, String.class);
map.put("subject", "cs");
assertEquals("cs", map.get("subject"));
@NotNull Reference<String> ref = assetTree.acquireReference("group/subject", String.class);
ref.set("sport");
assertEquals("sport", map.get("subject"));
assertEquals("sport", ref.get());
ref.getAndSet("biology");
assertEquals("biology", ref.get());
@Nullable String s = ref.getAndRemove();
assertEquals("biology", s);
ref.set("physics");
assertEquals("physics", ref.get());
ref.remove();
assertEquals(null, ref.get());
ref.set("chemistry");
assertEquals("chemistry", ref.get());
s = ref.applyTo(o -> "applied_" + o.toString());
assertEquals("applied_chemistry", s);
ref.asyncUpdate(o -> "**" + o.toString());
assertEquals("**chemistry", ref.get());
ref.set("maths");
assertEquals("maths", ref.get());
s = ref.syncUpdate(o -> "**" + o.toString(), o -> "**" + o.toString());
assertEquals("****maths", s);
assertEquals("**maths", ref.get());
}
@Test
public void testReferenceSubscriptions() throws InterruptedException {
@NotNull Map map = assetTree.acquireMap("group", String.class, String.class);
map.put("subject", "cs");
assertEquals("cs", map.get("subject"));
@NotNull Reference<String> ref = assetTree.acquireReference("group/subject", String.class);
ref.set("sport");
assertEquals("sport", map.get("subject"));
assertEquals("sport", ref.get());
@NotNull CountDownLatch lacth1 = new CountDownLatch(1);
@NotNull CountDownLatch lacth2 = new CountDownLatch(2);
@NotNull CountDownLatch lacth3 = new CountDownLatch(3);
@NotNull List<String> events = new ArrayList<>();
@NotNull Subscriber<String> subscriber = s -> {
events.add(s);
lacth1.countDown();
lacth2.countDown();
lacth3.countDown();
};
assetTree.registerSubscriber("group/subject?bootstrap=true", String.class, subscriber);
lacth1.await(20, TimeUnit.SECONDS);
assertEquals("sport", events.get(0));//bootstrap
ref.set("maths");
lacth2.await(20, TimeUnit.SECONDS);
assertEquals("maths", events.get(1));
ref.set("cs");
lacth3.await(20, TimeUnit.SECONDS);
assertEquals("cs", events.get(2));
}
@Test
public void testAssetReferenceSubscriptions() {
@NotNull Map map = assetTree.acquireMap("group", String.class, String.class);
//TODO The child has to be in the map before you register to it
map.put("subject", "init");
@NotNull List<String> events = new ArrayList<>();
@NotNull Subscriber<String> keyEventSubscriber = new Subscriber<String>() {
@Override
public void onMessage(String s) {
events.add(s);
}
@Override
public void onEndOfSubscription() {
events.add("END");
}
};
assetTree.registerSubscriber("group" + "/" + "subject" + "?bootstrap=false&putReturnsNull=true", String.class, keyEventSubscriber);
// Jvm.pause(100);
Asset child = assetTree.getAsset("group").getChild("subject");
assertNotNull(child);
@Nullable SubscriptionCollection subscription = child.subscription(false);
while (subscription.subscriberCount() == 0) {
}
assertEquals(1, subscription.subscriberCount());
assetTree.unregisterSubscriber("group" + "/" + "subject", keyEventSubscriber);
while (subscription.subscriberCount() != 0) {
}
assertEquals(0, subscription.subscriberCount());
}
@Test
public void testAssetReferenceSubscriptionsBootstrapTrue() {
@NotNull Map map = assetTree.acquireMap("group", String.class, String.class);
//TODO The child has to be in the map before you register to it
map.put("subject", "init");
@NotNull List<String> events = new ArrayList<>();
@NotNull Subscriber<String> keyEventSubscriber = new Subscriber<String>() {
@Override
public void onMessage(String s) {
events.add(s);
}
@Override
public void onEndOfSubscription() {
events.add("END");
}
};
assetTree.registerSubscriber("group" + "/" + "subject" + "?bootstrap=true&putReturnsNull=true", String.class, keyEventSubscriber);
Jvm.pause(100);
Asset child = assetTree.getAsset("group").getChild("subject");
assertNotNull(child);
@Nullable SubscriptionCollection subscription = child.subscription(false);
assertEquals(1, subscription.subscriberCount());
map.put("subject", "cs");
map.put("subject", "maths");
assetTree.unregisterSubscriber("group" + "/" + "subject", keyEventSubscriber);
Jvm.pause(100);
assertEquals(0, subscription.subscriberCount());
assertEquals("init", events.get(0));
assertEquals("cs", events.get(1));
assertEquals("maths", events.get(2));
assertEquals("END", events.get(3));
}
@Test
public void testSubscriptionMUFG() {
@NotNull String key = "subject";
@NotNull String _mapName = "group";
@NotNull Map map = assetTree.acquireMap(_mapName, String.class, String.class);
//TODO does not work without an initial put
map.put(key, "init");
@NotNull List<String> events = new ArrayList<>();
@NotNull Subscriber<String> keyEventSubscriber = s -> {
System.out.println("** rec:" + s);
events.add(s);
};
assetTree.registerSubscriber(_mapName + "/" + key + "?bootstrap=false&putReturnsNull=true", String.class, keyEventSubscriber);
// TODO CHENT-49
Jvm.pause(100);
Asset child = assetTree.getAsset(_mapName).getChild(key);
assertNotNull(child);
@Nullable SubscriptionCollection subscription = child.subscription(false);
assertEquals(1, subscription.subscriberCount());
// YamlLogging.showServerWrites(true);
//Perform test a number of times to allow the JVM to warm up, but verify runtime against average
@NotNull AtomicInteger count = new AtomicInteger();
// IntStream.range(0, 3).forEach(i ->
// {
//_testMap.put(key, _twoMbTestString + i);
map.put(key, "" + count.incrementAndGet());
map.put(key, "" + count.incrementAndGet());
map.put(key, "" + count.incrementAndGet());
// });
for (int i = 0; i < 100; i++) {
if (events.size() == 3)
break;
Jvm.pause(150);
}
assertEquals(3, events.size());
assetTree.unregisterSubscriber(_mapName + "/" + key, keyEventSubscriber);
Jvm.pause(100);
assertEquals(0, subscription.subscriberCount());
}
}