X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fsal-clustering-commons%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fcluster%2Fdatastore%2Fnode%2Futils%2Fstream%2FAbstractLithiumDataOutput.java;h=47082698f98b622dac1f1496f170711194b0ca12;hp=73a4bf78fe536200b759e97ee7c5a8e6fa1c7916;hb=ce5a523664a789cefb9950ec9368abc35f849d7c;hpb=ba411b9ab99dc4420f2ba180a91a4e9a3af1d5d8 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 index 73a4bf78fe..47082698f9 100644 --- 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 @@ -7,23 +7,43 @@ */ package org.opendaylight.controller.cluster.datastore.node.utils.stream; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Verify.verifyNotNull; import static java.util.Objects.requireNonNull; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.DataOutput; import java.io.IOException; +import java.io.StringWriter; import java.math.BigDecimal; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; import java.util.Set; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import org.eclipse.jdt.annotation.NonNull; 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; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * "original" type mapping. Baseline is Lithium but it really was introduced in Oxygen, where {@code type empty} was @@ -35,6 +55,8 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; * knowledge. */ abstract class AbstractLithiumDataOutput extends AbstractNormalizedNodeDataOutput { + private static final Logger LOG = LoggerFactory.getLogger(AbstractLithiumDataOutput.class); + private static final TransformerFactory TF = TransformerFactory.newInstance(); private static final ImmutableMap, Byte> KNOWN_TYPES = ImmutableMap., Byte>builder() .put(String.class, ValueTypes.STRING_TYPE) .put(Byte.class, ValueTypes.BYTE_TYPE) @@ -49,45 +71,271 @@ abstract class AbstractLithiumDataOutput extends AbstractNormalizedNodeDataOutpu .put(Empty.class, ValueTypes.EMPTY_TYPE) .build(); - private final Map stringCodeMap = new HashMap<>(); + private QName lastLeafSetQName; + private boolean inSimple; + AbstractLithiumDataOutput(final DataOutput output) { super(output); } @Override - final void writeObject(final DataOutput rawOuput, final Object value) throws IOException { + public final void startLeafNode(final NodeIdentifier name) throws IOException { + LOG.trace("Starting a new leaf node"); + startNode(name, LithiumNode.LEAF_NODE); + inSimple = true; + } + + @Override + public final void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException { + LOG.trace("Starting a new leaf set"); + commonStartLeafSet(name, LithiumNode.LEAF_SET); + } + + @Override + public final void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException { + LOG.trace("Starting a new ordered leaf set"); + commonStartLeafSet(name, LithiumNode.ORDERED_LEAF_SET); + } + + private void commonStartLeafSet(final NodeIdentifier name, final byte nodeType) throws IOException { + startNode(name, nodeType); + lastLeafSetQName = name.getNodeType(); + } + + @Override + public final void startLeafSetEntryNode(final NodeWithValue name) throws IOException { + LOG.trace("Starting a new leaf set entry node"); + + output.writeByte(LithiumNode.LEAF_SET_ENTRY_NODE); + + // lastLeafSetQName is set if the parent LeafSetNode was previously written. Otherwise this is a + // stand alone LeafSetEntryNode so write out it's name here. + if (lastLeafSetQName == null) { + writeQNameInternal(name.getNodeType()); + } + inSimple = true; + } + + @Override + public final void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException { + LOG.trace("Starting a new container node"); + startNode(name, LithiumNode.CONTAINER_NODE); + } + + @Override + public final void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) + throws IOException { + LOG.trace("Starting a new yang modeled anyXml node"); + startNode(name, LithiumNode.YANG_MODELED_ANY_XML_NODE); + } + + @Override + public final void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException { + LOG.trace("Starting a new unkeyed list"); + startNode(name, LithiumNode.UNKEYED_LIST); + } + + @Override + public final void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException { + LOG.trace("Starting a new unkeyed list item"); + startNode(name, LithiumNode.UNKEYED_LIST_ITEM); + } + + @Override + public final void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException { + LOG.trace("Starting a new map node"); + startNode(name, LithiumNode.MAP_NODE); + } + + @Override + public final void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint) + throws IOException { + LOG.trace("Starting a new map entry node"); + startNode(identifier, LithiumNode.MAP_ENTRY_NODE); + writeKeyValueMap(identifier.entrySet()); + } + + @Override + public final void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException { + LOG.trace("Starting a new ordered map node"); + startNode(name, LithiumNode.ORDERED_MAP_NODE); + } + + @Override + public final void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IOException { + LOG.trace("Starting a new choice node"); + startNode(name, LithiumNode.CHOICE_NODE); + } + + @Override + public final void startAugmentationNode(final AugmentationIdentifier identifier) throws IOException { + requireNonNull(identifier, "Node identifier should not be null"); + LOG.trace("Starting a new augmentation node"); + + output.writeByte(LithiumNode.AUGMENTATION_NODE); + writeAugmentationIdentifier(identifier); + } + + @Override + public final void startAnyxmlNode(final NodeIdentifier name) throws IOException { + LOG.trace("Starting any xml node"); + startNode(name, LithiumNode.ANY_XML_NODE); + inSimple = true; + } + + @Override + public final void scalarValue(final Object value) throws IOException { + writeObject(value); + } + + @Override + public final void domSourceValue(final DOMSource value) throws IOException { + final StringWriter writer = new StringWriter(); + try { + TF.newTransformer().transform(value, new StreamResult(writer)); + } catch (TransformerException e) { + throw new IOException("Error writing anyXml", e); + } + writeObject(writer.toString()); + } + + @Override + public final void endNode() throws IOException { + LOG.trace("Ending the node"); + if (!inSimple) { + lastLeafSetQName = null; + output.writeByte(LithiumNode.END_NODE); + } + inSimple = false; + } + + @Override + @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST", + justification = "The casts in the switch clauses are indirectly confirmed via the determination of 'type'.") + final void writePathArgumentInternal(final PathArgument pathArgument) throws IOException { + final byte type = LithiumPathArgument.getSerializablePathArgumentType(pathArgument); + output.writeByte(type); + + switch (type) { + case LithiumPathArgument.NODE_IDENTIFIER: + NodeIdentifier nodeIdentifier = (NodeIdentifier) pathArgument; + writeQNameInternal(nodeIdentifier.getNodeType()); + break; + case LithiumPathArgument.NODE_IDENTIFIER_WITH_PREDICATES: + NodeIdentifierWithPredicates nodeIdentifierWithPredicates = + (NodeIdentifierWithPredicates) pathArgument; + writeQNameInternal(nodeIdentifierWithPredicates.getNodeType()); + writeKeyValueMap(nodeIdentifierWithPredicates.entrySet()); + break; + case LithiumPathArgument.NODE_IDENTIFIER_WITH_VALUE: + NodeWithValue nodeWithValue = (NodeWithValue) pathArgument; + writeQNameInternal(nodeWithValue.getNodeType()); + writeObject(nodeWithValue.getValue()); + break; + case LithiumPathArgument.AUGMENTATION_IDENTIFIER: + // No Qname in augmentation identifier + writeAugmentationIdentifier((AugmentationIdentifier) pathArgument); + break; + default: + throw new IllegalStateException("Unknown node identifier type is found : " + + pathArgument.getClass().toString()); + } + } + + @Override + final void writeYangInstanceIdentifierInternal(final YangInstanceIdentifier identifier) throws IOException { + List pathArguments = identifier.getPathArguments(); + output.writeInt(pathArguments.size()); + + for (PathArgument pathArgument : pathArguments) { + writePathArgumentInternal(pathArgument); + } + } + + final void defaultWriteAugmentationIdentifier(final @NonNull AugmentationIdentifier aid) throws IOException { + final Set qnames = aid.getPossibleChildNames(); + // Write each child's qname separately, if list is empty send count as 0 + if (!qnames.isEmpty()) { + output.writeInt(qnames.size()); + for (QName qname : qnames) { + writeQNameInternal(qname); + } + } else { + LOG.debug("augmentation node does not have any child"); + output.writeInt(0); + } + } + + 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()); + final Optional revision = module.getRevision(); + if (revision.isPresent()) { + writeString(revision.get().toString()); + } else { + writeByte(LithiumTokens.IS_NULL_VALUE); + } + } + + abstract void writeModule(QNameModule module) throws IOException; + + abstract void writeAugmentationIdentifier(@NonNull AugmentationIdentifier aid) throws IOException; + + private void startNode(final PathArgument arg, final byte nodeType) throws IOException { + requireNonNull(arg, "Node identifier should not be null"); + checkState(!inSimple, "Attempted to start a child in a simple node"); + + // First write the type of node + output.writeByte(nodeType); + // Write Start Tag + writeQNameInternal(arg.getNodeType()); + } + + private 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); + writeString((String) o); + } + } + + private void writeObject(final Object value) throws IOException { byte type = getSerializableType(value); // Write object type first - rawOuput.writeByte(type); + output.writeByte(type); switch (type) { case ValueTypes.BOOL_TYPE: - rawOuput.writeBoolean((Boolean) value); + output.writeBoolean((Boolean) value); break; case ValueTypes.QNAME_TYPE: - writeQName((QName) value); + writeQNameInternal((QName) value); break; case ValueTypes.INT_TYPE: - rawOuput.writeInt((Integer) value); + output.writeInt((Integer) value); break; case ValueTypes.BYTE_TYPE: - rawOuput.writeByte((Byte) value); + output.writeByte((Byte) value); break; case ValueTypes.LONG_TYPE: - rawOuput.writeLong((Long) value); + output.writeLong((Long) value); break; case ValueTypes.SHORT_TYPE: - rawOuput.writeShort((Short) value); + output.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); + output.writeInt(bytes.length); + output.write(bytes); break; case ValueTypes.YANG_IDENTIFIER_TYPE: writeYangInstanceIdentifierInternal((YangInstanceIdentifier) value); @@ -96,43 +344,38 @@ abstract class AbstractLithiumDataOutput extends AbstractNormalizedNodeDataOutpu break; case ValueTypes.STRING_BYTES_TYPE: final byte[] valueBytes = value.toString().getBytes(StandardCharsets.UTF_8); - rawOuput.writeInt(valueBytes.length); - rawOuput.write(valueBytes); + output.writeInt(valueBytes.length); + output.write(valueBytes); break; default: - rawOuput.writeUTF(value.toString()); + output.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); + private void writeKeyValueMap(final Set> entrySet) throws IOException { + if (!entrySet.isEmpty()) { + output.writeInt(entrySet.size()); + for (Entry entry : entrySet) { + writeQNameInternal(entry.getKey()); + writeObject(entry.getValue()); } } else { - writeByte(TokenTypes.IS_NULL_VALUE); + output.writeInt(0); } } - abstract void writeModule(QNameModule module) throws IOException; + private void writeString(final @NonNull String string) throws IOException { + final Integer value = stringCodeMap.get(verifyNotNull(string)); + if (value == null) { + stringCodeMap.put(string, stringCodeMap.size()); + writeByte(LithiumTokens.IS_STRING_VALUE); + writeUTF(string); + } else { + writeByte(LithiumTokens.IS_CODE_VALUE); + writeInt(value); + } + } @VisibleForTesting static final byte getSerializableType(final Object node) {