Disconnect {Lithium,NeonSR2} implementations 67/84267/2
authorRobert Varga <robert.varga@pantheon.tech>
Sat, 7 Sep 2019 07:54:32 +0000 (09:54 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Sat, 7 Sep 2019 09:12:19 +0000 (11:12 +0200)
We will need to define two new serialization formats: one
sharing object value serialization (for NeonSR3) and one
disconnected from it (for Magnesium).

In order to maintain scalability with so many formats, we want
to avoid JIT considering implementations which were not referenced
at runtime. This should make CHA and method dispatch more
efficient.

Since we are isolating things, we also pull ValueTypes lookups
and keep it in the same class for easier understanding.

Change-Id: I3db4e9633db4a75016e17ac31bb38452bcb9f42a
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/AbstractLithiumDataOutput.java [new file with mode: 0644]
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/AbstractNormalizedNodeDataOutput.java
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/LithiumNormalizedNodeOutputStreamWriter.java
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NeonSR2NormalizedNodeOutputStreamWriter.java
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/ValueTypes.java
opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/LithiumWriteObjectMappingTest.java [moved from opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/ValueTypesTest.java with 72% similarity]

diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/AbstractLithiumDataOutput.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/AbstractLithiumDataOutput.java
new file mode 100644 (file)
index 0000000..73a4bf7
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.datastore.node.utils.stream;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.Empty;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * "original" type mapping. Baseline is Lithium but it really was introduced in Oxygen, where {@code type empty} was
+ * remapped from null.
+ *
+ * <p>
+ * {@code uint8}, {@code uint16}, {@code uint32} use java.lang types with widening, hence their value types overlap with
+ * mapping of {@code int16}, {@code int32} and {@code int64}, making that difference indiscernible without YANG schema
+ * knowledge.
+ */
+abstract class AbstractLithiumDataOutput extends AbstractNormalizedNodeDataOutput {
+    private static final ImmutableMap<Class<?>, Byte> KNOWN_TYPES = ImmutableMap.<Class<?>, Byte>builder()
+            .put(String.class, ValueTypes.STRING_TYPE)
+            .put(Byte.class, ValueTypes.BYTE_TYPE)
+            .put(Integer.class, ValueTypes.INT_TYPE)
+            .put(Long.class, ValueTypes.LONG_TYPE)
+            .put(Boolean.class, ValueTypes.BOOL_TYPE)
+            .put(QName.class, ValueTypes.QNAME_TYPE)
+            .put(Short.class, ValueTypes.SHORT_TYPE)
+            .put(BigInteger.class, ValueTypes.BIG_INTEGER_TYPE)
+            .put(BigDecimal.class, ValueTypes.BIG_DECIMAL_TYPE)
+            .put(byte[].class, ValueTypes.BINARY_TYPE)
+            .put(Empty.class, ValueTypes.EMPTY_TYPE)
+            .build();
+
+
+    private final Map<String, Integer> stringCodeMap = new HashMap<>();
+
+    AbstractLithiumDataOutput(final DataOutput output) {
+        super(output);
+    }
+
+    @Override
+    final void writeObject(final DataOutput rawOuput, final Object value) throws IOException {
+        byte type = getSerializableType(value);
+        // Write object type first
+        rawOuput.writeByte(type);
+
+        switch (type) {
+            case ValueTypes.BOOL_TYPE:
+                rawOuput.writeBoolean((Boolean) value);
+                break;
+            case ValueTypes.QNAME_TYPE:
+                writeQName((QName) value);
+                break;
+            case ValueTypes.INT_TYPE:
+                rawOuput.writeInt((Integer) value);
+                break;
+            case ValueTypes.BYTE_TYPE:
+                rawOuput.writeByte((Byte) value);
+                break;
+            case ValueTypes.LONG_TYPE:
+                rawOuput.writeLong((Long) value);
+                break;
+            case ValueTypes.SHORT_TYPE:
+                rawOuput.writeShort((Short) value);
+                break;
+            case ValueTypes.BITS_TYPE:
+                writeObjSet((Set<?>) value);
+                break;
+            case ValueTypes.BINARY_TYPE:
+                byte[] bytes = (byte[]) value;
+                rawOuput.writeInt(bytes.length);
+                rawOuput.write(bytes);
+                break;
+            case ValueTypes.YANG_IDENTIFIER_TYPE:
+                writeYangInstanceIdentifierInternal((YangInstanceIdentifier) value);
+                break;
+            case ValueTypes.EMPTY_TYPE:
+                break;
+            case ValueTypes.STRING_BYTES_TYPE:
+                final byte[] valueBytes = value.toString().getBytes(StandardCharsets.UTF_8);
+                rawOuput.writeInt(valueBytes.length);
+                rawOuput.write(valueBytes);
+                break;
+            default:
+                rawOuput.writeUTF(value.toString());
+                break;
+        }
+    }
+
+    final void defaultWriteQName(final QName qname) throws IOException {
+        writeString(qname.getLocalName());
+        writeModule(qname.getModule());
+    }
+
+    final void defaultWriteModule(final QNameModule module) throws IOException {
+        writeString(module.getNamespace().toString());
+        writeString(module.getRevision().map(Revision::toString).orElse(null));
+    }
+
+    @Override
+    protected final void writeString(final String string) throws IOException {
+        if (string != null) {
+            final Integer value = stringCodeMap.get(string);
+            if (value == null) {
+                stringCodeMap.put(string, stringCodeMap.size());
+                writeByte(TokenTypes.IS_STRING_VALUE);
+                writeUTF(string);
+            } else {
+                writeByte(TokenTypes.IS_CODE_VALUE);
+                writeInt(value);
+            }
+        } else {
+            writeByte(TokenTypes.IS_NULL_VALUE);
+        }
+    }
+
+    abstract void writeModule(QNameModule module) throws IOException;
+
+    @VisibleForTesting
+    static final byte getSerializableType(final Object node) {
+        final Byte type = KNOWN_TYPES.get(requireNonNull(node).getClass());
+        if (type != null) {
+            if (type == ValueTypes.STRING_TYPE
+                    && ((String) node).length() >= ValueTypes.STRING_BYTES_LENGTH_THRESHOLD) {
+                return ValueTypes.STRING_BYTES_TYPE;
+            }
+            return type;
+        }
+
+        if (node instanceof Set) {
+            return ValueTypes.BITS_TYPE;
+        }
+
+        if (node instanceof YangInstanceIdentifier) {
+            return ValueTypes.YANG_IDENTIFIER_TYPE;
+        }
+
+        throw new IllegalArgumentException("Unknown value type " + node.getClass().getSimpleName());
+    }
+}
index cf4ec56..b096a44 100755 (executable)
@@ -16,7 +16,6 @@ import java.io.DataOutput;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.StringWriter;
-import java.nio.charset.StandardCharsets;
 import java.util.Collection;
 import java.util.Map.Entry;
 import java.util.Set;
@@ -25,6 +24,7 @@ import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.TransformerFactoryConfigurationError;
 import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.stream.StreamResult;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
@@ -317,7 +317,7 @@ abstract class AbstractNormalizedNodeDataOutput implements NormalizedNodeDataOut
         writeQName(arg.getNodeType());
     }
 
-    private void writeObjSet(final Set<?> set) throws IOException {
+    final void writeObjSet(final Set<?> set) throws IOException {
         output.writeInt(set.size());
         for (Object o : set) {
             checkArgument(o instanceof String, "Expected value type to be String but was %s (%s)", o.getClass(), o);
@@ -343,7 +343,7 @@ abstract class AbstractNormalizedNodeDataOutput implements NormalizedNodeDataOut
         writeYangInstanceIdentifierInternal(identifier);
     }
 
-    private void writeYangInstanceIdentifierInternal(final YangInstanceIdentifier identifier) throws IOException {
+    final void writeYangInstanceIdentifierInternal(final YangInstanceIdentifier identifier) throws IOException {
         Collection<PathArgument> pathArguments = identifier.getPathArguments();
         output.writeInt(pathArguments.size());
 
@@ -423,52 +423,9 @@ abstract class AbstractNormalizedNodeDataOutput implements NormalizedNodeDataOut
         }
     }
 
-    private void writeObject(final Object value) throws IOException {
-
-        byte type = ValueTypes.getSerializableType(value);
-        // Write object type first
-        output.writeByte(type);
+    abstract void writeObject(@NonNull DataOutput output, @NonNull Object value) throws IOException;
 
-        switch (type) {
-            case ValueTypes.BOOL_TYPE:
-                output.writeBoolean((Boolean) value);
-                break;
-            case ValueTypes.QNAME_TYPE:
-                writeQName((QName) value);
-                break;
-            case ValueTypes.INT_TYPE:
-                output.writeInt((Integer) value);
-                break;
-            case ValueTypes.BYTE_TYPE:
-                output.writeByte((Byte) value);
-                break;
-            case ValueTypes.LONG_TYPE:
-                output.writeLong((Long) value);
-                break;
-            case ValueTypes.SHORT_TYPE:
-                output.writeShort((Short) value);
-                break;
-            case ValueTypes.BITS_TYPE:
-                writeObjSet((Set<?>) value);
-                break;
-            case ValueTypes.BINARY_TYPE:
-                byte[] bytes = (byte[]) value;
-                output.writeInt(bytes.length);
-                output.write(bytes);
-                break;
-            case ValueTypes.YANG_IDENTIFIER_TYPE:
-                writeYangInstanceIdentifierInternal((YangInstanceIdentifier) value);
-                break;
-            case ValueTypes.EMPTY_TYPE:
-                break;
-            case ValueTypes.STRING_BYTES_TYPE:
-                final byte[] valueBytes = value.toString().getBytes(StandardCharsets.UTF_8);
-                output.writeInt(valueBytes.length);
-                output.write(valueBytes);
-                break;
-            default:
-                output.writeUTF(value.toString());
-                break;
-        }
+    private void writeObject(final Object value) throws IOException {
+        writeObject(output, value);
     }
 }
index 493d35c..3f7fb3b 100644 (file)
@@ -9,11 +9,8 @@ package org.opendaylight.controller.cluster.datastore.node.utils.stream;
 
 import java.io.DataOutput;
 import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
-import org.opendaylight.yangtools.yang.common.Revision;
 
 /**
  * NormalizedNodeOutputStreamWriter will be used by distributed datastore to send normalized node in
@@ -29,9 +26,7 @@ import org.opendaylight.yangtools.yang.common.Revision;
  * <p>Based on the each node, the node type is also written to the stream, that helps in reconstructing the object,
  * while reading.
  */
-class LithiumNormalizedNodeOutputStreamWriter extends AbstractNormalizedNodeDataOutput {
-    private final Map<String, Integer> stringCodeMap = new HashMap<>();
-
+final class LithiumNormalizedNodeOutputStreamWriter extends AbstractLithiumDataOutput {
     LithiumNormalizedNodeOutputStreamWriter(final DataOutput output) {
         super(output);
     }
@@ -43,29 +38,11 @@ class LithiumNormalizedNodeOutputStreamWriter extends AbstractNormalizedNodeData
 
     @Override
     public void writeQName(final QName qname) throws IOException {
-        writeString(qname.getLocalName());
-        writeModule(qname.getModule());
-    }
-
-    void writeModule(final QNameModule module) throws IOException {
-        writeString(module.getNamespace().toString());
-        writeString(module.getRevision().map(Revision::toString).orElse(null));
+        defaultWriteQName(qname);
     }
 
     @Override
-    protected final void writeString(final String string) throws IOException {
-        if (string != null) {
-            final Integer value = stringCodeMap.get(string);
-            if (value == null) {
-                stringCodeMap.put(string, stringCodeMap.size());
-                writeByte(TokenTypes.IS_STRING_VALUE);
-                writeUTF(string);
-            } else {
-                writeByte(TokenTypes.IS_CODE_VALUE);
-                writeInt(value);
-            }
-        } else {
-            writeByte(TokenTypes.IS_NULL_VALUE);
-        }
+    void writeModule(final QNameModule module) throws IOException {
+        defaultWriteModule(module);
     }
 }
index e25aab5..e38c34a 100644 (file)
@@ -29,7 +29,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.Augmentat
  * <p>Based on the each node, the node type is also written to the stream, that helps in reconstructing the object,
  * while reading.
  */
-class NeonSR2NormalizedNodeOutputStreamWriter extends LithiumNormalizedNodeOutputStreamWriter {
+class NeonSR2NormalizedNodeOutputStreamWriter extends AbstractLithiumDataOutput {
     private final Map<AugmentationIdentifier, Integer> aidCodeMap = new HashMap<>();
     private final Map<QNameModule, Integer> moduleCodeMap = new HashMap<>();
     private final Map<QName, Integer> qnameCodeMap = new HashMap<>();
@@ -50,7 +50,7 @@ class NeonSR2NormalizedNodeOutputStreamWriter extends LithiumNormalizedNodeOutpu
             // Fresh QName, remember it and emit as three strings
             qnameCodeMap.put(qname, qnameCodeMap.size());
             writeByte(TokenTypes.IS_QNAME_VALUE);
-            super.writeQName(qname);
+            defaultWriteQName(qname);
         } else {
             // We have already seen this QName: write its code
             writeByte(TokenTypes.IS_QNAME_CODE);
@@ -80,7 +80,7 @@ class NeonSR2NormalizedNodeOutputStreamWriter extends LithiumNormalizedNodeOutpu
             // Fresh QNameModule, remember it and emit as three strings
             moduleCodeMap.put(module, moduleCodeMap.size());
             writeByte(TokenTypes.IS_MODULE_VALUE);
-            super.writeModule(module);
+            defaultWriteModule(module);
         } else {
             // We have already seen this QNameModule: write its code
             writeByte(TokenTypes.IS_MODULE_CODE);
index 0fd9590..8eac59a 100644 (file)
@@ -7,16 +7,6 @@
  */
 package org.opendaylight.controller.cluster.datastore.node.utils.stream;
 
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.collect.ImmutableMap;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.util.Set;
-import org.opendaylight.yangtools.yang.common.Empty;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-
 final class ValueTypes {
     // The String length threshold beyond which a String should be encoded as bytes
     static final int STRING_BYTES_LENGTH_THRESHOLD = Short.MAX_VALUE / 4;
@@ -40,41 +30,7 @@ final class ValueTypes {
     static final byte STRING_BYTES_TYPE = 14;
     static final byte EMPTY_TYPE = 15;
 
-    private static final ImmutableMap<Class<?>, Byte> TYPES = ImmutableMap.<Class<?>, Byte>builder()
-            .put(String.class, STRING_TYPE)
-            .put(Byte.class, BYTE_TYPE)
-            .put(Integer.class, INT_TYPE)
-            .put(Long.class, LONG_TYPE)
-            .put(Boolean.class, BOOL_TYPE)
-            .put(QName.class, QNAME_TYPE)
-            .put(Short.class, SHORT_TYPE)
-            .put(BigInteger.class, BIG_INTEGER_TYPE)
-            .put(BigDecimal.class, BIG_DECIMAL_TYPE)
-            .put(byte[].class, BINARY_TYPE)
-            .put(Empty.class, EMPTY_TYPE)
-            .build();
-
     private ValueTypes() {
         throw new UnsupportedOperationException("Utility class");
     }
-
-    static byte getSerializableType(final Object node) {
-        final Byte type = TYPES.get(requireNonNull(node).getClass());
-        if (type != null) {
-            if (type == STRING_TYPE && ((String) node).length() >= STRING_BYTES_LENGTH_THRESHOLD) {
-                return STRING_BYTES_TYPE;
-            }
-            return type;
-        }
-
-        if (node instanceof Set) {
-            return BITS_TYPE;
-        }
-
-        if (node instanceof YangInstanceIdentifier) {
-            return YANG_IDENTIFIER_TYPE;
-        }
-
-        throw new IllegalArgumentException("Unknown value type " + node.getClass().getSimpleName());
-    }
 }
@@ -5,22 +5,21 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-
 package org.opendaylight.controller.cluster.datastore.node.utils.stream;
 
 import static org.junit.Assert.assertEquals;
 
 import org.junit.Test;
 
-public class ValueTypesTest {
+public class LithiumWriteObjectMappingTest {
     @Test
     public void testStringType() {
-        assertEquals(ValueTypes.STRING_TYPE, ValueTypes.getSerializableType("foobar"));
+        assertEquals(ValueTypes.STRING_TYPE, AbstractLithiumDataOutput.getSerializableType("foobar"));
         final String largeString = largeString(ValueTypes.STRING_BYTES_LENGTH_THRESHOLD);
-        assertEquals(ValueTypes.STRING_BYTES_TYPE, ValueTypes.getSerializableType(largeString));
+        assertEquals(ValueTypes.STRING_BYTES_TYPE, AbstractLithiumDataOutput.getSerializableType(largeString));
     }
 
-    private static String largeString(int minSize) {
+    private static String largeString(final int minSize) {
         final int pow = (int) (Math.log(minSize * 2) / Math.log(2));
         StringBuilder sb = new StringBuilder("X");
         for (int i = 0; i < pow; i++) {

©2013 OpenDaylight, A Linux Foundation Collaborative Project. All Rights Reserved.
OpenDaylight is a registered trademark of The OpenDaylight Project, Inc.
Linux Foundation and OpenDaylight are registered trademarks of the Linux Foundation.
Linux is a registered trademark of Linus Torvalds.