/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2006-2009 Sun Microsystems, Inc. * Portions Copyright 2013 ForgeRock AS. */ package org.opends.server.replication.protocol; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; import java.util.zip.DataFormatException; import org.opends.server.replication.common.CSN; /** * AckMsg messages are used for acknowledging an update that has been sent * requesting an ack: update sent in Assured Mode, either safe data or safe * read sub mode. * The CSN refers to the update CSN that was requested to be acknowledged. * If some errors occurred during attempt to acknowledge the update in the path * to final servers, errors are marked with the following fields: * - hasTimeout: * Some servers went in timeout when the matching update was sent. * - hasWrongStatus: * Some servers were in a status where we cannot ask for an ack when the * matching update was sent. * - hasReplayError: * Some servers made an error replaying the sent matching update. * - failedServers: * The list of server ids that had errors for the sent matching update. Each * server id of the list had one of the 3 possible errors (timeout/wrong status * /replay error) * * AckMsg messages are sent all along the reverse path of the path followed * an update message. */ public class AckMsg extends ReplicationMsg { /** CSN of the update that was acked. */ private CSN csn; /** * Did some servers go in timeout when the matching update (corresponding to * CSN) was sent?. */ private boolean hasTimeout = false; /** * Were some servers in wrong status when the matching update (corresponding * to CSN) was sent?. */ private boolean hasWrongStatus = false; /** * Did some servers make an error replaying the sent matching update * (corresponding to CSN)?. */ private boolean hasReplayError = false; /** * The list of server ids that had errors for the sent matching update * (corresponding to CSN). Each server id of the list had one of the 3 * possible errors (timeout/degraded or admin/replay error). */ private List failedServers = new ArrayList(); /** * Creates a new AckMsg from a CSN (no errors). * * @param csn The CSN used to build the AckMsg. */ public AckMsg(CSN csn) { this.csn = csn; } /** * Creates a new AckMsg from a CSN (with specified error info). * * @param csn The CSN used to build the AckMsg. * @param hasTimeout The hasTimeout info * @param hasWrongStatus The hasWrongStatus info * @param hasReplayError The hasReplayError info * @param failedServers The list of failed servers */ public AckMsg(CSN csn, boolean hasTimeout, boolean hasWrongStatus, boolean hasReplayError, List failedServers) { this.csn = csn; this.hasTimeout = hasTimeout; this.hasWrongStatus = hasWrongStatus; this.hasReplayError = hasReplayError; this.failedServers = failedServers; } /** * Sets the timeout marker for this message. * @param hasTimeout True if some timeout occurred */ public void setHasTimeout(boolean hasTimeout) { this.hasTimeout = hasTimeout; } /** * Sets the wrong status marker for this message. * @param hasWrongStatus True if some servers were in wrong status */ public void setHasWrongStatus(boolean hasWrongStatus) { this.hasWrongStatus = hasWrongStatus; } /** * Sets the replay error marker for this message. * @param hasReplayError True if some servers had errors replaying the change */ public void setHasReplayError(boolean hasReplayError) { this.hasReplayError = hasReplayError; } /** * Sets the list of failing servers for this message. * @param idList The list of failing servers for this message. */ public void setFailedServers(List idList) { this.failedServers = idList; } /** * Creates a new AckMsg by decoding the provided byte array. * * @param in The byte array containing the encoded form of the AckMsg. * @throws DataFormatException If in does not contain a properly encoded * AckMsg. */ public AckMsg(byte[] in) throws DataFormatException { try { /* * The message is stored in the form: * */ // First byte is the type if (in[0] != MSG_TYPE_ACK) { throw new DataFormatException("byte[] is not a valid modify msg"); } int pos = 1; // Read the CSN int length = getNextLength(in, pos); String csnStr = new String(in, pos, length, "UTF-8"); csn = new CSN(csnStr); pos += length + 1; // Read the hasTimeout flag hasTimeout = in[pos++] == 1; // Read the hasWrongStatus flag hasWrongStatus = in[pos++] == 1; // Read the hasReplayError flag hasReplayError = in[pos++] == 1; // Read the list of failed server ids while (pos < in.length) { length = getNextLength(in, pos); String serverIdString = new String(in, pos, length, "UTF-8"); Integer serverId = Integer.valueOf(serverIdString); failedServers.add(serverId); pos += length + 1; } } catch (UnsupportedEncodingException e) { throw new DataFormatException("UTF-8 is not supported by this jvm."); } } /** * Get the CSN from the message. * * @return the CSN */ public CSN getCSN() { return csn; } /** * {@inheritDoc} */ @Override public byte[] getBytes(short protocolVersion) { try { /* * The message is stored in the form: * */ ByteArrayOutputStream oStream = new ByteArrayOutputStream(200); // Put the type of the operation oStream.write(MSG_TYPE_ACK); // Put the CSN byte[] csnByte = csn.toString().getBytes("UTF-8"); oStream.write(csnByte); oStream.write(0); // Put the hasTimeout flag oStream.write(hasTimeout ? 1 : 0); // Put the hasWrongStatus flag oStream.write(hasWrongStatus ? 1 : 0); // Put the hasReplayError flag oStream.write(hasReplayError ? 1 : 0); // Put the list of server ids for (Integer sid : failedServers) { byte[] byteServerId = String.valueOf(sid).getBytes("UTF-8"); oStream.write(byteServerId); oStream.write(0); } return oStream.toByteArray(); } catch (IOException e) { // never happens return null; } } /** * Tells if the matching update had timeout. * @return true if the matching update had timeout */ public boolean hasTimeout() { return hasTimeout; } /** * Tells if the matching update had wrong status error. * @return true if the matching update had wrong status error */ public boolean hasWrongStatus() { return hasWrongStatus; } /** * Tells if the matching update had replay error. * @return true if the matching update had replay error */ public boolean hasReplayError() { return hasReplayError; } /** * Get the list of failed servers. * @return the list of failed servers */ public List getFailedServers() { return failedServers; } /** * Transforms the errors information of the ack into human readable string. * @return A human readable string for errors embedded in the message. */ public String errorsToString() { String idList; if (failedServers.size() > 0) { idList = "["; int size = failedServers.size(); for (int i=0 ; i