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.io.OutputStream;
16 import java.util.Optional;
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
36 public static ShardDataTreeSnapshot deserialize(final byte[] bytes) throws IOException {
38 * Unfortunately versions prior to Boron did not include any way to evolve the snapshot format and contained
39 * only the raw data stored in the datastore. Furthermore utilities involved do not check if the array is
40 * completely consumed, which has a nasty side-effect when coupled with the fact that PayloadVersion writes
43 * Since our versions fit into a single byte, we end up writing the 0 as the first byte, which would be
44 * interpreted as 'not present' by the old snapshot format, which uses writeBoolean/readBoolean. A further
45 * complication is that readBoolean interprets any non-zero value as true, hence we cannot use a wild value
46 * to cause it to fail.
48 if (isLegacyStream(bytes)) {
49 return deserializeLegacy(bytes);
53 try (InputStream is = new ByteArrayInputStream(bytes)) {
54 return deserialize(is);
56 } catch (IOException e) {
57 LOG.debug("Failed to deserialize versioned stream, attempting pre-Lithium ProtoBuf", e);
58 return deserializeLegacy(bytes);
62 public static ShardDataTreeSnapshot deserialize(final InputStream is) throws IOException {
63 try (DataInputStream dis = new DataInputStream(is)) {
64 final ShardDataTreeSnapshot ret = AbstractVersionedShardDataTreeSnapshot.deserialize(dis);
66 // Make sure we consume all bytes, otherwise something went very wrong
67 final int bytesLeft = dis.available();
69 throw new IOException("Deserialization left " + bytesLeft + " in the buffer");
78 * Get the root data node contained in this snapshot.
80 * @return An optional root node.
82 public abstract Optional<NormalizedNode<?, ?>> getRootNode();
84 public abstract void serialize(OutputStream os) 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));