/* * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 2007-2010 Sun Microsystems, Inc. * Portions Copyright 2011-2013 ForgeRock AS */ package org.opends.server.replication.protocol; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.*; import java.util.zip.DataFormatException; import org.opends.server.replication.common.AssuredMode; import org.opends.server.replication.common.DSInfo; import org.opends.server.replication.common.RSInfo; import org.opends.server.replication.common.ServerStatus; /** * * This class defines a message that is sent: * - By a RS to the other RSs in the topology, containing: * - the list of DSs directly connected to the RS in the DS list * - only this RS in the RS list * - By a RS to his connected DSs, containing every DSs and RSs he knows. * In that case the message contains: * - the list of every DS the RS knows except the destinator DS in the DS list * - the list of every connected RSs (including the sending RS) in the RS list * * Exchanging these messages allows to have each RS or DS take * appropriate decisions according to the current topology: * - a RS can route a message to a DS * - a DS can decide towards which peer DS send referrals * ... */ public class TopologyMsg extends ReplicationMsg { // Information for the DS known in the topology private final List<DSInfo> dsList; // Information for the RS known in the topology private final List<RSInfo> rsList; /** * Creates a new changelogInfo message from its encoded form. * * @param in The byte array containing the encoded form of the message. * @param version The protocol version to use to decode the msg. * @throws java.util.zip.DataFormatException If the byte array does not * contain a valid encoded form of the message. */ public TopologyMsg(byte[] in, short version) throws DataFormatException { try { /* First byte is the type */ if (in.length < 1 || in[0] != MSG_TYPE_TOPOLOGY) { throw new DataFormatException( "Input is not a valid " + this.getClass().getCanonicalName()); } int pos = 1; /* Read number of following DS info entries */ byte nDsInfo = in[pos++]; /* Read the DS info entries */ List<DSInfo> dsList = new ArrayList<DSInfo>(Math.max(0, nDsInfo)); while ( (nDsInfo > 0) && (pos < in.length) ) { /* Read DS id */ int length = getNextLength(in, pos); String serverIdString = new String(in, pos, length, "UTF-8"); int dsId = Integer.valueOf(serverIdString); pos += length + 1; /* Read DS URL */ String dsUrl; if (version >= ProtocolVersion.REPLICATION_PROTOCOL_V6) { length = getNextLength(in, pos); dsUrl = new String(in, pos, length, "UTF-8"); pos += length + 1; } else { dsUrl = ""; } /* Read RS id */ length = getNextLength(in, pos); serverIdString = new String(in, pos, length, "UTF-8"); int rsId = Integer.valueOf(serverIdString); pos += length + 1; /* Read the generation id */ length = getNextLength(in, pos); long generationId = Long.valueOf(new String(in, pos, length, "UTF-8")); pos += length + 1; /* Read DS status */ ServerStatus status = ServerStatus.valueOf(in[pos++]); /* Read DS assured flag */ boolean assuredFlag; assuredFlag = in[pos++] == 1; /* Read DS assured mode */ AssuredMode assuredMode = AssuredMode.valueOf(in[pos++]); /* Read DS safe data level */ byte safeDataLevel = in[pos++]; /* Read DS group id */ byte groupId = in[pos++]; /* Read number of referrals URLs */ List<String> refUrls = new ArrayList<String>(); byte nUrls = in[pos++]; byte nRead = 0; /* Read urls until expected number read */ while ((nRead != nUrls) && (pos < in.length) //security ) { length = getNextLength(in, pos); String url = new String(in, pos, length, "UTF-8"); refUrls.add(url); pos += length + 1; nRead++; } Set<String> attrs = new HashSet<String>(); Set<String> delattrs = new HashSet<String>(); short protocolVersion = -1; if (version >= ProtocolVersion.REPLICATION_PROTOCOL_V4) { byte nAttrs = in[pos++]; nRead = 0; /* Read attrs until expected number read */ while ((nRead != nAttrs) && (pos < in.length)) { length = getNextLength(in, pos); String attr = new String(in, pos, length, "UTF-8"); attrs.add(attr); pos += length + 1; nRead++; } if (version >= ProtocolVersion.REPLICATION_PROTOCOL_V5) { nAttrs = in[pos++]; nRead = 0; /* Read attrs until expected number read */ while ((nRead != nAttrs) && (pos < in.length)) { length = getNextLength(in, pos); String attr = new String(in, pos, length, "UTF-8"); delattrs.add(attr); pos += length + 1; nRead++; } } else { // Default to using the same set of attributes for deletes. delattrs.addAll(attrs); } /* Read Protocol version */ protocolVersion = (short)in[pos++]; } /* Now create DSInfo and store it in list */ DSInfo dsInfo = new DSInfo(dsId, dsUrl, rsId, generationId, status, assuredFlag, assuredMode, safeDataLevel, groupId, refUrls, attrs, delattrs, protocolVersion); dsList.add(dsInfo); nDsInfo--; } /* Read number of following RS info entries */ byte nRsInfo = in[pos++]; /* Read the RS info entries */ List<RSInfo> rsList = new ArrayList<RSInfo>(Math.max(0, nRsInfo)); while ( (nRsInfo > 0) && (pos < in.length) ) { /* Read RS id */ int length = getNextLength(in, pos); String serverIdString = new String(in, pos, length, "UTF-8"); int id = Integer.valueOf(serverIdString); pos += length + 1; /* Read the generation id */ length = getNextLength(in, pos); long generationId = Long.valueOf(new String(in, pos, length, "UTF-8")); pos += length + 1; /* Read RS group id */ byte groupId = in[pos++]; int weight = 1; String serverUrl = null; if (version >= ProtocolVersion.REPLICATION_PROTOCOL_V4) { length = getNextLength(in, pos); serverUrl = new String(in, pos, length, "UTF-8"); pos += length + 1; /* Read RS weight */ length = getNextLength(in, pos); weight = Integer.valueOf(new String(in, pos, length, "UTF-8")); pos += length + 1; } /* Now create RSInfo and store it in list */ RSInfo rsInfo = new RSInfo(id, serverUrl, generationId, groupId, weight); rsList.add(rsInfo); nRsInfo--; } this.dsList = Collections.unmodifiableList(dsList); this.rsList = Collections.unmodifiableList(rsList); } catch (UnsupportedEncodingException e) { throw new DataFormatException("UTF-8 is not supported by this jvm."); } } /** * Creates a new message from a list of the currently connected servers. * * @param dsList The list of currently connected DS servers ID. * @param rsList The list of currently connected RS servers ID. */ public TopologyMsg(List<DSInfo> dsList, List<RSInfo> rsList) { if (dsList == null || dsList.isEmpty()) { this.dsList = Collections.emptyList(); } else { this.dsList = Collections.unmodifiableList(new ArrayList<DSInfo>(dsList)); } if (rsList == null || rsList.isEmpty()) { this.rsList = Collections.emptyList(); } else { this.rsList = Collections.unmodifiableList(new ArrayList<RSInfo>(rsList)); } } // ============ // Msg encoding // ============ /** * {@inheritDoc} */ @Override public byte[] getBytes(short version) throws UnsupportedEncodingException { try { /** * Message has the following form: * <pdu type><number of following DSInfo entries>[<DSInfo>]* * <number of following RSInfo entries>[<RSInfo>]* */ ByteArrayOutputStream oStream = new ByteArrayOutputStream(); /* Put the message type */ oStream.write(MSG_TYPE_TOPOLOGY); // Put number of following DS info entries oStream.write((byte)dsList.size()); // Put DS info for (DSInfo dsInfo : dsList) { // Put DS id byte[] byteServerId = String.valueOf(dsInfo.getDsId()).getBytes("UTF-8"); oStream.write(byteServerId); oStream.write(0); if (version >= ProtocolVersion.REPLICATION_PROTOCOL_V6) { // Put DS URL oStream.write(dsInfo.getDsUrl().getBytes("UTF-8")); oStream.write(0); } // Put RS id byteServerId = String.valueOf(dsInfo.getRsId()).getBytes("UTF-8"); oStream.write(byteServerId); oStream.write(0); // Put the generation id oStream.write(String.valueOf(dsInfo.getGenerationId()). getBytes("UTF-8")); oStream.write(0); // Put DS status oStream.write(dsInfo.getStatus().getValue()); // Put DS assured flag oStream.write(dsInfo.isAssured() ? (byte) 1 : (byte) 0); // Put DS assured mode oStream.write(dsInfo.getAssuredMode().getValue()); // Put DS safe data level oStream.write(dsInfo.getSafeDataLevel()); // Put DS group id oStream.write(dsInfo.getGroupId()); List<String> refUrls = dsInfo.getRefUrls(); // Put number of following URLs as a byte oStream.write(refUrls.size()); for (String url : refUrls) { // Write the url and a 0 terminating byte oStream.write(url.getBytes("UTF-8")); oStream.write(0); } if (version >= ProtocolVersion.REPLICATION_PROTOCOL_V4) { // Put ECL includes Set<String> attrs = dsInfo.getEclIncludes(); oStream.write(attrs.size()); for (String attr : attrs) { oStream.write(attr.getBytes("UTF-8")); oStream.write(0); } if (version >= ProtocolVersion.REPLICATION_PROTOCOL_V5) { Set<String> delattrs = dsInfo.getEclIncludesForDeletes(); oStream.write(delattrs.size()); for (String attr : delattrs) { oStream.write(attr.getBytes("UTF-8")); oStream.write(0); } } oStream.write(dsInfo.getProtocolVersion()); } } // Put number of following RS info entries oStream.write((byte)rsList.size()); // Put RS info for (RSInfo rsInfo : rsList) { // Put RS id byte[] byteServerId = String.valueOf(rsInfo.getId()).getBytes("UTF-8"); oStream.write(byteServerId); oStream.write(0); // Put the generation id oStream.write(String.valueOf(rsInfo.getGenerationId()). getBytes("UTF-8")); oStream.write(0); // Put RS group id oStream.write(rsInfo.getGroupId()); if (version >= ProtocolVersion.REPLICATION_PROTOCOL_V4) { // Put server URL oStream.write(rsInfo.getServerUrl().getBytes("UTF-8")); oStream.write(0); // Put RS weight oStream.write(String.valueOf(rsInfo.getWeight()).getBytes("UTF-8")); oStream.write(0); } } return oStream.toByteArray(); } catch (IOException e) { // never happens throw new RuntimeException(e); } } /** * {@inheritDoc} */ @Override public String toString() { String dsStr = ""; for (DSInfo dsInfo : dsList) { dsStr += dsInfo.toString() + "\n----------------------------\n"; } String rsStr = ""; for (RSInfo rsInfo : rsList) { rsStr += rsInfo.toString() + "\n----------------------------\n"; } return ("TopologyMsg content: " + "\n----------------------------" + "\nCONNECTED DS SERVERS:" + "\n--------------------\n" + dsStr + "CONNECTED RS SERVERS:" + "\n--------------------\n" + rsStr + (rsStr.equals("") ? "----------------------------\n" : "")); } /** * Get the list of DS info. * @return The list of DS info */ public List<DSInfo> getDsList() { return dsList; } /** * Get the list of RS info. * @return The list of RS info */ public List<RSInfo> getRsList() { return rsList; } }