From: Robert Varga Date: Sat, 7 Sep 2019 07:54:32 +0000 (+0200) Subject: Disconnect {Lithium,NeonSR2} implementations X-Git-Tag: release/magnesium~108 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=ba411b9ab99dc4420f2ba180a91a4e9a3af1d5d8 Disconnect {Lithium,NeonSR2} implementations 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 --- 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 index 0000000000..73a4bf78fe --- /dev/null +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/AbstractLithiumDataOutput.java @@ -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. + * + *

+ * {@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, Byte> KNOWN_TYPES = ImmutableMap., 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 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()); + } +} diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/AbstractNormalizedNodeDataOutput.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/AbstractNormalizedNodeDataOutput.java index cf4ec56ced..b096a445f3 100755 --- a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/AbstractNormalizedNodeDataOutput.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/AbstractNormalizedNodeDataOutput.java @@ -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 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); } } diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/LithiumNormalizedNodeOutputStreamWriter.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/LithiumNormalizedNodeOutputStreamWriter.java index 493d35c9fe..3f7fb3b290 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/LithiumNormalizedNodeOutputStreamWriter.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/LithiumNormalizedNodeOutputStreamWriter.java @@ -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; *

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 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); } } diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NeonSR2NormalizedNodeOutputStreamWriter.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NeonSR2NormalizedNodeOutputStreamWriter.java index e25aab5a72..e38c34aa96 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NeonSR2NormalizedNodeOutputStreamWriter.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NeonSR2NormalizedNodeOutputStreamWriter.java @@ -29,7 +29,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.Augmentat *

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 aidCodeMap = new HashMap<>(); private final Map moduleCodeMap = new HashMap<>(); private final Map 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); diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/ValueTypes.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/ValueTypes.java index 0fd959039d..8eac59a96b 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/ValueTypes.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/ValueTypes.java @@ -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, Byte> TYPES = ImmutableMap., 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()); - } } diff --git a/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/ValueTypesTest.java b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/LithiumWriteObjectMappingTest.java similarity index 72% rename from opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/ValueTypesTest.java rename to opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/LithiumWriteObjectMappingTest.java index 0395e94ee8..229ebaf5cd 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/ValueTypesTest.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/LithiumWriteObjectMappingTest.java @@ -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++) {