From 68c66d10948a84a46182a361b453f92551039416 Mon Sep 17 00:00:00 2001 From: Rob Austin Date: Fri, 28 Nov 2014 00:14:37 +0000 Subject: [PATCH] HCOLL-219 improved the test coverage and implementation of import and export --- .../chronicle/map/VanillaChronicleMap.java | 205 ++++++++++-------- .../map/ChronicleMapImportExportTest.java | 167 +++++++++++++- .../openhft/chronicle/map/MapByNameTest.java | 3 +- 3 files changed, 284 insertions(+), 91 deletions(-) diff --git a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java index eeff3d0be..f3b848de1 100755 --- a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java @@ -41,9 +41,9 @@ import net.openhft.lang.threadlocal.Provider; import net.openhft.lang.threadlocal.StatefulCopyable; import net.openhft.lang.threadlocal.ThreadLocalCopies; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.jetbrains.annotations.Nullable; import java.io.*; import java.lang.reflect.Array; @@ -473,11 +473,11 @@ class VanillaChronicleMap, , RV, VB extends RV, VBI, MVBI extends MetaBytesInterop> RV put(ThreadLocalCopies copies, SegmentState segmentState, - MKBI metaKeyInterop, KBI keyInterop, KB key, long keySize, - InstanceOrBytesToInstance toKey, - GetValueInterops getValueInterops, VB value, - InstanceOrBytesToInstance toValue, - boolean replaceIfPresent, ReadValue readValue, boolean resultUnused) { + MKBI metaKeyInterop, KBI keyInterop, KB key, long keySize, + InstanceOrBytesToInstance toKey, + GetValueInterops getValueInterops, VB value, + InstanceOrBytesToInstance toValue, + boolean replaceIfPresent, ReadValue readValue, boolean resultUnused) { long hash = metaKeyInterop.hash(keyInterop, key); int segmentNum = getSegment(hash); long segmentHash = segmentHash(hash); @@ -533,10 +533,10 @@ class VanillaChronicleMap, private , RV> RV lookupUsing(ThreadLocalCopies copies, SegmentState segmentState, - MKBI metaKeyInterop, KBI keyInterop, KB key, long keySize, - InstanceOrBytesToInstance toKey, - ReadValue readValue, RV usingValue, InstanceOrBytesToInstance toValue, - boolean create) { + MKBI metaKeyInterop, KBI keyInterop, KB key, long keySize, + InstanceOrBytesToInstance toKey, + ReadValue readValue, RV usingValue, InstanceOrBytesToInstance toValue, + boolean create) { long hash = metaKeyInterop.hash(keyInterop, key); int segmentNum = getSegment(hash); long segmentHash = segmentHash(hash); @@ -666,17 +666,14 @@ class VanillaChronicleMap, final XStream xstream = new XStream(new JettisonMappedXmlDriver()); xstream.setMode(XStream.NO_REFERENCES); - xstream.alias("key", kClass); - xstream.alias("value", vClass); - xstream.alias("entries", net.openhft.chronicle.map.VanillaChronicleMap.EntrySet.class); + + xstream.alias("chronicle-entries", net.openhft.chronicle.map.VanillaChronicleMap.EntrySet.class); // ideally we will use java serialization if we can - lazyXStreamConverter().registerConverter(xstream); - try (FileOutputStream out = new FileOutputStream(toFile)) { - xstream.toXML(entrySet, out); + xstream.toXML(entrySet(), out); } } @@ -685,9 +682,8 @@ class VanillaChronicleMap, public void putAll(File fromFile) throws IOException { final XStream xstream = new XStream(new JettisonMappedXmlDriver()); xstream.setMode(XStream.NO_REFERENCES); - xstream.alias("key", kClass); - xstream.alias("value", vClass); - xstream.alias("entries", net.openhft.chronicle.map.VanillaChronicleMap.EntrySet.class); + xstream.alias("chronicle-entries", net.openhft.chronicle.map.VanillaChronicleMap.EntrySet + .class); // ideally we will use java serialization if we can lazyXStreamConverter().registerConverter(xstream); @@ -788,15 +784,18 @@ class VanillaChronicleMap, public boolean canConvert(Class aClass) { if (EntrySet.class - .isAssignableFrom(aClass) || SimpleEntry.class - .isAssignableFrom(aClass)) + .isAssignableFrom(aClass) || WriteThroughEntry.class.isAssignableFrom + (aClass)) return true; if (Serializable.class .isAssignableFrom(aClass)) return false; - return kClass.equals(aClass) || vClass.equals(aClass); + if (vClass.equals(Map.class)) + return false; + + return kClass.isAssignableFrom(aClass) || vClass.isAssignableFrom(aClass); } @Override @@ -805,33 +804,43 @@ class VanillaChronicleMap, buffer.clear(); - if (o.getClass().equals(kClass)) { + if (kClass.isAssignableFrom(o.getClass())) { - //String text = toKeyBytes((K) o).asString().toString(); - // System.out.println("key=" + text); + String text = toKeyBytes((K) o).asString().toString(); + System.out.println("key=" + text); // writer.setValue(text); - // writer.startNode("key"); writer.setValue(o.toString()); // writer.endNode(); - } else if (o.getClass().equals(vClass)) { + } else if (vClass.isAssignableFrom(o.getClass())) { writer.setValue(o.toString()); - } else if (o instanceof Entry) { + } else if (WriteThroughEntry.class.isAssignableFrom + (o.getClass())) { + final SimpleEntry e = (SimpleEntry) o; - writer.startNode("key"); - marshallingContext.convertAnother(e.getKey()); + writer.startNode("chronicle-key"); + + Object key = e.getKey(); + if (!kClass.equals(key.getClass())) + writer.addAttribute("type", key.getClass().getCanonicalName()); + + marshallingContext.convertAnother(key); writer.endNode(); - writer.startNode("value"); - marshallingContext.convertAnother(e.getValue()); + + writer.startNode("chronicle-value"); + Object value = e.getValue(); + if (!vClass.equals(value.getClass())) + writer.addAttribute("type", value.getClass().getCanonicalName()); + marshallingContext.convertAnother(value); writer.endNode(); } else if (EntrySet.class .isAssignableFrom(o.getClass())) { for (Entry e : (EntrySet) o) { - writer.startNode("entry"); + writer.startNode("chronicle-entry"); marshallingContext.convertAnother(e); writer.endNode(); } @@ -850,14 +859,14 @@ class VanillaChronicleMap, final String nodeName = reader.getNodeName(); - if (nodeName.equals("entries")) { + if (nodeName.equals("chronicle-entries")) { while (reader.hasMoreChildren()) { reader.moveDown(); final String nodeName0 = reader .getNodeName(); - if (!nodeName0.equals("entry")) + if (!nodeName0.equals("chronicle-entry")) throw new ConversionException("unable to convert node " + "named=" + nodeName0); K k = null; @@ -867,13 +876,19 @@ class VanillaChronicleMap, reader.moveDown(); final String nodeName1 = reader .getNodeName(); - if ("key".equals(nodeName1)) - k = (K) unmarshallingContext.convertAnother(null, kClass); - else if ("value".equals(nodeName1)) - v = (V) unmarshallingContext.convertAnother(null, vClass); - else throw new ConversionException - ("expecting either a key or" + - " value"); + if ("chronicle-key".equals(nodeName1)) { + + + k = get(reader, unmarshallingContext, kClass); + } else if ("chronicle-value".equals(nodeName1)) { + + + v = get(reader, unmarshallingContext, vClass); + + + } else throw new ConversionException + ("expecting either a key or" + + " value"); reader.moveUp(); } @@ -883,13 +898,29 @@ class VanillaChronicleMap, } - } else if (nodeName.equals("key")) + } else if (nodeName.equals("chronicle-key")) return keyValueSerializer.readKey(buffer, threadLocalCopies); - else if (nodeName.equals("value")) + else if (nodeName.equals("chronicle-value")) return keyValueSerializer.readValue(buffer, threadLocalCopies); return null; } + + private E get(HierarchicalStreamReader reader, UnmarshallingContext + unmarshallingContext, final Class clazz) { + + if (reader.getAttributeCount() > 0) { + String type = reader.getAttribute("type"); + try { + return (E) unmarshallingContext.convertAnother(null, Class.forName + (type)); + } catch (ClassNotFoundException e) { + throw new ConversionException(e); + } + } else + return (E) unmarshallingContext.convertAnother(null, clazz); + + } }; xstream.registerConverter(converter); @@ -956,7 +987,7 @@ class VanillaChronicleMap, if (mustReuseValue && returnedValue != null && returnedValue != usingValue) throw new IllegalArgumentException( "acquireUsingLocked/getUsingLocked MUST reuse given " + - "values. Given value" + usingValue + + "values. Given value" + usingValue + " cannot be reused to read " + returnedValue); } @@ -977,30 +1008,30 @@ class VanillaChronicleMap, transient InstanceOrBytesToInstance keyBytesToInstance = new InstanceOrBytesToInstance() { - @Override - public K toInstance(@NotNull ThreadLocalCopies copies, Bytes keyBytes, long size) { - assertNotNull(copies); - BytesReader keyReader = keyReaderProvider.get(copies, originalKeyReader); - return keyReader.read(keyBytes, size); - } - }; + @Override + public K toInstance(@NotNull ThreadLocalCopies copies, Bytes keyBytes, long size) { + assertNotNull(copies); + BytesReader keyReader = keyReaderProvider.get(copies, originalKeyReader); + return keyReader.read(keyBytes, size); + } + }; transient InstanceOrBytesToInstance valueBytesToInstance = new InstanceOrBytesToInstance() { - @Override - public V toInstance(@NotNull ThreadLocalCopies copies, Bytes valueBytes, long size) { - return readValue(copies, valueBytes, null, size); - } - }; + @Override + public V toInstance(@NotNull ThreadLocalCopies copies, Bytes valueBytes, long size) { + return readValue(copies, valueBytes, null, size); + } + }; private transient InstanceOrBytesToInstance outputValueBytesToInstance = new InstanceOrBytesToInstance() { - @Override - public V toInstance(@NotNull ThreadLocalCopies copies, Bytes outputBytes, long size) { - outputBytes.position(outputBytes.position() - size); - return readValue(copies, outputBytes, null, size); - } - }; + @Override + public V toInstance(@NotNull ThreadLocalCopies copies, Bytes outputBytes, long size) { + outputBytes.position(outputBytes.position() - size); + return readValue(copies, outputBytes, null, size); + } + }; @Override public final boolean containsKey(final Object k) { @@ -1251,13 +1282,13 @@ class VanillaChronicleMap, * replace the value in a map, only if the existing entry equals {@param expectedValue} * * @param key the key into the map - * @param expectedValue the expected existing value in the map - * ( could be null when we don't wish to do this check ) + * @param expectedValue the expected existing value in the map ( could be null when we don't + * wish to do this check ) * @param newValue the new value you wish to store in the map * @return the value that was replaced */ private Object replaceIfValueIs(@net.openhft.lang.model.constraints.NotNull final K key, - final V expectedValue, final V newValue) { + final V expectedValue, final V newValue) { checkKey(key); checkValue(newValue); ThreadLocalCopies copies = keyInteropProvider.getCopies(null); @@ -1372,7 +1403,7 @@ class VanillaChronicleMap, final void putAll(Bytes entries) { long numberOfEntries = entries.readStopBit(); long entryPosition = entries.position(); - while (numberOfEntries --> 0) { + while (numberOfEntries-- > 0) { long keySize = keySizeMarshaller.readSize(entries); entries.skip(keySize); long valueSize = valueSizeMarshaller.readSize(entries); @@ -1410,7 +1441,9 @@ class VanillaChronicleMap, Provider.of(SegmentState.class); private static final SegmentState originalSegmentState = new SegmentState(0); - static @NotNull ThreadLocalCopies getCopies(ThreadLocalCopies copies) { + static + @NotNull + ThreadLocalCopies getCopies(ThreadLocalCopies copies) { return segmentStateProvider.getCopies(copies); } @@ -1793,9 +1826,9 @@ class VanillaChronicleMap, metaKeyInterop, keyInterop, key, keySize, metaValueInterop, valueInterop, defaultValue, entry, true); } else if (prepareValueBytesAsWriter != null) { - valueSize = putEntry(segmentState, - metaKeyInterop, keyInterop, key, keySize, - prepareValueBytesAsWriter, null, keyInstance, entry, true); + valueSize = putEntry(segmentState, + metaKeyInterop, keyInterop, key, keySize, + prepareValueBytesAsWriter, null, keyInstance, entry, true); } else { throw defaultValueOrPrepareBytesShouldBeSpecified(); } @@ -1847,12 +1880,12 @@ class VanillaChronicleMap, private , RV, VB extends RV, VBI, MVBI extends MetaBytesInterop> RV put(@Nullable ThreadLocalCopies copies, final @Nullable SegmentState segmentState, - MKBI metaKeyInterop, KBI keyInterop, KB key, long keySize, - InstanceOrBytesToInstance toKey, - GetValueInterops getValueInterops, VB value, - InstanceOrBytesToInstance toValue, - long hash2, boolean replaceIfPresent, - ReadValue readValue, boolean resultUnused) { + MKBI metaKeyInterop, KBI keyInterop, KB key, long keySize, + InstanceOrBytesToInstance toKey, + GetValueInterops getValueInterops, VB value, + InstanceOrBytesToInstance toValue, + long hash2, boolean replaceIfPresent, + ReadValue readValue, boolean resultUnused) { shouldNotBeCalledFromReplicatedChronicleMap("Segment.put"); writeLock(null); try { @@ -1976,9 +2009,9 @@ class VanillaChronicleMap, } /** - * Returns value size, writes the entry (key, value, sizes) to the entry, after this - * method call entry positioned after value bytes written (i. e. at the end of entry), - * sets entry position (in segment) and value size position in the given segmentState + * Returns value size, writes the entry (key, value, sizes) to the entry, after this method + * call entry positioned after value bytes written (i. e. at the end of entry), sets entry + * position (in segment) and value size position in the given segmentState */ final , E, EW> long putEntry(SegmentState segmentState, @@ -2106,12 +2139,10 @@ class VanillaChronicleMap, } /** - * - if expectedValue is not null, returns Boolean.TRUE (removed) or - * Boolean.FALSE (entry not found), regardless the expectedValue object is Bytes instance - * (RPC call) or the value instance - * - if expectedValue is null: - * - if resultUnused is false, null or removed value is returned - * - if resultUnused is true, null is always returned + * - if expectedValue is not null, returns Boolean.TRUE (removed) or Boolean.FALSE (entry + * not found), regardless the expectedValue object is Bytes instance (RPC call) or the value + * instance - if expectedValue is null: - if resultUnused is false, null or removed value is + * returned - if resultUnused is true, null is always returned */ , VB, VBI, MVBI extends MetaBytesInterop> @@ -2683,7 +2714,7 @@ class VanillaChronicleMap, } } - private class WriteThroughEntry extends SimpleEntry { + class WriteThroughEntry extends SimpleEntry { private static final long serialVersionUID = 0L; WriteThroughEntry(K key, V value) { diff --git a/src/test/java/net/openhft/chronicle/map/ChronicleMapImportExportTest.java b/src/test/java/net/openhft/chronicle/map/ChronicleMapImportExportTest.java index 8bf8d2289..f922c68c7 100644 --- a/src/test/java/net/openhft/chronicle/map/ChronicleMapImportExportTest.java +++ b/src/test/java/net/openhft/chronicle/map/ChronicleMapImportExportTest.java @@ -1,10 +1,15 @@ package net.openhft.chronicle.map; import junit.framework.Assert; +import net.openhft.lang.values.LongValue; +import net.openhft.lang.values.LongValue$$Native; +import org.junit.Ignore; import org.junit.Test; import java.io.File; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; /** * @author Rob Austin. @@ -26,8 +31,6 @@ public class ChronicleMapImportExportTest { expected.put("hello", "world"); expected.put("aKey", "aValue"); - - System.out.println(expected); expected.getAll(file); try (ChronicleMap actual = ChronicleMapBuilder.of(String.class, String @@ -46,5 +49,165 @@ public class ChronicleMapImportExportTest { } + @Test + public void testWithMapValue() throws IOException, InterruptedException { + + File file = new File(TMP + "/chronicle-map-" + System.nanoTime() + ".json"); + file.deleteOnExit(); + + System.out.println(file.getAbsoluteFile()); + try (ChronicleMap expected = ChronicleMapBuilder.of(String.class, Map + .class) + .create()) { + + HashMap data = new HashMap<>(); + data.put("myKey", "myValue"); + expected.put("hello", data); + expected.getAll(file); + + try (ChronicleMap actual = ChronicleMapBuilder.of(String.class, Map + .class) + .create()) { + + actual.putAll(file); + + Assert.assertEquals(expected, actual); + } + } finally { + file.delete(); + } + + } + + + @Test + public void testWithMapOfMapValue() throws IOException, InterruptedException { + + File file = new File(TMP + "/chronicle-map-" + System.nanoTime() + ".json"); + file.deleteOnExit(); + + System.out.println(file.getAbsoluteFile()); + try (ChronicleMap expected = ChronicleMapBuilder.of + (String.class, Map.class).create()) { + + HashMap data = new HashMap<>(); + HashMap data2 = new HashMap<>(); + data2.put("nested", "map"); + data.put("myKey", data2); + expected.put("hello", data); + + expected.getAll(file); + + try (ChronicleMap actual = ChronicleMapBuilder.of(String.class, Map + .class) + .create()) { + + actual.putAll(file); + + Assert.assertEquals(expected, actual); + } + } finally { + file.delete(); + } + + + } + + + @Test + public void testWithIntegerAndDouble() throws IOException, InterruptedException { + + File file = new File(TMP + "/chronicle-map-" + System.nanoTime() + ".json"); + file.deleteOnExit(); + + try (ChronicleMap expected = ChronicleMapBuilder.of(Integer.class, Double + .class) + .create()) { + + + expected.put(1, 1.0); + + + expected.getAll(file); + + try (ChronicleMap actual = ChronicleMapBuilder.of(Integer.class, Double + .class) + .create()) { + + actual.putAll(file); + + Assert.assertEquals(expected, actual); + } + } finally { + file.delete(); + } + + + } + + + @Test + public void testWithCharSeq() throws IOException, InterruptedException { + + File file = new File(TMP + "/chronicle-map-" + System.nanoTime() + ".json"); + file.deleteOnExit(); + + System.out.println(file.getAbsolutePath()); + try (ChronicleMap expected = ChronicleMapBuilder.of(CharSequence.class, CharSequence + .class) + .create()) { + + + expected.put("hello", "world"); + + + expected.getAll(file); + + try (ChronicleMap actual = ChronicleMapBuilder.of(CharSequence.class, CharSequence + .class) + .create()) { + + actual.putAll(file); + + Assert.assertEquals(expected, actual); + } + } finally { + file.delete(); + } + + + } + + @Ignore("this tyep of off heap reference is not currently supported") + @Test + public void testWithLongValue() throws IOException, InterruptedException { + + File file = new File(TMP + "/chronicle-map-" + System.nanoTime() + ".json"); + file.deleteOnExit(); + + System.out.println(file.getAbsolutePath()); + try (ChronicleMap expected = ChronicleMapBuilder.of(CharSequence.class, LongValue + .class) + .create()) { + + + expected.put("hello", new LongValue$$Native()); + + expected.getAll(file); + + try (ChronicleMap actual = ChronicleMapBuilder.of(CharSequence.class, LongValue + .class) + .create()) { + + actual.putAll(file); + + Assert.assertEquals(expected, actual); + } + } finally { + file.delete(); + } + + + } } diff --git a/src/test/java/net/openhft/chronicle/map/MapByNameTest.java b/src/test/java/net/openhft/chronicle/map/MapByNameTest.java index a4453f785..c4ed5e881 100644 --- a/src/test/java/net/openhft/chronicle/map/MapByNameTest.java +++ b/src/test/java/net/openhft/chronicle/map/MapByNameTest.java @@ -67,9 +67,8 @@ public class MapByNameTest { // } - // currently work in progress @Test - @Ignore + @Ignore("currently work in progress") public void testConnectByName() throws IOException, InterruptedException, TimeoutException { NodeDiscovery nodeDiscovery = new NodeDiscovery();