/*
* 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 2007-2010 Sun Microsystems, Inc.
* Portions Copyright 2011-2015 ForgeRock AS
*/
package org.opends.server.replication.protocol;
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;
import static org.opends.server.replication.protocol.ProtocolVersion.*;
/**
* This class defines a message that is sent:
* - By a RS to the other RSs in the topology, containing:
* - the DSs directly connected to the RS in the DS infos
* - only this RS in the RS infos
* - By a RS to his connected DSs, containing every DSs and RSs he knows.
* In that case the message contains:
* - every DSs the RS knows except the destinator DS in the DS infos
* - every connected RSs (including the sending RS) in the RS infos
*
* 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 DSs (aka replicas) known in the topology. */
private final Map<Integer, DSInfo> replicaInfos;
/** Information for the RSs known in the topology. */
private final List<RSInfo> rsInfos;
/**
* 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.
*/
TopologyMsg(byte[] in, short version) throws DataFormatException
{
final ByteArrayScanner scanner = new ByteArrayScanner(in);
final byte msgType = scanner.nextByte();
if (msgType != MSG_TYPE_TOPOLOGY)
{
throw new DataFormatException("Input is not a valid "
+ getClass().getCanonicalName());
}
// Read the DS info entries, first read number of them
int nDsInfo = scanner.nextByte();
final Map<Integer, DSInfo> replicaInfos = new HashMap<>(Math.max(0, nDsInfo));
while (nDsInfo > 0 && !scanner.isEmpty())
{
final DSInfo dsInfo = nextDSInfo(scanner, version);
replicaInfos.put(dsInfo.getDsId(), dsInfo);
nDsInfo--;
}
// Read the RS info entries
int nRsInfo = scanner.nextByte();
final List<RSInfo> rsInfos = new ArrayList<>(Math.max(0, nRsInfo));
while (nRsInfo > 0 && !scanner.isEmpty())
{
rsInfos.add(nextRSInfo(scanner, version));
nRsInfo--;
}
this.replicaInfos = Collections.unmodifiableMap(replicaInfos);
this.rsInfos = Collections.unmodifiableList(rsInfos);
}
private DSInfo nextDSInfo(ByteArrayScanner scanner, short version)
throws DataFormatException
{
final int dsId = scanner.nextIntUTF8();
final String dsUrl =
version < REPLICATION_PROTOCOL_V6 ? "" : scanner.nextString();
final int rsId = scanner.nextIntUTF8();
final long generationId = scanner.nextLongUTF8();
final ServerStatus status = ServerStatus.valueOf(scanner.nextByte());
final boolean assuredFlag = scanner.nextBoolean();
final AssuredMode assuredMode = AssuredMode.valueOf(scanner.nextByte());
final byte safeDataLevel = scanner.nextByte();
final byte groupId = scanner.nextByte();
final List<String> refUrls = new ArrayList<>();
scanner.nextStrings(refUrls);
final Set<String> attrs = new HashSet<>();
final Set<String> delattrs = new HashSet<>();
short protocolVersion = -1;
if (version >= REPLICATION_PROTOCOL_V4)
{
scanner.nextStrings(attrs);
if (version >= REPLICATION_PROTOCOL_V5)
{
scanner.nextStrings(delattrs);
}
else
{
// Default to using the same set of attributes for deletes.
delattrs.addAll(attrs);
}
protocolVersion = scanner.nextByte();
}
return new DSInfo(dsId, dsUrl, rsId, generationId, status, assuredFlag,
assuredMode, safeDataLevel, groupId, refUrls, attrs, delattrs,
protocolVersion);
}
private RSInfo nextRSInfo(ByteArrayScanner scanner, short version)
throws DataFormatException
{
final int rsId = scanner.nextIntUTF8();
final long generationId = scanner.nextLongUTF8();
final byte groupId = scanner.nextByte();
int weight = 1;
String serverUrl = null;
if (version >= REPLICATION_PROTOCOL_V4)
{
serverUrl = scanner.nextString();
weight = scanner.nextIntUTF8();
}
return new RSInfo(rsId, serverUrl, generationId, groupId, weight);
}
/**
* Creates a new message of the currently connected servers.
*
* @param dsInfos The collection of currently connected DS servers ID.
* @param rsInfos The list of currently connected RS servers ID.
*/
public TopologyMsg(Collection<DSInfo> dsInfos, List<RSInfo> rsInfos)
{
if (dsInfos == null || dsInfos.isEmpty())
{
this.replicaInfos = Collections.emptyMap();
}
else
{
Map<Integer, DSInfo> replicas = new HashMap<>();
for (DSInfo dsInfo : dsInfos)
{
replicas.put(dsInfo.getDsId(), dsInfo);
}
this.replicaInfos = Collections.unmodifiableMap(replicas);
}
if (rsInfos == null || rsInfos.isEmpty())
{
this.rsInfos = Collections.emptyList();
}
else
{
this.rsInfos =
Collections.unmodifiableList(new ArrayList<RSInfo>(rsInfos));
}
}
// ============
// Msg encoding
// ============
/** {@inheritDoc} */
@Override
public byte[] getBytes(short version)
{
/**
* Message has the following form:
* <pdu type><number of following DSInfo entries>[<DSInfo>]*
* <number of following RSInfo entries>[<RSInfo>]*
*/
final ByteArrayBuilder builder = new ByteArrayBuilder();
builder.appendByte(MSG_TYPE_TOPOLOGY);
// Put DS infos
builder.appendByte(replicaInfos.size());
for (DSInfo dsInfo : replicaInfos.values())
{
builder.appendIntUTF8(dsInfo.getDsId());
if (version >= REPLICATION_PROTOCOL_V6)
{
builder.appendString(dsInfo.getDsUrl());
}
builder.appendIntUTF8(dsInfo.getRsId());
builder.appendLongUTF8(dsInfo.getGenerationId());
builder.appendByte(dsInfo.getStatus().getValue());
builder.appendBoolean(dsInfo.isAssured());
builder.appendByte(dsInfo.getAssuredMode().getValue());
builder.appendByte(dsInfo.getSafeDataLevel());
builder.appendByte(dsInfo.getGroupId());
builder.appendStrings(dsInfo.getRefUrls());
if (version >= REPLICATION_PROTOCOL_V4)
{
builder.appendStrings(dsInfo.getEclIncludes());
if (version >= REPLICATION_PROTOCOL_V5)
{
builder.appendStrings(dsInfo.getEclIncludesForDeletes());
}
builder.appendByte(dsInfo.getProtocolVersion());
}
}
// Put RS infos
builder.appendByte(rsInfos.size());
for (RSInfo rsInfo : rsInfos)
{
builder.appendIntUTF8(rsInfo.getId());
builder.appendLongUTF8(rsInfo.getGenerationId());
builder.appendByte(rsInfo.getGroupId());
if (version >= REPLICATION_PROTOCOL_V4)
{
builder.appendString(rsInfo.getServerUrl());
builder.appendIntUTF8(rsInfo.getWeight());
}
}
return builder.toByteArray();
}
/** {@inheritDoc} */
@Override
public String toString()
{
String dsStr = "";
for (DSInfo dsInfo : replicaInfos.values())
{
dsStr += dsInfo + "\n----------------------------\n";
}
String rsStr = "";
for (RSInfo rsInfo : rsInfos)
{
rsStr += rsInfo + "\n----------------------------\n";
}
return "TopologyMsg content:"
+ "\n----------------------------"
+ "\nCONNECTED DS SERVERS:"
+ "\n--------------------\n"
+ dsStr
+ "CONNECTED RS SERVERS:"
+ "\n--------------------\n"
+ rsStr
+ ("".equals(rsStr) ? "----------------------------\n" : "");
}
/**
* Get the DS infos.
*
* @return The DS infos
*/
public Map<Integer, DSInfo> getReplicaInfos()
{
return replicaInfos;
}
/**
* Get the RS infos.
*
* @return The RS infos
*/
public List<RSInfo> getRsInfos()
{
return rsInfos;
}
}