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