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.access.concepts;
10 import com.google.common.annotations.Beta;
11 import com.google.common.annotations.VisibleForTesting;
12 import com.google.common.base.MoreObjects;
13 import com.google.common.base.MoreObjects.ToStringHelper;
14 import com.google.common.base.Preconditions;
15 import com.google.common.base.Verify;
16 import java.io.Serializable;
17 import javax.annotation.Nonnull;
18 import org.opendaylight.controller.cluster.access.ABIVersion;
19 import org.opendaylight.yangtools.concepts.Immutable;
20 import org.opendaylight.yangtools.concepts.WritableIdentifier;
23 * An abstract concept of a Message. This class cannot be instantiated directly, use its specializations {@link Request}
24 * and {@link Response}.
27 * Messages have a target and a sequence number. Sequence numbers are expected to be assigned monotonically on a
28 * per-target basis, hence two targets can observe the same sequence number.
31 * This class includes explicit versioning for forward- and backward- compatibility of serialization format. This is
32 * achieved by using the serialization proxy pattern. Subclasses are in complete control of what proxy is used to
33 * serialize a particular object on the wire. This class can serve as an explicit version marker, hence no further
34 * action is necessary in the deserialization path.
37 * For the serialization path an explicit call from the user is required to select the appropriate serialization
38 * version. This is done via {@link #toVersion(ABIVersion)} method, which should return a copy of this object with
39 * the requested ABI version recorded and should return the appropriate serialization proxy.
42 * This workflow allows least disturbance across ABI versions, as all messages not affected by a ABI version bump
43 * will remain working with the same serialization format for the new ABI version.
46 * Note that this class specifies the {@link Immutable} contract, which means that all subclasses must follow this API
49 * @author Robert Varga
51 * @param <T> Target identifier type
52 * @param <C> Message type
55 public abstract class Message<T extends WritableIdentifier, C extends Message<T, C>> implements Immutable,
57 private static final long serialVersionUID = 1L;
59 private final ABIVersion version;
60 private final long sequence;
61 private final T target;
63 private Message(final ABIVersion version, final T target, final long sequence) {
64 this.target = Preconditions.checkNotNull(target);
65 this.version = Preconditions.checkNotNull(version);
66 this.sequence = sequence;
69 Message(final T target, final long sequence) {
70 this(ABIVersion.current(), target, sequence);
73 Message(final C msg, final ABIVersion version) {
74 this(version, msg.getTarget(), msg.getSequence());
78 * Get the target identifier for this message.
80 * @return Target identifier
83 public final T getTarget() {
88 * Get the logical sequence number.
90 * @return logical sequence number
92 public final long getSequence() {
98 public final ABIVersion getVersion() {
103 * Return a message which will end up being serialized in the specified {@link ABIVersion}.
105 * @param toVersion Request {@link ABIVersion}
106 * @return A new message which will use ABIVersion as its serialization.
108 @SuppressWarnings("unchecked")
110 public final C toVersion(@Nonnull final ABIVersion toVersion) {
111 if (this.version == toVersion) {
117 return Verify.verifyNotNull(cloneAsVersion(toVersion));
118 case TEST_PAST_VERSION:
119 case TEST_FUTURE_VERSION:
121 // Fall-through to throw
125 throw new IllegalArgumentException("Unhandled ABI version " + toVersion);
129 * Create a copy of this message which will serialize to a stream corresponding to the specified method. This
130 * method should be implemented by the concrete final message class and should invoke the equivalent of
131 * {@link #Message(Message, ABIVersion)}.
133 * @param targetVersion target ABI version
134 * @return A message with the specified serialization stream
135 * @throws IllegalArgumentException if this message does not support the target ABI
138 protected abstract C cloneAsVersion(@Nonnull ABIVersion targetVersion);
141 public final String toString() {
142 return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
146 * Add attributes to the output of {@link #toString()}. Subclasses wanting to contribute additional information
147 * should override this method. Any null attributes will be omitted from the output.
149 * @param toStringHelper a {@link ToStringHelper} instance
150 * @return The {@link ToStringHelper} passed in as argument
151 * @throws NullPointerException if toStringHelper is null
154 protected ToStringHelper addToStringAttributes(@Nonnull final ToStringHelper toStringHelper) {
155 return toStringHelper.add("target", target).add("sequence", Long.toUnsignedString(sequence));
159 * Instantiate a serialization proxy for this object for the target ABI version. Implementations should return
160 * different objects for incompatible {@link ABIVersion}s. This method should never fail, as any compatibility
161 * checks should have been done by {@link #cloneAsVersion(ABIVersion)}.
163 * @param reqVersion Requested ABI version
164 * @return Proxy for this object
167 abstract AbstractMessageProxy<T, C> externalizableProxy(@Nonnull ABIVersion reqVersion);
169 protected final Object writeReplace() {
170 return externalizableProxy(version);