/*
* 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;
import net.openhft.chronicle.core.OS;
import net.openhft.chronicle.core.threads.ThreadDump;
import net.openhft.chronicle.engine.api.map.KeyValueStore;
import net.openhft.chronicle.engine.api.map.MapEvent;
import net.openhft.chronicle.engine.api.tree.LeafViewFactory;
import net.openhft.chronicle.engine.map.AuthenticatedKeyValueStore;
import net.openhft.chronicle.engine.map.FilePerKeyValueStore;
import net.openhft.chronicle.engine.map.VanillaMapView;
import net.openhft.chronicle.engine.map.VanillaStringMarshallableKeyValueStore;
import net.openhft.chronicle.engine.tree.VanillaAsset;
import net.openhft.chronicle.wire.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static net.openhft.chronicle.core.Jvm.pause;
import static net.openhft.chronicle.engine.Chassis.*;
import static org.junit.Assert.assertEquals;
/**
* JUnit test class to support
*/
public class MarshallableFilePerKeyValueStoreTest {
public static final String NAME = "marsfileperkvstoretests";
private Map<String, TestMarshallable> map;
private ThreadDump threadDump;
@Before
public void threadDump() {
threadDump = new ThreadDump();
}
@After
public void checkThreadDump() {
threadDump.assertNoNewThreads();
}
@Before
public void createMap() throws IOException {
resetChassis();
@NotNull WireType writeType = WireType.TEXT;
((VanillaAsset) assetTree().root()).enableTranslatingValuesToBytesStore();
@NotNull LeafViewFactory<AuthenticatedKeyValueStore> factory = (context, asset) -> new FilePerKeyValueStore(context.basePath(OS.TARGET).wireType(writeType), asset);
assetTree().root().addLeafRule(AuthenticatedKeyValueStore.class, "FilePer Key", factory);
map = acquireMap(NAME, String.class, TestMarshallable.class);
@Nullable KeyValueStore mapU = ((VanillaMapView) map).underlying();
assertEquals(VanillaStringMarshallableKeyValueStore.class, mapU.getClass());
assertEquals(FilePerKeyValueStore.class, mapU.underlying().getClass());
//just in case it hasn't been cleared up last time
map.clear();
// allow the events to be picked up.
pause(50);
}
@After
public void cleanUp() {
map.clear();
}
@Ignore("todo fix - see JIRA https://higherfrequencytrading.atlassian.net/browse/CE-118")
@Test
public void test() throws InterruptedException {
@NotNull TestMarshallable tm = new TestMarshallable("testing1", "testing2",
new Nested(Arrays.asList(2.3, 4.5, 6.7, 8.9)));
@NotNull List<MapEvent<String, TestMarshallable>> events = new ArrayList<>();
registerSubscriber(NAME, MapEvent.class, events::add);
map.put("testA", tm);
map.put("testB", tm);
waitFor(events, 2);
tm.setS1("hello");
map.put("testB", tm);
assertEquals(2, map.size());
assertEquals("testing1", map.get("testA").getS1());
assertEquals(4.5, map.get("testA").getNested().getListDouble().get(1), 0);
waitFor(events, 3);
TimeUnit.MILLISECONDS.sleep(100);
if (events.size() != 3)
events.forEach(System.out::println);
assertEquals(3, events.size());
}
private void waitFor(@NotNull List<MapEvent<String, TestMarshallable>> events, int count) throws InterruptedException {
for (int i = 1; i <= 10; i++) {
if (events.size() >= count)
break;
TimeUnit.MILLISECONDS.sleep(i * i);
}
}
static class TestMarshallable implements Marshallable {
private String s1, s2;
private Nested nested;
// used via reflection
@SuppressWarnings("unused")
public TestMarshallable() {
nested = new Nested();
}
public TestMarshallable(String s1, String s2, Nested nested) {
this.s1 = s1;
this.s2 = s2;
this.nested = nested;
}
public Nested getNested() {
return nested;
}
public void setNested(Nested nested) {
this.nested = nested;
}
public String getS1() {
return s1;
}
public void setS1(String s1) {
this.s1 = s1;
}
public String getS2() {
return s2;
}
public void setS2(String s2) {
this.s2 = s2;
}
@Override
public void readMarshallable(@NotNull WireIn wireIn) throws IllegalStateException {
setS1(wireIn.read(TestKey.S1).text());
setS2(wireIn.read(TestKey.S2).text());
wireIn.read(TestKey.nested).marshallable(nested);
}
@Override
public void writeMarshallable(@NotNull WireOut wireOut) {
wireOut.write(TestKey.S1).text(getS1());
wireOut.write(TestKey.S2).text(getS2());
wireOut.write(TestKey.nested).marshallable(nested);
}
@NotNull
@Override
public String toString() {
return "TestMarshallable{" +
"s1='" + s1 + '\'' +
", s2='" + s2 + '\'' +
", nested=" + nested +
'}';
}
private enum TestKey implements WireKey {
S1, S2, nested
}
}
static class Nested implements Marshallable {
List<Double> listDouble;
public Nested() {
listDouble = new ArrayList<>();
}
public Nested(List<Double> listDouble) {
this.listDouble = listDouble;
}
public List<Double> getListDouble() {
return listDouble;
}
public void setListDouble(List<Double> listDouble) {
this.listDouble = listDouble;
}
@Override
public void readMarshallable(@NotNull WireIn wireIn) throws IllegalStateException {
listDouble.clear();
wireIn.read(TestKey.listDouble).sequence(this, (o, v) -> {
while (v.hasNextSequenceItem()) {
v.float64(o.listDouble, List::add);
}
});
}
@Override
public void writeMarshallable(@NotNull WireOut wireOut) {
wireOut.write(TestKey.listDouble).sequence(v ->
listDouble.stream().forEach(v::float64)
);
}
@NotNull
@Override
public String toString() {
return "Nested{" +
"listDouble=" + listDouble +
'}';
}
private enum TestKey implements WireKey {
listDouble
}
}
}