Disconnect {Lithium,NeonSR2} implementations 69/84669/2
authorRobert Varga <robert.varga@pantheon.tech>
Sat, 7 Sep 2019 07:54:32 +0000 (09:54 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Tue, 24 Sep 2019 05:11:25 +0000 (07:11 +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>
(cherry picked from commit ba411b9ab99dc4420f2ba180a91a4e9a3af1d5d8)
(cherry picked from commit 57505019083cd85e296d10b1a4dcf82c3f42f1d2)

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 0506fd2fcf3655196060f5b0ea2821cff8de461c..f4138488b522d24a2594e8f33de1c001b6878e11 100755 (executable)
@@ -13,7 +13,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;
 import java.util.Set;
@@ -22,6 +21,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;
@@ -338,7 +338,7 @@ abstract class AbstractNormalizedNodeDataOutput implements NormalizedNodeDataOut
         writeQName(qname);
     }
 
-    private void writeObjSet(final Set<?> set) throws IOException {
+    final void writeObjSet(final Set<?> set) throws IOException {
         output.writeInt(set.size());
         for (Object o : set) {
             Preconditions.checkArgument(o instanceof String, "Expected value type to be String but was %s (%s)",
@@ -366,7 +366,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());
 
@@ -447,52 +447,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 a0aa813f64dd90cc0cceea7608f1c9aae5e950e8..ccc5e96249efb183a35f24004d05cfdeda3b6de9 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,43 +26,23 @@ 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<>();
-
+class LithiumNormalizedNodeOutputStreamWriter extends AbstractLithiumDataOutput {
     LithiumNormalizedNodeOutputStreamWriter(final DataOutput output) {
         super(output);
     }
 
     @Override
-    protected short streamVersion() {
+    protected final short streamVersion() {
         return TokenTypes.LITHIUM_VERSION;
     }
 
     @Override
-    protected 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));
+    protected final void writeQName(final QName qname) throws IOException {
+        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);
-        }
+    final void writeModule(final QNameModule module) throws IOException {
+        defaultWriteModule(module);
     }
 }
index b3663e707eb6cd34c52f537160bcc77d4cc9c84a..5570fc839b503bbf61b45dc7869b83c2a8f48050 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 {
+final 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<>();
@@ -44,13 +44,13 @@ class NeonSR2NormalizedNodeOutputStreamWriter extends LithiumNormalizedNodeOutpu
     }
 
     @Override
-    protected final void writeQName(final QName qname) throws IOException {
+    protected void writeQName(final QName qname) throws IOException {
         final Integer value = qnameCodeMap.get(qname);
         if (value == null) {
             // 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 0fd959039db63d899db169616e7539e89ee0fc04..8eac59a96b51ec8b1ab2fba25cd3f0f1ffbdc4d7 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++) {