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