87b0e6ef345d8ad0c142fd6259d0ab0b1a5f2b67
[controller.git] / opendaylight / md-sal / cds-access-api / src / main / java / org / opendaylight / controller / cluster / access / concepts / Message.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.cluster.access.concepts;
9
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;
21
22 /**
23  * An abstract concept of a Message. This class cannot be instantiated directly, use its specializations {@link Request}
24  * and {@link Response}.
25  *
26  * Messages have a target and a sequence number. Sequence numbers are expected to be assigned monotonically on a
27  * per-target basis, hence two targets can observe the same sequence number.
28  *
29  * This class includes explicit versioning for forward- and backward- compatibility of serialization format. This is
30  * achieved by using the serialization proxy pattern. Subclasses are in complete control of what proxy is used to
31  * serialize a particular object on the wire. This class can serve as an explicit version marker, hence no further
32  * action is necessary in the deserialization path.
33  *
34  * For the serialization path an explicit call from the user is required to select the appropriate serialization
35  * version. This is done via {@link #toVersion(ABIVersion)} method, which should return a copy of this object with
36  * the requested ABI version recorded and should return the appropriate serialization proxy.
37  *
38  * This workflow allows least disturbance across ABI versions, as all messages not affected by a ABI version bump
39  * will remain working with the same serialization format for the new ABI version.
40  *
41  * Note that this class specifies the {@link Immutable} contract, which means that all subclasses must follow this API
42  * contract.
43  *
44  * @author Robert Varga
45  *
46  * @param <T> Target identifier type
47  * @param <C> Message type
48  */
49 @Beta
50 public abstract class Message<T extends WritableIdentifier, C extends Message<T, C>> implements Immutable,
51         Serializable {
52     private static final long serialVersionUID = 1L;
53     private final T target;
54     private final long sequence;
55     private final ABIVersion version;
56     private final long retry;
57
58     private Message(final ABIVersion version, final T target, final long sequence, final long retry) {
59         this.target = Preconditions.checkNotNull(target);
60         this.version = Preconditions.checkNotNull(version);
61         this.sequence = sequence;
62         this.retry = retry;
63     }
64
65     Message(final T target, final long sequence, final long retry) {
66         this(ABIVersion.current(), target, sequence, retry);
67     }
68
69     Message(final C msg, final ABIVersion version) {
70         this(version, msg.getTarget(), msg.getSequence(), msg.getRetry());
71     }
72
73     Message(final C msg, final long retry) {
74         this(msg.getVersion(), msg.getTarget(), msg.getSequence(), retry);
75     }
76
77     /**
78      * Get the target identifier for this message.
79      *
80      * @return Target identifier
81      */
82     public final @Nonnull T getTarget() {
83         return target;
84     }
85
86     /**
87      * Get the message sequence of this message.
88      *
89      * @return Message sequence
90      */
91     public final long getSequence() {
92         return sequence;
93     }
94
95     @VisibleForTesting
96     public final ABIVersion getVersion() {
97         return version;
98     }
99
100     /**
101      * Get the message retry counter.
102      *
103      * @return Retry counter
104      */
105     public final long getRetry() {
106         return retry;
107     }
108
109     /**
110      * Return a message which will end up being serialized in the specified {@link ABIVersion}.
111      *
112      * @param version Request {@link ABIVersion}
113      * @return A new message which will use ABIVersion as its serialization.
114      */
115     @SuppressWarnings("unchecked")
116     public final @Nonnull C toVersion(final @Nonnull ABIVersion version) {
117         if (this.version == version) {
118             return (C)this;
119         }
120
121         switch (version) {
122             case BORON:
123                 return Verify.verifyNotNull(cloneAsVersion(version));
124             case TEST_PAST_VERSION:
125             case TEST_FUTURE_VERSION:
126                 // Fall-through to throw
127                 break;
128         }
129
130         throw new IllegalArgumentException("Unhandled ABI version " + version);
131     }
132
133     /**
134      * Create a copy of this message which will serialize to a stream corresponding to the specified method. This
135      * method should be implemented by the concrete final message class and should invoke the equivalent of
136      * {@link #Message(Message, ABIVersion)}.
137      *
138      * @param version target ABI version
139      * @return A message with the specified serialization stream
140      * @throws IllegalArgumentException if this message does not support the target ABI
141      */
142     protected abstract @Nonnull C cloneAsVersion(@Nonnull ABIVersion version);
143
144     /**
145      * Return a message which will have the retry counter incremented by one.
146      *
147      * @return A message with the specified retry counter
148      */
149     public final @Nonnull C incrementRetry() {
150         return Verify.verifyNotNull(cloneAsRetry(retry +1));
151     }
152
153     /**
154      * Create a copy of this message which will have its retry count bumped. This method should be implemented by
155      * the concrete final message class and should invoked the equivalent of {@link #Message(Message, long)}.
156      *
157      * @param retry new retry count
158      * @return A message with the specified retry counter
159      */
160     protected abstract @Nonnull C cloneAsRetry(long retry);
161
162     @Override
163     public final String toString() {
164         return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
165     }
166
167     /**
168      * Add attributes to the output of {@link #toString()}. Subclasses wanting to contribute additional information
169      * should override this method. Any null attributes will be omitted from the output.
170      *
171      * @param toStringHelper a {@link ToStringHelper} instance
172      * @return The {@link ToStringHelper} passed in as argument
173      * @throws NullPointerException if toStringHelper is null
174      */
175     protected @Nonnull ToStringHelper addToStringAttributes(final @Nonnull ToStringHelper toStringHelper) {
176         return toStringHelper.add("target", target).add("sequence", Long.toUnsignedString(sequence, 16));
177     }
178
179     /**
180      * Instantiate a serialization proxy for this object for the target ABI version. Implementations should return
181      * different objects for incompatible {@link ABIVersion}s. This method should never fail, as any compatibility
182      * checks should have been done by {@link #cloneAsVersion(ABIVersion)}.
183      *
184      * @param version Requested ABI version
185      * @return Proxy for this object
186      */
187     abstract @Nonnull AbstractMessageProxy<T, C> externalizableProxy(@Nonnull ABIVersion version);
188
189     protected final Object writeReplace() {
190         return externalizableProxy(version);
191     }
192 }