2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.controller.cluster.datastore.persisted;
10 import com.google.common.annotations.Beta;
11 import java.io.ByteArrayInputStream;
12 import java.io.DataInputStream;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.util.Optional;
16 import javax.annotation.Nonnull;
17 import org.opendaylight.controller.cluster.datastore.node.utils.stream.SerializationUtils;
18 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
23 * Abstract base class for snapshots of the ShardDataTree.
25 * @author Robert Varga
28 public abstract class ShardDataTreeSnapshot {
29 private static final Logger LOG = LoggerFactory.getLogger(ShardDataTreeSnapshot.class);
31 ShardDataTreeSnapshot() {
32 // Hidden to prevent subclassing from outside of this package
35 public static ShardDataTreeSnapshot deserialize(final byte[] bytes) throws IOException {
37 * Unfortunately versions prior to Boron did not include any way to evolve the snapshot format and contained
38 * only the raw data stored in the datastore. Furthermore utilities involved do not check if the array is
39 * completely consumed, which has a nasty side-effect when coupled with the fact that PayloadVersion writes
42 * Since our versions fit into a single byte, we end up writing the 0 as the first byte, which would be
43 * interpreted as 'not present' by the old snapshot format, which uses writeBoolean/readBoolean. A further
44 * complication is that readBoolean interprets any non-zero value as true, hence we cannot use a wild value
45 * to cause it to fail.
47 if (isLegacyStream(bytes)) {
48 return deserializeLegacy(bytes);
52 try (final InputStream is = new ByteArrayInputStream(bytes)) {
53 try (final DataInputStream dis = new DataInputStream(is)) {
54 final ShardDataTreeSnapshot ret = AbstractVersionedShardDataTreeSnapshot.deserialize(dis);
56 // Make sure we consume all bytes, otherwise something went very wrong
57 final int bytesLeft = dis.available();
59 throw new IOException("Deserialization left " + bytesLeft + " in the buffer");
66 } catch (IOException e) {
67 LOG.debug("Failed to deserialize versioned stream, attempting pre-Lithium ProtoBuf", e);
68 return deserializeLegacy(bytes);
73 * Get the root data node contained in this snapshot.
75 * @return An optional root node.
77 public abstract Optional<NormalizedNode<?, ?>> getRootNode();
80 * Serialize this snapshot into a byte array for persistence.
82 * @return Serialized snapshot
83 * @throws IOException when a serialization problem occurs
85 public abstract @Nonnull byte[] serialize() throws IOException;
87 private static boolean isLegacyStream(final byte[] bytes) {
88 if (bytes.length < 2) {
89 // Versioned streams have at least two bytes
94 * The stream could potentially be a versioned stream. Here we rely on the signature marker available
95 * in org.opendaylight.controller.cluster.datastore.node.utils.stream.TokenTypes.
97 * For an old stream to be this long, the first byte has to be non-zero and the second byte has to be 0xAB.
99 * For a versioned stream, that translates to at least version 427 -- giving us at least 421 further versions
100 * before this check breaks.
102 return bytes[0] != 0 && bytes[1] == (byte)0xAB;
106 private static ShardDataTreeSnapshot deserializeLegacy(final byte[] bytes) {
107 return new PreBoronShardDataTreeSnapshot(SerializationUtils.deserializeNormalizedNode(bytes));