Define efficient serialization proxies 22/103422/25
authorRobert Varga <robert.varga@pantheon.tech>
Sat, 26 Nov 2022 17:35:35 +0000 (18:35 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Tue, 29 Nov 2022 18:54:25 +0000 (19:54 +0100)
Superclasses have footprint in serialization protocol, as their
descriptors get written out.

Capture Externalizable logic into SerialForm, which implements
Externalizable -- the protocol to be easily implemented without a
superclass. This entails updating the API footprint of AbstractProxy
to reuse its methods.

Then introduce shorthand proxy classes which behave exactly like their
AbstractProxy counterparts, but without the AbstractProxy baggage.

This enables reading implied serialization format, if we encounter it,
providing forward compatibility with the format.

JIRA: CONTROLLER-2056
Change-Id: I4da54ca57849c2bfdaaf76c22e7903abcfa58be9
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
20 files changed:
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/AT.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/AbstractIdentifiablePayload.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/CH.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/CT.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/CommitTransactionPayload.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/DH.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/DS.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/DSS.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/DT.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/DatastoreSnapshot.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/FM.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/MS.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/PH.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/PT.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/SM.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/SS.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/ST.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/ShardManagerSnapshot.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/ShardSnapshotState.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/SkipTransactionsPayload.java

diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/AT.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/AT.java
new file mode 100644 (file)
index 0000000..ddb9dc9
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.persisted;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import org.opendaylight.controller.cluster.access.concepts.TransactionIdentifier;
+import org.opendaylight.controller.cluster.datastore.persisted.AbstractIdentifiablePayload.SerialForm;
+
+/**
+ * Serialization proxy for {@link AbortTransactionPayload}.
+ */
+final class AT implements SerialForm {
+    private static final long serialVersionUID = 1L;
+
+    private TransactionIdentifier identifier;
+    private byte[] bytes;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public AT() {
+        // For Externalizable
+    }
+
+    AT(final byte[] bytes) {
+        this.bytes = requireNonNull(bytes);
+    }
+
+    @Override
+    public byte[] bytes() {
+        return bytes;
+    }
+
+    @Override
+    public void readExternal(final byte[] newBytes) throws IOException {
+        bytes = requireNonNull(newBytes);
+        identifier = verifyNotNull(TransactionIdentifier.readFrom(ByteStreams.newDataInput(newBytes)));
+    }
+
+    @Override
+    public Object readResolve() {
+        return new AbortTransactionPayload(identifier, bytes);
+    }
+}
index f07d4dbe903d10244f9a0ec4e764b82864e5fb7e..9bc5a319da2da9bde6385398f2f74329985ba723 100644 (file)
@@ -32,7 +32,62 @@ import org.opendaylight.yangtools.concepts.Identifier;
  */
 public abstract class AbstractIdentifiablePayload<T extends Identifier> extends IdentifiablePayload<T>
         implements Serializable {
-    protected abstract static class AbstractProxy<T extends Identifier> implements Externalizable {
+    /**
+     * An {@link Externalizable} with default implementations we expect our implementations to comply with. On-wire
+     * serialization format is defined by {@link #bytes()}.
+     */
+    protected interface SerialForm extends Externalizable {
+        /**
+         * Return the serial form of this object contents, corresponding to
+         * {@link AbstractIdentifiablePayload#serialized}.
+         *
+         * @return Serialized form
+         */
+        byte[] bytes();
+
+        /**
+         * Resolve this proxy to an actual {@link AbstractIdentifiablePayload}.
+         *
+         * @return A payload.
+         */
+        Object readResolve();
+
+        /**
+         * Restore state from specified serialized form.
+         *
+         * @param newBytes Serialized form, as returned by {@link #bytes()}
+         * @throws IOException when a deserialization problem occurs
+         */
+        void readExternal(byte[] newBytes) throws IOException;
+
+        /**
+         * {@inheritDoc}
+         *
+         * <p>
+         * The default implementation is canonical and should never be overridden.
+         */
+        @Override
+        default void readExternal(final ObjectInput in) throws IOException {
+            final var bytes = new byte[in.readInt()];
+            in.readFully(bytes);
+            readExternal(bytes);
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * <p>
+         * The default implementation is canonical and should never be overridden.
+         */
+        @Override
+        default void writeExternal(final ObjectOutput out) throws IOException {
+            final var bytes = bytes();
+            out.writeInt(bytes.length);
+            out.write(bytes);
+        }
+    }
+
+    protected abstract static class AbstractProxy<T extends Identifier> implements SerialForm {
         private static final long serialVersionUID = 1L;
 
         private byte[] serialized;
@@ -47,20 +102,18 @@ public abstract class AbstractIdentifiablePayload<T extends Identifier> extends
         }
 
         @Override
-        public final void writeExternal(final ObjectOutput out) throws IOException {
-            out.writeInt(serialized.length);
-            out.write(serialized);
+        public final byte[] bytes() {
+            return serialized;
         }
 
         @Override
-        public final void readExternal(final ObjectInput in) throws IOException {
-            final int length = in.readInt();
-            serialized = new byte[length];
-            in.readFully(serialized);
+        public final void readExternal(final byte[] bytes) throws IOException {
+            serialized = requireNonNull(bytes);
             identifier = verifyNotNull(readIdentifier(ByteStreams.newDataInput(serialized)));
         }
 
-        protected final Object readResolve() {
+        @Override
+        public final Object readResolve() {
             return verifyNotNull(createObject(identifier, serialized));
         }
 
@@ -90,6 +143,10 @@ public abstract class AbstractIdentifiablePayload<T extends Identifier> extends
         return serialized.length;
     }
 
+    protected final byte @NonNull [] serialized() {
+        return serialized;
+    }
+
     @Override
     public final int serializedSize() {
         // TODO: this is not entirely accurate, as the serialization stream has additional overheads:
@@ -109,7 +166,6 @@ public abstract class AbstractIdentifiablePayload<T extends Identifier> extends
         return verifyNotNull(externalizableProxy(serialized));
     }
 
-    @SuppressWarnings("checkstyle:hiddenField")
     protected abstract @NonNull AbstractProxy<T> externalizableProxy(byte @NonNull[] serialized);
 
     protected abstract int externalizableProxySize();
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/CH.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/CH.java
new file mode 100644 (file)
index 0000000..b4e827b
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.persisted;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import org.opendaylight.controller.cluster.access.concepts.LocalHistoryIdentifier;
+import org.opendaylight.controller.cluster.datastore.persisted.AbstractIdentifiablePayload.SerialForm;
+
+/**
+ * Serialization proxy for {@link CreateLocalHistoryPayload}.
+ */
+final class CH implements SerialForm {
+    private static final long serialVersionUID = 1L;
+
+    private LocalHistoryIdentifier identifier;
+    private byte[] bytes;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public CH() {
+        // For Externalizable
+    }
+
+    CH(final byte[] bytes) {
+        this.bytes = requireNonNull(bytes);
+    }
+
+    @Override
+    public byte[] bytes() {
+        return bytes;
+    }
+
+    @Override
+    public void readExternal(final byte[] newBytes) throws IOException {
+        bytes = requireNonNull(newBytes);
+        identifier = verifyNotNull(LocalHistoryIdentifier.readFrom(ByteStreams.newDataInput(newBytes)));
+    }
+
+    @Override
+    public Object readResolve() {
+        return new CreateLocalHistoryPayload(identifier, bytes);
+    }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/CT.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/CT.java
new file mode 100644 (file)
index 0000000..98c2562
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.persisted;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.StreamCorruptedException;
+import org.opendaylight.controller.cluster.datastore.persisted.CommitTransactionPayload.Chunked;
+import org.opendaylight.controller.cluster.datastore.persisted.CommitTransactionPayload.Simple;
+import org.opendaylight.controller.cluster.io.ChunkedByteArray;
+
+/**
+ * Serialization proxy for {@link CommitTransactionPayload}.
+ */
+final class CT implements Externalizable {
+    private static final long serialVersionUID = 1L;
+
+    private CommitTransactionPayload payload;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public CT() {
+        // For Externalizable
+    }
+
+    CT(final CommitTransactionPayload payload) {
+        this.payload = requireNonNull(payload);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        out.writeInt(payload.size());
+        payload.writeBytes(out);
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException {
+        final int length = in.readInt();
+        if (length < 0) {
+            throw new StreamCorruptedException("Invalid payload length " + length);
+        } else if (length < CommitTransactionPayload.MAX_ARRAY_SIZE) {
+            final byte[] serialized = new byte[length];
+            in.readFully(serialized);
+            payload = new Simple(serialized);
+        } else {
+            payload = new Chunked(ChunkedByteArray.readFrom(in, length, CommitTransactionPayload.MAX_ARRAY_SIZE));
+        }
+    }
+
+    private Object readResolve() {
+        return verifyNotNull(payload);
+    }
+}
index 128d46dc9113751c5891943cccf00ec7bca69364..cb261a2b19392da2373357383957a7f2cad4b4b5 100644 (file)
@@ -53,7 +53,7 @@ public abstract sealed class CommitTransactionPayload extends IdentifiablePayloa
     private static final Logger LOG = LoggerFactory.getLogger(CommitTransactionPayload.class);
     private static final long serialVersionUID = 1L;
 
-    private static final int MAX_ARRAY_SIZE = ceilingPowerOfTwo(Integer.getInteger(
+    static final int MAX_ARRAY_SIZE = ceilingPowerOfTwo(Integer.getInteger(
         "org.opendaylight.controller.cluster.datastore.persisted.max-array-size", 256 * 1024));
 
     private volatile Entry<TransactionIdentifier, DataTreeCandidateWithVersion> candidate = null;
@@ -153,7 +153,7 @@ public abstract sealed class CommitTransactionPayload extends IdentifiablePayloa
         return new Proxy(this);
     }
 
-    private static final class Simple extends CommitTransactionPayload {
+    static final class Simple extends CommitTransactionPayload {
         private static final long serialVersionUID = 1L;
 
         private final byte[] serialized;
@@ -178,7 +178,7 @@ public abstract sealed class CommitTransactionPayload extends IdentifiablePayloa
         }
     }
 
-    private static final class Chunked extends CommitTransactionPayload {
+    static final class Chunked extends CommitTransactionPayload {
         private static final long serialVersionUID = 1L;
 
         @SuppressFBWarnings(value = "SE_BAD_FIELD", justification = "Handled via serialization proxy")
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/DH.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/DH.java
new file mode 100644 (file)
index 0000000..a9c6c63
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.persisted;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import org.opendaylight.controller.cluster.access.concepts.LocalHistoryIdentifier;
+import org.opendaylight.controller.cluster.datastore.persisted.AbstractIdentifiablePayload.SerialForm;
+
+/**
+ * Serialization proxy for {@link CloseLocalHistoryPayload}.
+ */
+final class DH implements SerialForm {
+    private static final long serialVersionUID = 1L;
+
+    private LocalHistoryIdentifier identifier;
+    private byte[] bytes;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public DH() {
+        // For Externalizable
+    }
+
+    DH(final byte[] bytes) {
+        this.bytes = requireNonNull(bytes);
+    }
+
+    @Override
+    public byte[] bytes() {
+        return bytes;
+    }
+
+    @Override
+    public void readExternal(final byte[] newBytes) throws IOException {
+        bytes = requireNonNull(newBytes);
+        identifier = verifyNotNull(LocalHistoryIdentifier.readFrom(ByteStreams.newDataInput(newBytes)));
+    }
+
+    @Override
+    public Object readResolve() {
+        return new CloseLocalHistoryPayload(identifier, bytes);
+    }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/DS.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/DS.java
new file mode 100644 (file)
index 0000000..5dc3654
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.persisted;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Serialization proxy for {@link DatastoreSnapshot}.
+ */
+final class DS implements DatastoreSnapshot.SerialForm {
+    private static final long serialVersionUID = 1L;
+
+    private DatastoreSnapshot datastoreSnapshot;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public DS() {
+        // For Externalizable
+    }
+
+    DS(final DatastoreSnapshot datastoreSnapshot) {
+        this.datastoreSnapshot = requireNonNull(datastoreSnapshot);
+    }
+
+    @Override
+    public DatastoreSnapshot datastoreSnapshot() {
+        return datastoreSnapshot;
+    }
+
+    @Override
+    public void resolveTo(final DatastoreSnapshot newDatastoreSnapshot) {
+        datastoreSnapshot = requireNonNull(newDatastoreSnapshot);
+    }
+
+    @Override
+    public Object readResolve() {
+        return verifyNotNull(datastoreSnapshot);
+    }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/DSS.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/DSS.java
new file mode 100644 (file)
index 0000000..074706b
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.persisted;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import org.opendaylight.controller.cluster.datastore.persisted.DatastoreSnapshot.ShardSnapshot;
+import org.opendaylight.controller.cluster.raft.persisted.Snapshot;
+
+/**
+ * Serialization proxy for {@link ShardDataTreeSnapshot}.
+ */
+final class DSS implements ShardSnapshot.SerialForm {
+    private static final long serialVersionUID = 1L;
+
+    private ShardSnapshot shardSnapshot;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public DSS() {
+        // For Externalizable
+    }
+
+    DSS(final ShardSnapshot shardSnapshot) {
+        this.shardSnapshot = requireNonNull(shardSnapshot);
+    }
+
+    @Override
+    public ShardSnapshot shardSnapshot() {
+        return shardSnapshot;
+    }
+
+    @Override
+    public void resolveTo(final String name, final Snapshot snapshot) {
+        shardSnapshot = new ShardSnapshot(name, snapshot);
+    }
+
+    @Override
+    public Object readResolve() {
+        return verifyNotNull(shardSnapshot);
+    }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/DT.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/DT.java
new file mode 100644 (file)
index 0000000..8a619f6
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.persisted;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import org.opendaylight.controller.cluster.access.concepts.ClientIdentifier;
+import org.opendaylight.controller.cluster.datastore.persisted.AbstractIdentifiablePayload.SerialForm;
+
+/**
+ * Serialization proxy for {@link DisableTrackingPayload}.
+ */
+final class DT implements SerialForm {
+    private static final long serialVersionUID = 1L;
+
+    private ClientIdentifier identifier;
+    private byte[] bytes;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public DT() {
+        // For Externalizable
+    }
+
+    DT(final byte[] bytes) {
+        this.bytes = requireNonNull(bytes);
+    }
+
+    @Override
+    public byte[] bytes() {
+        return bytes;
+    }
+
+    @Override
+    public void readExternal(final byte[] newBytes) throws IOException {
+        bytes = requireNonNull(newBytes);
+        identifier = verifyNotNull(ClientIdentifier.readFrom(ByteStreams.newDataInput(newBytes)));
+    }
+
+    @Override
+    public Object readResolve() {
+        return new DisableTrackingPayload(identifier, bytes);
+    }
+}
index 9b58eb17235cd34a04181091d6a3666d0f4078eb..31e4356bbce4816916c17a418c52423c08e427d2 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.controller.cluster.datastore.persisted;
 
+import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.collect.ImmutableList;
@@ -27,24 +28,31 @@ import org.opendaylight.controller.cluster.raft.persisted.Snapshot;
  * @author Thomas Pantelis
  */
 public final class DatastoreSnapshot implements Serializable {
-    private static final class Proxy implements Externalizable {
-        private static final long serialVersionUID = 1L;
+    interface SerialForm extends Externalizable {
 
-        private DatastoreSnapshot datastoreSnapshot;
+        DatastoreSnapshot datastoreSnapshot();
 
-        // checkstyle flags the public modifier as redundant which really doesn't make sense since it clearly isn't
-        // redundant. It is explicitly needed for Java serialization to be able to create instances via reflection.
-        @SuppressWarnings("checkstyle:RedundantModifier")
-        public Proxy() {
-            // For Externalizable
-        }
+        Object readResolve();
 
-        Proxy(final DatastoreSnapshot datastoreSnapshot) {
-            this.datastoreSnapshot = datastoreSnapshot;
+        void resolveTo(@NonNull DatastoreSnapshot newDatastoreSnapshot);
+
+        @Override
+        default void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+            final var type = (String)in.readObject();
+            final var snapshot = (ShardManagerSnapshot) in.readObject();
+
+            final int size = in.readInt();
+            var localShardSnapshots = new ArrayList<ShardSnapshot>(size);
+            for (int i = 0; i < size; i++) {
+                localShardSnapshots.add((ShardSnapshot) in.readObject());
+            }
+
+            resolveTo(new DatastoreSnapshot(type, snapshot, localShardSnapshots));
         }
 
         @Override
-        public void writeExternal(ObjectOutput out) throws IOException {
+        default void writeExternal(ObjectOutput out) throws IOException {
+            final var datastoreSnapshot = datastoreSnapshot();
             out.writeObject(datastoreSnapshot.type);
             out.writeObject(datastoreSnapshot.shardManagerSnapshot);
 
@@ -53,31 +61,45 @@ public final class DatastoreSnapshot implements Serializable {
                 out.writeObject(shardSnapshot);
             }
         }
+    }
 
-        @Override
-        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
-            String localType = (String)in.readObject();
-            ShardManagerSnapshot localShardManagerSnapshot = (ShardManagerSnapshot) in.readObject();
+    private static final class Proxy implements SerialForm {
+        private static final long serialVersionUID = 1L;
 
-            int size = in.readInt();
-            List<ShardSnapshot> localShardSnapshots = new ArrayList<>(size);
-            for (int i = 0; i < size; i++) {
-                localShardSnapshots.add((ShardSnapshot) in.readObject());
-            }
+        private DatastoreSnapshot datastoreSnapshot;
+
+        // checkstyle flags the public modifier as redundant which really doesn't make sense since it clearly isn't
+        // redundant. It is explicitly needed for Java serialization to be able to create instances via reflection.
+        @SuppressWarnings("checkstyle:RedundantModifier")
+        public Proxy() {
+            // For Externalizable
+        }
 
-            datastoreSnapshot = new DatastoreSnapshot(localType, localShardManagerSnapshot, localShardSnapshots);
+        Proxy(final DatastoreSnapshot datastoreSnapshot) {
+            this.datastoreSnapshot = requireNonNull(datastoreSnapshot);
         }
 
-        private Object readResolve() {
+        @Override
+        public DatastoreSnapshot datastoreSnapshot() {
             return datastoreSnapshot;
         }
+
+        @Override
+        public void resolveTo(DatastoreSnapshot newDatastoreSnapshot) {
+            datastoreSnapshot = requireNonNull(newDatastoreSnapshot);
+        }
+
+        @Override
+        public Object readResolve() {
+            return verifyNotNull(datastoreSnapshot);
+        }
     }
 
     private static final long serialVersionUID = 1L;
 
-    private final String type;
+    private final @NonNull String type;
     private final ShardManagerSnapshot shardManagerSnapshot;
-    private final List<ShardSnapshot> shardSnapshots;
+    private final @NonNull ImmutableList<ShardSnapshot> shardSnapshots;
 
     public DatastoreSnapshot(@NonNull String type, @Nullable ShardManagerSnapshot shardManagerSnapshot,
             @NonNull List<ShardSnapshot> shardSnapshots) {
@@ -103,9 +125,28 @@ public final class DatastoreSnapshot implements Serializable {
     }
 
     public static final class ShardSnapshot implements Serializable {
-        private static final long serialVersionUID = 1L;
+        interface SerialForm extends Externalizable {
+
+            ShardSnapshot shardSnapshot();
+
+            Object readResolve();
 
-        private static final class Proxy implements Externalizable {
+            void resolveTo(String name, Snapshot snapshot);
+
+            @Override
+            default void writeExternal(ObjectOutput out) throws IOException {
+                final var shardSnapshot = shardSnapshot();
+                out.writeObject(shardSnapshot.name);
+                out.writeObject(shardSnapshot.snapshot);
+            }
+
+            @Override
+            default void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+                resolveTo((String) in.readObject(), (Snapshot) in.readObject());
+            }
+        }
+
+        private static final class Proxy implements SerialForm {
             private static final long serialVersionUID = 1L;
 
             private ShardSnapshot shardSnapshot;
@@ -122,21 +163,23 @@ public final class DatastoreSnapshot implements Serializable {
             }
 
             @Override
-            public void writeExternal(ObjectOutput out) throws IOException {
-                out.writeObject(shardSnapshot.name);
-                out.writeObject(shardSnapshot.snapshot);
+            public ShardSnapshot shardSnapshot() {
+                return shardSnapshot;
             }
 
             @Override
-            public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
-                shardSnapshot = new ShardSnapshot((String)in.readObject(), (Snapshot) in.readObject());
+            public void resolveTo(final String name, final Snapshot snapshot) {
+                shardSnapshot = new ShardSnapshot(name, snapshot);
             }
 
-            private Object readResolve() {
-                return shardSnapshot;
+            @Override
+            public Object readResolve() {
+                return verifyNotNull(shardSnapshot);
             }
         }
 
+        private static final long serialVersionUID = 1L;
+
         private final @NonNull String name;
         private final @NonNull Snapshot snapshot;
 
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/FM.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/FM.java
new file mode 100644 (file)
index 0000000..5a432cc
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.persisted;
+
+import com.google.common.collect.ImmutableList;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.List;
+
+/**
+ * Externalizable proxy for {@link FrontendShardDataTreeSnapshotMetadata}.
+ */
+final class FM implements Externalizable {
+    private static final long serialVersionUID = 1L;
+
+    private List<FrontendClientMetadata> clients;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public FM() {
+        // For Externalizable
+    }
+
+    FM(final FrontendShardDataTreeSnapshotMetadata metadata) {
+        clients = metadata.getClients();
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        out.writeInt(clients.size());
+        for (var c : clients) {
+            c.writeTo(out);
+        }
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException {
+        final int size = in.readInt();
+        final var builder = ImmutableList.<FrontendClientMetadata>builderWithExpectedSize(size);
+        for (int i = 0; i < size ; ++i) {
+            builder.add(FrontendClientMetadata.readFrom(in));
+        }
+        clients = builder.build();
+    }
+
+    private Object readResolve() {
+        return new FrontendShardDataTreeSnapshotMetadata(clients);
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/MS.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/MS.java
new file mode 100644 (file)
index 0000000..7246e50
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.persisted;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.collect.ImmutableMap;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.codec.binfmt.NormalizedNodeDataInput;
+import org.opendaylight.yangtools.yang.data.codec.binfmt.NormalizedNodeStreamVersion;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Externalizable proxy for {@link MetadataShardDataTreeSnapshot}.
+ */
+final class MS implements Externalizable {
+    private static final Logger LOG = LoggerFactory.getLogger(MS.class);
+    private static final long serialVersionUID = 1L;
+
+    private Map<Class<? extends ShardDataTreeSnapshotMetadata<?>>, ShardDataTreeSnapshotMetadata<?>> metadata;
+    private NormalizedNodeStreamVersion version;
+    private NormalizedNode rootNode;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public MS() {
+        // For Externalizable
+    }
+
+    MS(final MetadataShardDataTreeSnapshot snapshot) {
+        rootNode = snapshot.getRootNode().orElseThrow();
+        metadata = snapshot.getMetadata();
+        version = snapshot.version().getStreamVersion();
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        out.writeInt(metadata.size());
+        for (var m : metadata.values()) {
+            out.writeObject(m);
+        }
+        try (var stream = version.newDataOutput(out)) {
+            stream.writeNormalizedNode(rootNode);
+        }
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+        final int metaSize = in.readInt();
+        checkArgument(metaSize >= 0, "Invalid negative metadata map length %s", metaSize);
+
+        // Default pre-allocate is 4, which should be fine
+        final var metaBuilder = ImmutableMap
+            .<Class<? extends ShardDataTreeSnapshotMetadata<?>>, ShardDataTreeSnapshotMetadata<?>>builder();
+        for (int i = 0; i < metaSize; ++i) {
+            final var m = (ShardDataTreeSnapshotMetadata<?>) in.readObject();
+            if (m != null) {
+                metaBuilder.put(m.getType(), m);
+            } else {
+                LOG.warn("Skipping null metadata");
+            }
+        }
+        metadata = metaBuilder.build();
+
+        final var stream = NormalizedNodeDataInput.newDataInput(in);
+        version = stream.getVersion();
+        rootNode = stream.readNormalizedNode();
+    }
+
+    private Object readResolve() {
+        return new MetadataShardDataTreeSnapshot(rootNode, metadata);
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/PH.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/PH.java
new file mode 100644 (file)
index 0000000..028d150
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.persisted;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import org.opendaylight.controller.cluster.access.concepts.LocalHistoryIdentifier;
+import org.opendaylight.controller.cluster.datastore.persisted.AbstractIdentifiablePayload.SerialForm;
+
+/**
+ * Serialization proxy for {@link PurgeLocalHistoryPayload}.
+ */
+final class PH implements SerialForm {
+    private static final long serialVersionUID = 1L;
+
+    private LocalHistoryIdentifier identifier;
+    private byte[] bytes;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public PH() {
+        // For Externalizable
+    }
+
+    PH(final byte[] bytes) {
+        this.bytes = requireNonNull(bytes);
+    }
+
+    @Override
+    public byte[] bytes() {
+        return bytes;
+    }
+
+    @Override
+    public void readExternal(final byte[] newBytes) throws IOException {
+        bytes = requireNonNull(newBytes);
+        identifier = verifyNotNull(LocalHistoryIdentifier.readFrom(ByteStreams.newDataInput(newBytes)));
+    }
+
+    @Override
+    public Object readResolve() {
+        return new PurgeLocalHistoryPayload(identifier, bytes);
+    }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/PT.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/PT.java
new file mode 100644 (file)
index 0000000..23bd12b
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.persisted;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import org.opendaylight.controller.cluster.access.concepts.TransactionIdentifier;
+import org.opendaylight.controller.cluster.datastore.persisted.AbstractIdentifiablePayload.SerialForm;
+
+/**
+ * Serialization proxy for {@link PurgeTransactionPayload}.
+ */
+final class PT implements SerialForm {
+    private static final long serialVersionUID = 1L;
+
+    private TransactionIdentifier identifier;
+    private byte[] bytes;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public PT() {
+        // For Externalizable
+    }
+
+    PT(final byte[] bytes) {
+        this.bytes = requireNonNull(bytes);
+    }
+
+    @Override
+    public byte[] bytes() {
+        return bytes;
+    }
+
+    @Override
+    public void readExternal(final byte[] newBytes) throws IOException {
+        bytes = requireNonNull(newBytes);
+        identifier = verifyNotNull(TransactionIdentifier.readFrom(ByteStreams.newDataInput(newBytes)));
+    }
+
+    @Override
+    public Object readResolve() {
+        return new PurgeTransactionPayload(identifier, bytes);
+    }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/SM.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/SM.java
new file mode 100644 (file)
index 0000000..1e2cdc8
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.persisted;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+/**
+ * Serialization proxy for {@link ShardManagerSnapshot}.
+ */
+final class SM implements ShardManagerSnapshot.SerializedForm {
+    private static final long serialVersionUID = 1L;
+
+    private ShardManagerSnapshot snapshot;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public SM() {
+        // For Externalizable
+    }
+
+    SM(final ShardManagerSnapshot snapshot) {
+        this.snapshot = requireNonNull(snapshot);
+    }
+
+    @Override
+    public List<String> shardNames() {
+        return snapshot.getShardList();
+    }
+
+    @Override
+    public void resolveTo(final ShardManagerSnapshot newSnapshot) {
+        snapshot = requireNonNull(newSnapshot);
+    }
+
+    @Override
+    public Object readResolve() {
+        return verifyNotNull(snapshot);
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/SS.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/SS.java
new file mode 100644 (file)
index 0000000..13a8ac6
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.persisted;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Serialization proxy for {@link ShardSnapshotState}.
+ */
+final class SS implements ShardSnapshotState.SerialForm {
+    private static final long serialVersionUID = 1L;
+
+    private ShardSnapshotState snapshotState;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public SS() {
+        // For Externalizable
+    }
+
+    SS(final ShardSnapshotState snapshotState) {
+        this.snapshotState = requireNonNull(snapshotState);
+    }
+
+    @Override
+    public ShardSnapshotState snapshotState() {
+        return snapshotState;
+    }
+
+    @Override
+    public void resolveTo(final ShardSnapshotState newSnapshotState) {
+        snapshotState = requireNonNull(newSnapshotState);
+    }
+
+    @Override
+    public Object readResolve() {
+        return verifyNotNull(snapshotState);
+    }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/ST.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/persisted/ST.java
new file mode 100644 (file)
index 0000000..756a20b
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.persisted;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import org.opendaylight.controller.cluster.access.concepts.LocalHistoryIdentifier;
+import org.opendaylight.controller.cluster.datastore.persisted.AbstractIdentifiablePayload.SerialForm;
+import org.opendaylight.controller.cluster.datastore.utils.ImmutableUnsignedLongSet;
+
+/**
+ * Serialization proxy for {@link SkipTransactionsPayload}.
+ */
+final class ST implements SerialForm {
+    private static final long serialVersionUID = 1L;
+
+    private ImmutableUnsignedLongSet transactionIds;
+    private LocalHistoryIdentifier identifier;
+    private byte[] bytes;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public ST() {
+        // For Externalizable
+    }
+
+    ST(final byte[] bytes) {
+        this.bytes = requireNonNull(bytes);
+    }
+
+    @Override
+    public byte[] bytes() {
+        return bytes;
+    }
+
+    @Override
+    public void readExternal(final byte[] newBytes) throws IOException {
+        bytes = requireNonNull(newBytes);
+
+        final var in = ByteStreams.newDataInput(newBytes);
+        identifier = LocalHistoryIdentifier.readFrom(in);
+        transactionIds = verifyNotNull(ImmutableUnsignedLongSet.readFrom(in));
+    }
+
+    @Override
+    public Object readResolve() {
+        return new SkipTransactionsPayload(identifier, bytes, transactionIds);
+    }
+}
index 03ffa9af793a2778c07fb119a4360e1b6819bb3d..2e0320bbcd7785d3cdb5546d3098280cce9180f0 100644 (file)
@@ -7,6 +7,9 @@
  */
 package org.opendaylight.controller.cluster.datastore.persisted;
 
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
 import com.google.common.collect.ImmutableList;
 import java.io.Externalizable;
 import java.io.IOException;
@@ -23,7 +26,50 @@ import org.eclipse.jdt.annotation.NonNull;
  * @author Thomas Pantelis
  */
 public final class ShardManagerSnapshot implements Serializable {
-    private static final class Proxy implements Externalizable {
+    interface SerializedForm extends Externalizable {
+        /**
+         * Return the serial form of this object contents, corresponding to {@link ShardManagerSnapshot#shardList}.
+         *
+         * @return List of shards names.
+         */
+        List<String> shardNames();
+
+        /**
+         * Resolve this proxy to an actual {@link ShardManagerSnapshot}. Implementations can rely on the object to be
+         * set via {@link #resolveTo(ShardManagerSnapshot)}.
+         *
+         * @return A snapshot
+         */
+        Object readResolve();
+
+        /**
+         * Set this proxy to return {@code snapshot} on next {@link #readResolve()}.
+         *
+         * @param newSnapshot Snapshot to set
+         */
+        void resolveTo(@NonNull ShardManagerSnapshot newSnapshot);
+
+        @Override
+        default void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+            final int size = in.readInt();
+            final var shardList = new ArrayList<String>(size);
+            for (int i = 0; i < size; i++) {
+                shardList.add((String) in.readObject());
+            }
+            resolveTo(new ShardManagerSnapshot(shardList));
+        }
+
+        @Override
+        default void writeExternal(final ObjectOutput out) throws IOException {
+            final var shardList = shardNames();
+            out.writeInt(shardList.size());
+            for (var shardName : shardList) {
+                out.writeObject(shardName);
+            }
+        }
+    }
+
+    private static final class Proxy implements SerializedForm {
         private static final long serialVersionUID = 1L;
 
         private ShardManagerSnapshot snapshot;
@@ -40,26 +86,18 @@ public final class ShardManagerSnapshot implements Serializable {
         }
 
         @Override
-        public void writeExternal(final ObjectOutput out) throws IOException {
-            out.writeInt(snapshot.shardList.size());
-            for (String shard: snapshot.shardList) {
-                out.writeObject(shard);
-            }
+        public List<String> shardNames() {
+            return snapshot.getShardList();
         }
 
         @Override
-        public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
-            int size = in.readInt();
-            List<String> localShardList = new ArrayList<>(size);
-            for (int i = 0; i < size; i++) {
-                localShardList.add((String) in.readObject());
-            }
-
-            snapshot = new ShardManagerSnapshot(localShardList);
+        public void resolveTo(final ShardManagerSnapshot newSnapshot) {
+            snapshot = requireNonNull(newSnapshot);
         }
 
-        private Object readResolve() {
-            return snapshot;
+        @Override
+        public Object readResolve() {
+            return verifyNotNull(snapshot);
         }
     }
 
index 061749db81ab3b854520824769b2964f2a3ad25a..3f0b7486767adc76899b099f522eee3b41dc2715 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.controller.cluster.datastore.persisted;
 
+import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -24,7 +25,26 @@ import org.opendaylight.controller.cluster.raft.persisted.Snapshot;
  * @author Thomas Pantelis
  */
 public final class ShardSnapshotState implements Snapshot.State {
-    private static final class Proxy implements Externalizable {
+    interface SerialForm extends Externalizable {
+
+        ShardSnapshotState snapshotState();
+
+        void resolveTo(@NonNull ShardSnapshotState newSnapshotState);
+
+        Object readResolve();
+
+        @Override
+        default void readExternal(final ObjectInput in) throws IOException {
+            resolveTo(ShardDataTreeSnapshot.deserialize(in));
+        }
+
+        @Override
+        default void writeExternal(final ObjectOutput out) throws IOException {
+            snapshotState().getSnapshot().serialize(out);
+        }
+    }
+
+    private static final class Proxy implements SerialForm {
         private static final long serialVersionUID = 1L;
 
         private ShardSnapshotState snapshotState;
@@ -41,17 +61,18 @@ public final class ShardSnapshotState implements Snapshot.State {
         }
 
         @Override
-        public void writeExternal(final ObjectOutput out) throws IOException {
-            snapshotState.snapshot.serialize(out);
+        public ShardSnapshotState snapshotState() {
+            return snapshotState;
         }
 
         @Override
-        public void readExternal(final ObjectInput in) throws IOException {
-            snapshotState = ShardDataTreeSnapshot.deserialize(in);
+        public void resolveTo(final ShardSnapshotState newSnapshotState) {
+            snapshotState = requireNonNull(newSnapshotState);
         }
 
-        private Object readResolve() {
-            return snapshotState;
+        @Override
+        public Object readResolve() {
+            return verifyNotNull(snapshotState);
         }
     }
 
index 5b8ba739420f0953593cdb20596352fbdb3741e0..a3f7af99f049ad654861cd9373b46e42012341d0 100644 (file)
@@ -63,7 +63,7 @@ public final class SkipTransactionsPayload extends AbstractIdentifiablePayload<L
     @SuppressFBWarnings(value = "SE_BAD_FIELD", justification = "Handled via externalizable proxy")
     private final @NonNull ImmutableUnsignedLongSet transactionIds;
 
-    private SkipTransactionsPayload(final @NonNull LocalHistoryIdentifier historyId,
+    SkipTransactionsPayload(final @NonNull LocalHistoryIdentifier historyId,
             final byte @NonNull [] serialized, final ImmutableUnsignedLongSet transactionIds) {
         super(historyId, serialized);
         this.transactionIds = requireNonNull(transactionIds);