001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2006-2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2013-2015 ForgeRock AS.
026 */
027package org.opends.server.replication.protocol;
028
029import java.util.ArrayList;
030import java.util.List;
031import java.util.zip.DataFormatException;
032
033import org.opends.server.replication.common.CSN;
034
035/**
036 * AckMsg messages are used for acknowledging an update that has been sent
037 * requesting an ack: update sent in Assured Mode, either safe data or safe
038 * read sub mode.
039 * The CSN refers to the update CSN that was requested to be acknowledged.
040 * If some errors occurred during attempt to acknowledge the update in the path
041 * to final servers, errors are marked with the following fields:
042 * - hasTimeout:
043 * Some servers went in timeout when the matching update was sent.
044 * - hasWrongStatus:
045 * Some servers were in a status where we cannot ask for an ack when the
046 * matching update was sent.
047 * - hasReplayError:
048 * Some servers made an error replaying the sent matching update.
049 * - failedServers:
050 * The list of server ids that had errors for the sent matching update. Each
051 * server id of the list had one of the 3 possible errors (timeout/wrong status
052 * /replay error)
053 *
054 * AckMsg messages are sent all along the reverse path of the path followed
055 * an update message.
056 */
057public class AckMsg extends ReplicationMsg
058{
059  /** CSN of the update that was acked. */
060  private final CSN csn;
061
062  /**
063   * Did some servers go in timeout when the matching update (corresponding to
064   * CSN) was sent?.
065   */
066  private boolean hasTimeout;
067
068  /**
069   * Were some servers in wrong status when the matching update (corresponding
070   * to CSN) was sent?.
071   */
072  private boolean hasWrongStatus;
073
074  /**
075   * Did some servers make an error replaying the sent matching update
076   * (corresponding to CSN)?.
077   */
078  private boolean hasReplayError;
079
080  /**
081   * The list of server ids that had errors for the sent matching update
082   * (corresponding to CSN). Each server id of the list had one of the 3
083   * possible errors (timeout/degraded or admin/replay error).
084   */
085  private List<Integer> failedServers = new ArrayList<>();
086
087  /**
088   * Creates a new AckMsg from a CSN (no errors).
089   *
090   * @param csn The CSN used to build the AckMsg.
091   */
092  public AckMsg(CSN csn)
093  {
094    this.csn = csn;
095  }
096
097  /**
098   * Creates a new AckMsg from a CSN (with specified error info).
099   *
100   * @param csn The CSN used to build the AckMsg.
101   * @param hasTimeout The hasTimeout info
102   * @param hasWrongStatus The hasWrongStatus info
103   * @param hasReplayError The hasReplayError info
104   * @param failedServers The list of failed servers
105   */
106  public AckMsg(CSN csn, boolean hasTimeout, boolean hasWrongStatus,
107      boolean hasReplayError, List<Integer> failedServers)
108  {
109    this.csn = csn;
110    this.hasTimeout = hasTimeout;
111    this.hasWrongStatus = hasWrongStatus;
112    this.hasReplayError = hasReplayError;
113    this.failedServers = failedServers;
114  }
115
116  /**
117   * Sets the timeout marker for this message.
118   * @param hasTimeout True if some timeout occurred
119   */
120  public void setHasTimeout(boolean hasTimeout)
121  {
122    this.hasTimeout = hasTimeout;
123  }
124
125  /**
126   * Sets the wrong status marker for this message.
127   * @param hasWrongStatus True if some servers were in wrong status
128   */
129  public void setHasWrongStatus(boolean hasWrongStatus)
130  {
131    this.hasWrongStatus = hasWrongStatus;
132  }
133
134  /**
135   * Sets the replay error marker for this message.
136   * @param hasReplayError True if some servers had errors replaying the change
137   */
138  public void setHasReplayError(boolean hasReplayError)
139  {
140    this.hasReplayError = hasReplayError;
141  }
142
143  /**
144   * Sets the list of failing servers for this message.
145   * @param idList The list of failing servers for this message.
146   */
147  public void setFailedServers(List<Integer> idList)
148  {
149    this.failedServers = idList;
150  }
151
152  /**
153   * Creates a new AckMsg by decoding the provided byte array.
154   *
155   * @param in The byte array containing the encoded form of the AckMsg.
156   * @throws DataFormatException If in does not contain a properly encoded
157   *                             AckMsg.
158   */
159  AckMsg(byte[] in) throws DataFormatException
160  {
161    /*
162     * The message is stored in the form:
163     * <operation type><CSN><has timeout><has degraded><has replay
164     * error><failed server ids>
165     */
166    final ByteArrayScanner scanner = new ByteArrayScanner(in);
167    final byte msgType = scanner.nextByte();
168    if (msgType != MSG_TYPE_ACK)
169    {
170      throw new DataFormatException("byte[] is not a valid modify msg");
171    }
172
173    csn = scanner.nextCSNUTF8();
174    hasTimeout = scanner.nextBoolean();
175    hasWrongStatus = scanner.nextBoolean();
176    hasReplayError = scanner.nextBoolean();
177
178    while (!scanner.isEmpty())
179    {
180      failedServers.add(scanner.nextIntUTF8());
181    }
182  }
183
184  /**
185   * Get the CSN from the message.
186   *
187   * @return the CSN
188   */
189  public CSN getCSN()
190  {
191    return csn;
192  }
193
194  /** {@inheritDoc} */
195  @Override
196  public byte[] getBytes(short protocolVersion)
197  {
198    /*
199     * The message is stored in the form:
200     * <operation type><CSN><has timeout><has degraded><has replay
201     * error><failed server ids>
202     */
203    final ByteArrayBuilder builder = new ByteArrayBuilder();
204    builder.appendByte(MSG_TYPE_ACK);
205    builder.appendCSNUTF8(csn);
206    builder.appendBoolean(hasTimeout);
207    builder.appendBoolean(hasWrongStatus);
208    builder.appendBoolean(hasReplayError);
209    for (int serverId : failedServers)
210    {
211      builder.appendIntUTF8(serverId);
212    }
213    return builder.toByteArray();
214  }
215
216  /**
217   * Tells if the matching update had timeout.
218   * @return true if the matching update had timeout
219   */
220  public boolean hasTimeout()
221  {
222    return hasTimeout;
223  }
224
225  /**
226   * Tells if the matching update had wrong status error.
227   * @return true if the matching update had wrong status error
228   */
229  public boolean hasWrongStatus()
230  {
231    return hasWrongStatus;
232  }
233
234  /**
235   * Tells if the matching update had replay error.
236   * @return true if the matching update had replay error
237   */
238  public boolean hasReplayError()
239  {
240    return hasReplayError;
241  }
242
243  /**
244   * Get the list of failed servers.
245   * @return the list of failed servers
246   */
247  public List<Integer> getFailedServers()
248  {
249    return failedServers;
250  }
251
252  /**
253   * Transforms the errors information of the ack into human readable string.
254   * @return A human readable string for errors embedded in the message.
255   */
256  public String errorsToString()
257  {
258    final String idList =
259        !failedServers.isEmpty() ? failedServers.toString() : "none";
260
261    return "hasTimeout: " + (hasTimeout ? "yes" : "no")  + ", " +
262      "hasWrongStatus: " + (hasWrongStatus ? "yes" : "no")  + ", " +
263      "hasReplayError: " + (hasReplayError ? "yes" : "no")  + ", " +
264      "concerned server ids: " + idList;
265  }
266
267}
268