X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fcds-access-api%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fcluster%2Faccess%2Fconcepts%2FMessage.java;h=9748264e7fc0144bd2e9780599418600f2c28ae3;hb=HEAD;hp=30e631eed7b0e9f46ba5e5fd2a72e985d9981233;hpb=9b4f21460c6dcb10c381df631d064d05de16546c;p=controller.git diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/Message.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/Message.java index 30e631eed7..9748264e7f 100644 --- a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/Message.java +++ b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/Message.java @@ -7,57 +7,107 @@ */ package org.opendaylight.controller.cluster.access.concepts; -import com.google.common.annotations.Beta; +import static com.google.common.base.Verify.verifyNotNull; +import static java.util.Objects.requireNonNull; + import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects.ToStringHelper; -import com.google.common.base.Preconditions; -import com.google.common.base.Verify; +import java.io.DataInput; +import java.io.Externalizable; +import java.io.IOException; +import java.io.NotSerializableException; +import java.io.ObjectInput; +import java.io.ObjectInputStream; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamException; import java.io.Serializable; -import javax.annotation.Nonnull; +import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.controller.cluster.access.ABIVersion; import org.opendaylight.yangtools.concepts.Immutable; import org.opendaylight.yangtools.concepts.WritableIdentifier; +import org.opendaylight.yangtools.concepts.WritableObjects; /** * An abstract concept of a Message. This class cannot be instantiated directly, use its specializations {@link Request} * and {@link Response}. * + *

* Messages have a target and a sequence number. Sequence numbers are expected to be assigned monotonically on a * per-target basis, hence two targets can observe the same sequence number. * + *

* This class includes explicit versioning for forward- and backward- compatibility of serialization format. This is * achieved by using the serialization proxy pattern. Subclasses are in complete control of what proxy is used to * serialize a particular object on the wire. This class can serve as an explicit version marker, hence no further * action is necessary in the deserialization path. * + *

* For the serialization path an explicit call from the user is required to select the appropriate serialization * version. This is done via {@link #toVersion(ABIVersion)} method, which should return a copy of this object with * the requested ABI version recorded and should return the appropriate serialization proxy. * + *

* This workflow allows least disturbance across ABI versions, as all messages not affected by a ABI version bump * will remain working with the same serialization format for the new ABI version. * + *

* Note that this class specifies the {@link Immutable} contract, which means that all subclasses must follow this API * contract. * - * @author Robert Varga - * * @param Target identifier type * @param Message type */ -@Beta -public abstract class Message> implements Immutable, - Serializable { +public abstract class Message> + implements Immutable, Serializable { + /** + * Externalizable proxy for use with {@link Message} subclasses. + * + * @param Target identifier type + * @param Message class + */ + protected interface SerialForm> extends Externalizable { + + @NonNull C message(); + + void setMessage(@NonNull C message); + + @Override + default void writeExternal(final ObjectOutput out) throws IOException { + final var message = message(); + message.getTarget().writeTo(out); + WritableObjects.writeLong(out, message.getSequence()); + writeExternal(out, message); + } + + void writeExternal(@NonNull ObjectOutput out, @NonNull C msg) throws IOException; + + @Override + default void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException { + final var target = verifyNotNull(readTarget(in)); + final var sequence = WritableObjects.readLong(in); + setMessage(verifyNotNull(readExternal(in, target, sequence))); + } + + @NonNull C readExternal(@NonNull ObjectInput in, @NonNull T target, long sequence) + throws IOException, ClassNotFoundException; + + Object readResolve(); + + @NonNull T readTarget(@NonNull DataInput in) throws IOException; + } + + @java.io.Serial private static final long serialVersionUID = 1L; - private final ABIVersion version; + private final @NonNull ABIVersion version; private final long sequence; - private final T target; + private final @NonNull T target; private Message(final ABIVersion version, final T target, final long sequence) { - this.target = Preconditions.checkNotNull(target); - this.version = Preconditions.checkNotNull(version); + this.target = requireNonNull(target); + this.version = requireNonNull(version); this.sequence = sequence; } @@ -74,7 +124,7 @@ public abstract class Message verifyNotNull(cloneAsVersion(toVersion)); + default -> throw new IllegalArgumentException("Unhandled ABI version " + toVersion); + }; } /** @@ -121,11 +165,11 @@ public abstract class Message externalizableProxy(@Nonnull ABIVersion version); + protected abstract @NonNull SerialForm externalizableProxy(@NonNull ABIVersion reqVersion); + @java.io.Serial protected final Object writeReplace() { return externalizableProxy(version); } + + protected final void throwNSE() throws NotSerializableException { + throw new NotSerializableException(getClass().getName()); + } + + @java.io.Serial + private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException { + throwNSE(); + } + + @java.io.Serial + private void readObjectNoData() throws ObjectStreamException { + throwNSE(); + } + + @java.io.Serial + private void writeObject(final ObjectOutputStream stream) throws IOException { + throwNSE(); + } }