Force AbstractRaftRPC to use Externalizable proxy pattern
[controller.git] / opendaylight / md-sal / sal-akka-raft / src / main / java / org / opendaylight / controller / cluster / raft / messages / AppendEntriesReply.java
1 /*
2  * Copyright (c) 2014 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.raft.messages;
9
10 import com.google.common.annotations.VisibleForTesting;
11 import java.io.Externalizable;
12 import java.io.IOException;
13 import java.io.ObjectInput;
14 import java.io.ObjectOutput;
15 import org.opendaylight.controller.cluster.raft.RaftVersions;
16
17 /**
18  * Reply for the AppendEntries message.
19  */
20 public final class AppendEntriesReply extends AbstractRaftRPC {
21     private static final long serialVersionUID = -7487547356392536683L;
22
23     // true if follower contained entry matching
24     // prevLogIndex and prevLogTerm
25     private final boolean success;
26
27     // The index of the last entry in the followers log
28     // This will be used to set the matchIndex for the follower on the
29     // Leader
30     private final long logLastIndex;
31
32     private final long logLastTerm;
33
34     // The followerId - this will be used to figure out which follower is
35     // responding
36     private final String followerId;
37
38     private final short payloadVersion;
39
40     private final short raftVersion;
41
42     private final boolean forceInstallSnapshot;
43
44     private final boolean needsLeaderAddress;
45
46     private final short recipientRaftVersion;
47
48     @VisibleForTesting
49     public AppendEntriesReply(final String followerId, final long term, final boolean success, final long logLastIndex,
50             final long logLastTerm, final short payloadVersion) {
51         this(followerId, term, success, logLastIndex, logLastTerm, payloadVersion, false, false,
52                 RaftVersions.CURRENT_VERSION);
53     }
54
55     public AppendEntriesReply(final String followerId, final long term, final boolean success, final long logLastIndex,
56             final long logLastTerm, final short payloadVersion, final boolean forceInstallSnapshot,
57             final boolean needsLeaderAddress, final short recipientRaftVersion) {
58         this(followerId, term, success, logLastIndex, logLastTerm, payloadVersion, forceInstallSnapshot,
59                 needsLeaderAddress, RaftVersions.CURRENT_VERSION, recipientRaftVersion);
60     }
61
62     private AppendEntriesReply(final String followerId, final long term, final boolean success, final long logLastIndex,
63             final long logLastTerm, final short payloadVersion, final boolean forceInstallSnapshot,
64             final boolean needsLeaderAddress, final short raftVersion, final short recipientRaftVersion) {
65         super(term);
66         this.followerId = followerId;
67         this.success = success;
68         this.logLastIndex = logLastIndex;
69         this.logLastTerm = logLastTerm;
70         this.payloadVersion = payloadVersion;
71         this.forceInstallSnapshot = forceInstallSnapshot;
72         this.raftVersion = raftVersion;
73         this.needsLeaderAddress = needsLeaderAddress;
74         this.recipientRaftVersion = recipientRaftVersion;
75     }
76
77     public boolean isSuccess() {
78         return success;
79     }
80
81     public long getLogLastIndex() {
82         return logLastIndex;
83     }
84
85     public long getLogLastTerm() {
86         return logLastTerm;
87     }
88
89     public String getFollowerId() {
90         return followerId;
91     }
92
93     public short getPayloadVersion() {
94         return payloadVersion;
95     }
96
97     public short getRaftVersion() {
98         return raftVersion;
99     }
100
101     public boolean isForceInstallSnapshot() {
102         return forceInstallSnapshot;
103     }
104
105     public boolean isNeedsLeaderAddress() {
106         return needsLeaderAddress;
107     }
108
109     @Override
110     public String toString() {
111         return "AppendEntriesReply [term=" + getTerm() + ", success=" + success + ", followerId=" + followerId
112                 + ", logLastIndex=" + logLastIndex + ", logLastTerm=" + logLastTerm + ", forceInstallSnapshot="
113                 + forceInstallSnapshot + ", needsLeaderAddress=" + needsLeaderAddress
114                 + ", payloadVersion=" + payloadVersion + ", raftVersion=" + raftVersion
115                 + ", recipientRaftVersion=" + recipientRaftVersion + "]";
116     }
117
118     @Override
119     Object writeReplace() {
120         return recipientRaftVersion >= RaftVersions.FLUORINE_VERSION ? new Proxy2(this) : new Proxy(this);
121     }
122
123     /**
124      * Fluorine version that adds the needsLeaderAddress flag.
125      */
126     private static class Proxy2 implements Externalizable {
127         private static final long serialVersionUID = 1L;
128
129         private AppendEntriesReply appendEntriesReply;
130
131         // checkstyle flags the public modifier as redundant which really doesn't make sense since it clearly isn't
132         // redundant. It is explicitly needed for Java serialization to be able to create instances via reflection.
133         @SuppressWarnings("checkstyle:RedundantModifier")
134         public Proxy2() {
135         }
136
137         Proxy2(final AppendEntriesReply appendEntriesReply) {
138             this.appendEntriesReply = appendEntriesReply;
139         }
140
141         @Override
142         public void writeExternal(final ObjectOutput out) throws IOException {
143             out.writeShort(appendEntriesReply.raftVersion);
144             out.writeLong(appendEntriesReply.getTerm());
145             out.writeObject(appendEntriesReply.followerId);
146             out.writeBoolean(appendEntriesReply.success);
147             out.writeLong(appendEntriesReply.logLastIndex);
148             out.writeLong(appendEntriesReply.logLastTerm);
149             out.writeShort(appendEntriesReply.payloadVersion);
150             out.writeBoolean(appendEntriesReply.forceInstallSnapshot);
151             out.writeBoolean(appendEntriesReply.needsLeaderAddress);
152         }
153
154         @Override
155         public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
156             short raftVersion = in.readShort();
157             long term = in.readLong();
158             String followerId = (String) in.readObject();
159             boolean success = in.readBoolean();
160             long logLastIndex = in.readLong();
161             long logLastTerm = in.readLong();
162             short payloadVersion = in.readShort();
163             boolean forceInstallSnapshot = in.readBoolean();
164             boolean needsLeaderAddress = in.readBoolean();
165
166             appendEntriesReply = new AppendEntriesReply(followerId, term, success, logLastIndex, logLastTerm,
167                     payloadVersion, forceInstallSnapshot, needsLeaderAddress, raftVersion,
168                     RaftVersions.CURRENT_VERSION);
169         }
170
171         private Object readResolve() {
172             return appendEntriesReply;
173         }
174     }
175
176     /**
177      * Pre-Fluorine version.
178      */
179     @Deprecated
180     private static class Proxy implements Externalizable {
181         private static final long serialVersionUID = 1L;
182
183         private AppendEntriesReply appendEntriesReply;
184
185         // checkstyle flags the public modifier as redundant which really doesn't make sense since it clearly isn't
186         // redundant. It is explicitly needed for Java serialization to be able to create instances via reflection.
187         @SuppressWarnings("checkstyle:RedundantModifier")
188         public Proxy() {
189         }
190
191         Proxy(final AppendEntriesReply appendEntriesReply) {
192             this.appendEntriesReply = appendEntriesReply;
193         }
194
195         @Override
196         public void writeExternal(final ObjectOutput out) throws IOException {
197             out.writeShort(appendEntriesReply.raftVersion);
198             out.writeLong(appendEntriesReply.getTerm());
199             out.writeObject(appendEntriesReply.followerId);
200             out.writeBoolean(appendEntriesReply.success);
201             out.writeLong(appendEntriesReply.logLastIndex);
202             out.writeLong(appendEntriesReply.logLastTerm);
203             out.writeShort(appendEntriesReply.payloadVersion);
204             out.writeBoolean(appendEntriesReply.forceInstallSnapshot);
205         }
206
207         @Override
208         public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
209             short raftVersion = in.readShort();
210             long term = in.readLong();
211             String followerId = (String) in.readObject();
212             boolean success = in.readBoolean();
213             long logLastIndex = in.readLong();
214             long logLastTerm = in.readLong();
215             short payloadVersion = in.readShort();
216             boolean forceInstallSnapshot = in.readBoolean();
217
218             appendEntriesReply = new AppendEntriesReply(followerId, term, success, logLastIndex, logLastTerm,
219                     payloadVersion, forceInstallSnapshot, false, raftVersion, RaftVersions.CURRENT_VERSION);
220         }
221
222         private Object readResolve() {
223             return appendEntriesReply;
224         }
225     }
226 }