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.ObjectInput;
16 import java.io.ObjectOutput;
17 import java.util.Optional;
18 import org.opendaylight.controller.cluster.datastore.node.utils.stream.SerializationUtils;
19 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
24 * Abstract base class for snapshots of the ShardDataTree.
26 * @author Robert Varga
29 public abstract class ShardDataTreeSnapshot {
30 private static final Logger LOG = LoggerFactory.getLogger(ShardDataTreeSnapshot.class);
32 ShardDataTreeSnapshot() {
33 // Hidden to prevent subclassing from outside of this package
37 public static ShardDataTreeSnapshot deserializePreCarbon(final byte[] bytes) throws IOException {
39 * Unfortunately versions prior to Boron did not include any way to evolve the snapshot format and contained
40 * only the raw data stored in the datastore. Furthermore utilities involved do not check if the array is
41 * completely consumed, which has a nasty side-effect when coupled with the fact that PayloadVersion writes
44 * Since our versions fit into a single byte, we end up writing the 0 as the first byte, which would be
45 * interpreted as 'not present' by the old snapshot format, which uses writeBoolean/readBoolean. A further
46 * complication is that readBoolean interprets any non-zero value as true, hence we cannot use a wild value
47 * to cause it to fail.
49 if (isLegacyStream(bytes)) {
50 return deserializeLegacy(bytes);
54 try (InputStream is = new ByteArrayInputStream(bytes)) {
55 try (DataInputStream dis = new DataInputStream(is)) {
56 final ShardDataTreeSnapshot ret = AbstractVersionedShardDataTreeSnapshot.deserializePreCarbon(dis);
58 // Make sure we consume all bytes, otherwise something went very wrong
59 final int bytesLeft = dis.available();
61 throw new IOException("Deserialization left " + bytesLeft + " in the buffer");
68 } catch (IOException e) {
69 LOG.debug("Failed to deserialize versioned stream, attempting pre-Lithium ProtoBuf", e);
70 return deserializeLegacy(bytes);
74 public static ShardDataTreeSnapshot deserialize(final ObjectInput in) throws IOException {
75 final ShardDataTreeSnapshot ret = AbstractVersionedShardDataTreeSnapshot.versionedDeserialize(in);
77 // Make sure we consume all bytes, otherwise something went very wrong
78 final int bytesLeft = in.available();
80 throw new IOException("Deserialization left " + bytesLeft + " in the buffer");
88 * Get the root data node contained in this snapshot.
90 * @return An optional root node.
92 public abstract Optional<NormalizedNode<?, ?>> getRootNode();
94 public abstract void serialize(ObjectOutput out) throws IOException;
97 private static boolean isLegacyStream(final byte[] bytes) {
98 if (bytes.length < 2) {
99 // Versioned streams have at least two bytes
104 * The stream could potentially be a versioned stream. Here we rely on the signature marker available
105 * in org.opendaylight.controller.cluster.datastore.node.utils.stream.TokenTypes.
107 * For an old stream to be this long, the first byte has to be non-zero and the second byte has to be 0xAB.
109 * For a versioned stream, that translates to at least version 427 -- giving us at least 421 further versions
110 * before this check breaks.
112 return bytes[0] != 0 && bytes[1] == (byte)0xAB;
116 private static ShardDataTreeSnapshot deserializeLegacy(final byte[] bytes) {
117 return new PreBoronShardDataTreeSnapshot(SerializationUtils.deserializeNormalizedNode(bytes));