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