From 93a8fce074f43b79670ab396de76d9d7b5961aa3 Mon Sep 17 00:00:00 2001 From: Rob Austin Date: Tue, 16 Dec 2014 21:29:47 +0000 Subject: [PATCH] HCOLL-242 fixed - this type of off heap reference is not currently supported --- .../chronicle/map/VanillaChronicleMap.java | 115 ++++++++++++++---- .../map/ChronicleMapImportExportTest.java | 2 +- 2 files changed, 95 insertions(+), 22 deletions(-) diff --git a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java index 3b7f70e95..6fdfd2cb1 100755 --- a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java @@ -48,6 +48,7 @@ import org.slf4j.LoggerFactory; import java.io.*; import java.lang.reflect.Array; +import java.lang.reflect.Method; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.util.*; @@ -622,7 +623,6 @@ class VanillaChronicleMap, final XStream xstream = new XStream(new JettisonMappedXmlDriver()); xstream.setMode(XStream.NO_REFERENCES); xstream.alias("chronicle-entries", VanillaChronicleMap.EntrySet.class); - // ideally we will use java serialization if we can lazyXStreamConverter().registerConverter(xstream); OutputStream outputStream = new FileOutputStream(toFile); @@ -699,6 +699,7 @@ class VanillaChronicleMap, private XStreamConverter xStreamConverter; + /** * @return a lazily created XStreamConverter */ @@ -721,18 +722,17 @@ class VanillaChronicleMap, @Override public boolean canConvert(Class aClass) { + if (EntrySet.class.isAssignableFrom(aClass) || WriteThroughEntry.class.isAssignableFrom(aClass)) return true; - if (Serializable.class - .isAssignableFrom(aClass)) - return false; + String canonicalName = aClass.getCanonicalName(); + + if (canonicalName.startsWith("net.openhft.lang.values")) { + return true; + } - if (vClass.equals(Map.class)) - return false; - // todo add this back in when we do native - // return kClass.isAssignableFrom(aClass) || vClass.isAssignableFrom(aClass); return false; } @@ -745,16 +745,14 @@ class VanillaChronicleMap, writer.startNode("chronicle-key"); Object key = e.getKey(); - if (!kClass.equals(key.getClass())) - writer.addAttribute("type", key.getClass().getCanonicalName()); + writeType(writer, key, kClass); marshallingContext.convertAnother(key); writer.endNode(); writer.startNode("chronicle-value"); Object value = e.getValue(); - if (!vClass.equals(value.getClass())) - writer.addAttribute("type", value.getClass().getCanonicalName()); + writeType(writer, value, vClass); marshallingContext.convertAnother(value); writer.endNode(); } else if (EntrySet.class @@ -765,6 +763,23 @@ class VanillaChronicleMap, writer.endNode(); } } + if (o.getClass().getCanonicalName().startsWith("net.openhft.lang.values") + && (o.getClass().getCanonicalName().endsWith("$$Native") || + o.getClass().getCanonicalName().endsWith("$$Heap"))) { + Method[] methods = o.getClass().getMethods(); + for (Method method : methods) { + if (method.getName().equals("getValue") && method.getParameterTypes() + .length == 0) { + try { + Object result = method.invoke(o); + writer.setValue(result.toString()); + return; + } catch (Exception e) { + throw new RuntimeException("class=" + o.getClass().getCanonicalName(), e); + } + } + } + } } @Override @@ -804,18 +819,30 @@ class VanillaChronicleMap, } break; case "chronicle-key": - long keySize = keySizeMarshaller.readSize(buffer); - ThreadLocalCopies copies = keyReaderProvider.getCopies(null); - BytesReader keyReader = keyReaderProvider.get(copies, originalKeyReader); - return keyReader.read(buffer, keySize); + + if (kClass.getCanonicalName().startsWith("net.openhft.lang.values")) { + return toNative(reader, vClass); + } else { + long keySize = keySizeMarshaller.readSize(buffer); + ThreadLocalCopies copies = keyReaderProvider.getCopies(null); + BytesReader keyReader = keyReaderProvider.get(copies, originalKeyReader); + return keyReader.read(buffer, keySize); + } case "chronicle-value": - long valueSize = valueSizeMarshaller.readSize(buffer); - copies = valueReaderProvider.getCopies(null); - BytesReader valueReader = - valueReaderProvider.get(copies, originalValueReader); - return valueReader.read(buffer, valueSize); + + if (vClass.getCanonicalName().startsWith("net.openhft.lang.values")) { + return toNative(reader, vClass); + } else { + long valueSize = valueSizeMarshaller.readSize(buffer); + ThreadLocalCopies copies = valueReaderProvider.getCopies(null); + BytesReader valueReader = + valueReaderProvider.get(copies, originalValueReader); + return valueReader.read(buffer, valueSize); + } + } + return null; } @@ -831,10 +858,56 @@ class VanillaChronicleMap, } } else return (E) unmarshallingContext.convertAnother(null, clazz); } + + private Object toNative(HierarchicalStreamReader reader, final Class aClass) { + + Object o = DataValueClasses.newDirectInstance(aClass); + try { + for (Method $$nativeMethod : o.getClass().getMethods()) { + if ($$nativeMethod.getName().equals("setValue") && $$nativeMethod.getParameterTypes() + .length == 1) { + + Class parameterType = $$nativeMethod.getParameterTypes()[0]; + String value = reader.getValue(); + + if (parameterType.isPrimitive()) { + String name = parameterType.getSimpleName(); + + String properName = "java.lang." + Character.toString(name + .charAt(0)).toUpperCase() + + name.substring(1); + parameterType = Class.forName(properName); + } + + Method valueOf = parameterType.getMethod("valueOf", String.class); + Object invoke = valueOf.invoke(null, value); + $$nativeMethod.invoke(o, invoke); + return o; + + } + } + } catch (Exception e) { + throw new ConversionException("class=" + aClass.getCanonicalName(), e); + } + throw new ConversionException("setValue(..) method not found in class=" + aClass + .getCanonicalName + ()); + } }; xstream.registerConverter(converter); } + + private void writeType(HierarchicalStreamWriter writer, Object o, final Class clazz) { + String simpleName = o.getClass().getCanonicalName(); + if (simpleName.endsWith("$$Native")) { + String nodeName = simpleName.substring(0, simpleName.length() - + "$$Native".length()); + if (!nodeName.equals(clazz.getCanonicalName())) + writer.addAttribute("type", nodeName); + } else if (!clazz.equals(o.getClass())) + writer.addAttribute("type", o.getClass().getCanonicalName()); + } } @NotNull diff --git a/src/test/java/net/openhft/chronicle/map/ChronicleMapImportExportTest.java b/src/test/java/net/openhft/chronicle/map/ChronicleMapImportExportTest.java index bb0d712da..2984996d7 100644 --- a/src/test/java/net/openhft/chronicle/map/ChronicleMapImportExportTest.java +++ b/src/test/java/net/openhft/chronicle/map/ChronicleMapImportExportTest.java @@ -182,7 +182,6 @@ public class ChronicleMapImportExportTest { } - @Ignore("HCOLL-242 - this type of off heap reference is not currently supported") @Test public void testWithLongValue() throws IOException, InterruptedException { @@ -208,6 +207,7 @@ public class ChronicleMapImportExportTest { try (ChronicleMap actual = ChronicleMapBuilder.of(CharSequence.class, LongValue .class) .create()) { + actual.putAll(file); Assert.assertEquals(expected, actual);