8788c0df989e248d1eddad1be62b16a46237644a
[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  * <p>
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.
29  *
30  * <p>
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.
35  *
36  * <p>
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.
40  *
41  * <p>
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.
44  *
45  * <p>
46  * Note that this class specifies the {@link Immutable} contract, which means that all subclasses must follow this API
47  * contract.
48  *
49  * @author Robert Varga
50  *
51  * @param <T> Target identifier type
52  * @param <C> Message type
53  */
54 @Beta
55 public abstract class Message<T extends WritableIdentifier, C extends Message<T, C>> implements Immutable,
56         Serializable {
57     private static final long serialVersionUID = 1L;
58
59     private final ABIVersion version;
60     private final long sequence;
61     private final T target;
62
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;
67     }
68
69     Message(final T target, final long sequence) {
70         this(ABIVersion.current(), target, sequence);
71     }
72
73     Message(final C msg, final ABIVersion version) {
74         this(version, msg.getTarget(), msg.getSequence());
75     }
76
77     /**
78      * Get the target identifier for this message.
79      *
80      * @return Target identifier
81      */
82     @Nonnull
83     public final T getTarget() {
84         return target;
85     }
86
87     /**
88      * Get the logical sequence number.
89      *
90      * @return logical sequence number
91      */
92     public final long getSequence() {
93         return sequence;
94     }
95
96     @VisibleForTesting
97     @Nonnull
98     public final ABIVersion getVersion() {
99         return version;
100     }
101
102     /**
103      * Return a message which will end up being serialized in the specified {@link ABIVersion}.
104      *
105      * @param toVersion Request {@link ABIVersion}
106      * @return A new message which will use ABIVersion as its serialization.
107      */
108     @SuppressWarnings("unchecked")
109     @Nonnull
110     public final C toVersion(@Nonnull final ABIVersion toVersion) {
111         if (this.version == toVersion) {
112             return (C)this;
113         }
114
115         switch (toVersion) {
116             case BORON:
117                 return Verify.verifyNotNull(cloneAsVersion(toVersion));
118             case TEST_PAST_VERSION:
119             case TEST_FUTURE_VERSION:
120             default:
121                 // Fall-through to throw
122                 break;
123         }
124
125         throw new IllegalArgumentException("Unhandled ABI version " + toVersion);
126     }
127
128     /**
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)}.
132      *
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
136      */
137     @Nonnull
138     protected abstract C cloneAsVersion(@Nonnull ABIVersion targetVersion);
139
140     @Override
141     public final String toString() {
142         return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
143     }
144
145     /**
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.
148      *
149      * @param toStringHelper a {@link ToStringHelper} instance
150      * @return The {@link ToStringHelper} passed in as argument
151      * @throws NullPointerException if toStringHelper is null
152      */
153     @Nonnull
154     protected ToStringHelper addToStringAttributes(@Nonnull final ToStringHelper toStringHelper) {
155         return toStringHelper.add("target", target).add("sequence", Long.toUnsignedString(sequence));
156     }
157
158     /**
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)}.
162      *
163      * @param reqVersion Requested ABI version
164      * @return Proxy for this object
165      */
166     @Nonnull
167     abstract AbstractMessageProxy<T, C> externalizableProxy(@Nonnull ABIVersion reqVersion);
168
169     protected final Object writeReplace() {
170         return externalizableProxy(version);
171     }
172 }