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 2008-2009 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2015 ForgeRock AS 026 */ 027package org.opends.server.replication.protocol; 028 029import java.util.*; 030import java.util.zip.DataFormatException; 031 032import org.forgerock.opendj.io.ASN1; 033import org.forgerock.opendj.io.ASN1Reader; 034import org.forgerock.opendj.io.ASN1Writer; 035import org.opends.server.replication.common.AssuredMode; 036import org.opends.server.replication.common.ServerStatus; 037import org.forgerock.opendj.ldap.ByteSequenceReader; 038import org.forgerock.opendj.ldap.ByteString; 039import org.forgerock.opendj.ldap.ByteStringBuilder; 040import org.forgerock.util.Utils; 041 042/** 043 * This message is used by DS to confirm a RS he wants to connect to him (open 044 * a session): 045 * Handshake sequence between DS and RS is like this: 046 * DS --- ServerStartMsg ---> RS 047 * DS <--- ReplServerStartMsg --- RS 048 * DS --- StartSessionMsg ---> RS 049 * DS <--- TopologyMsg --- RS 050 * 051 * This message contains: 052 * - status: the status we are entering the topology with 053 * - referrals URLs: the referrals URLs we allow peer DSs to use to refer to 054 * our domain when needed. 055 */ 056public class StartSessionMsg extends ReplicationMsg 057{ 058 /** The list of referrals URLs to the sending DS. */ 059 private final List<String> referralsURLs = new ArrayList<>(); 060 /** The initial status the DS starts with. */ 061 private ServerStatus status = ServerStatus.INVALID_STATUS; 062 /** Assured replication enabled on DS or not. */ 063 private boolean assuredFlag; 064 /** DS assured mode (relevant if assured replication enabled). */ 065 private AssuredMode assuredMode = AssuredMode.SAFE_DATA_MODE; 066 /** DS safe data level (relevant if assured mode is safe data). */ 067 private byte safeDataLevel = 1; 068 069 private Set<String> eclIncludes = new HashSet<>(); 070 private Set<String> eclIncludesForDeletes = new HashSet<>(); 071 072 /** 073 * Creates a new StartSessionMsg message from its encoded form. 074 * 075 * @param in The byte array containing the encoded form of the message. 076 * @param version The protocol version to use to decode the msg. 077 * @throws java.util.zip.DataFormatException If the byte array does not 078 * contain a valid encoded form of the message. 079 */ 080 StartSessionMsg(byte[] in, short version) throws DataFormatException 081 { 082 if (version <= ProtocolVersion.REPLICATION_PROTOCOL_V3) 083 { 084 decode_V23(in); 085 } 086 else 087 { 088 decode_V45(in, version); 089 } 090 } 091 092 /** 093 * Creates a new message with the given required parameters. 094 * @param status Status we are starting with 095 * @param referralsURLs Referrals URLs to be used by peer DSs 096 * @param assuredFlag If assured mode is enabled or not 097 * @param assuredMode Assured type 098 * @param safeDataLevel Assured mode safe data level 099 */ 100 public StartSessionMsg(ServerStatus status, Collection<String> referralsURLs, 101 boolean assuredFlag, AssuredMode assuredMode, byte safeDataLevel) 102 { 103 this.referralsURLs.addAll(referralsURLs); 104 this.status = status; 105 this.assuredFlag = assuredFlag; 106 this.assuredMode = assuredMode; 107 this.safeDataLevel = safeDataLevel; 108 } 109 110 // ============ 111 // Msg encoding 112 // ============ 113 114 /** {@inheritDoc} */ 115 @Override 116 public byte[] getBytes(short protocolVersion) 117 { 118 if (protocolVersion <= ProtocolVersion.REPLICATION_PROTOCOL_V3) 119 { 120 return getBytes_V23(); 121 } 122 else 123 { 124 return getBytes_V45(protocolVersion); 125 } 126 } 127 128 private byte[] getBytes_V45(short version) 129 { 130 try 131 { 132 ByteStringBuilder byteBuilder = new ByteStringBuilder(); 133 ASN1Writer writer = ASN1.getWriter(byteBuilder); 134 135 byteBuilder.appendByte(MSG_TYPE_START_SESSION); 136 byteBuilder.appendByte(status.getValue()); 137 byteBuilder.appendByte(assuredFlag ? 1 : 0); 138 byteBuilder.appendByte(assuredMode.getValue()); 139 byteBuilder.appendByte(safeDataLevel); 140 141 writer.writeStartSequence(); 142 for (String url : referralsURLs) 143 { 144 writer.writeOctetString(url); 145 } 146 writer.writeEndSequence(); 147 148 writer.writeStartSequence(); 149 for (String attrDef : eclIncludes) 150 { 151 writer.writeOctetString(attrDef); 152 } 153 writer.writeEndSequence(); 154 155 if (version >= ProtocolVersion.REPLICATION_PROTOCOL_V5) 156 { 157 writer.writeStartSequence(); 158 for (String attrDef : eclIncludesForDeletes) 159 { 160 writer.writeOctetString(attrDef); 161 } 162 writer.writeEndSequence(); 163 } 164 165 return byteBuilder.toByteArray(); 166 } 167 catch (Exception e) 168 { 169 throw new RuntimeException(e); 170 } 171 } 172 173 private byte[] getBytes_V23() 174 { 175 /* 176 * The message is stored in the form: 177 * <message type><status><assured flag><assured mode><safe data level> 178 * <list of referrals urls> 179 * (each referral url terminates with 0) 180 */ 181 final ByteArrayBuilder builder = new ByteArrayBuilder(); 182 builder.appendByte(MSG_TYPE_START_SESSION); 183 builder.appendByte(status.getValue()); 184 builder.appendBoolean(assuredFlag); 185 builder.appendByte(assuredMode.getValue()); 186 builder.appendByte(safeDataLevel); 187 188 if (referralsURLs.size() >= 1) 189 { 190 for (String url : referralsURLs) 191 { 192 builder.appendString(url); 193 } 194 } 195 return builder.toByteArray(); 196 } 197 198 // ============ 199 // Msg decoding 200 // ============ 201 202 private void decode_V45(byte[] in, short version) throws DataFormatException 203 { 204 ByteSequenceReader reader = ByteString.wrap(in).asReader(); 205 try 206 { 207 if (reader.readByte() != MSG_TYPE_START_SESSION) 208 { 209 throw new DataFormatException("input is not a valid " 210 + getClass().getCanonicalName()); 211 } 212 213 /* 214 status = ServerStatus.valueOf(asn1Reader.readOctetString().byteAt(0)); 215 assuredFlag = (asn1Reader.readOctetString().byteAt(0) == 1); 216 assuredMode=AssuredMode.valueOf((asn1Reader.readOctetString().byteAt(0))); 217 safeDataLevel = asn1Reader.readOctetString().byteAt(0); 218 */ 219 status = ServerStatus.valueOf(reader.readByte()); 220 assuredFlag = reader.readByte() == 1; 221 assuredMode = AssuredMode.valueOf(reader.readByte()); 222 safeDataLevel = reader.readByte(); 223 224 ASN1Reader asn1Reader = ASN1.getReader(reader); 225 226 asn1Reader.readStartSequence(); 227 while(asn1Reader.hasNextElement()) 228 { 229 String s = asn1Reader.readOctetStringAsString(); 230 this.referralsURLs.add(s); 231 } 232 asn1Reader.readEndSequence(); 233 234 asn1Reader.readStartSequence(); 235 while(asn1Reader.hasNextElement()) 236 { 237 String s = asn1Reader.readOctetStringAsString(); 238 this.eclIncludes.add(s); 239 } 240 asn1Reader.readEndSequence(); 241 242 if (version >= ProtocolVersion.REPLICATION_PROTOCOL_V5) 243 { 244 asn1Reader.readStartSequence(); 245 while (asn1Reader.hasNextElement()) 246 { 247 this.eclIncludesForDeletes.add(asn1Reader.readOctetStringAsString()); 248 } 249 asn1Reader.readEndSequence(); 250 } 251 else 252 { 253 // Default to using the same set of attributes for deletes. 254 this.eclIncludesForDeletes.addAll(eclIncludes); 255 } 256 } 257 catch (Exception e) 258 { 259 throw new RuntimeException(e); 260 } 261 } 262 263 private void decode_V23(byte[] in) throws DataFormatException 264 { 265 /* 266 * The message is stored in the form: 267 * <message type><status><assured flag><assured mode><safe data level> 268 * <list of referrals urls> 269 * (each referral url terminates with 0) 270 */ 271 final ByteArrayScanner scanner = new ByteArrayScanner(in); 272 final byte msgType = scanner.nextByte(); 273 if (msgType != MSG_TYPE_START_SESSION) 274 { 275 throw new DataFormatException( 276 "Input is not a valid " + getClass().getCanonicalName()); 277 } 278 279 status = ServerStatus.valueOf(scanner.nextByte()); 280 assuredFlag = scanner.nextBoolean(); 281 assuredMode = AssuredMode.valueOf(scanner.nextByte()); 282 safeDataLevel = scanner.nextByte(); 283 284 while (!scanner.isEmpty()) 285 { 286 referralsURLs.add(scanner.nextString()); 287 } 288 } 289 290 /** 291 * Get the list of referrals URLs. 292 * 293 * @return The list of referrals URLs. 294 */ 295 public List<String> getReferralsURLs() 296 { 297 return referralsURLs; 298 } 299 300 /** 301 * Get the status from this message. 302 * @return The status. 303 */ 304 public ServerStatus getStatus() 305 { 306 return status; 307 } 308 309 /** {@inheritDoc} */ 310 @Override 311 public String toString() 312 { 313 String urls = Utils.joinAsString(" | ", referralsURLs); 314 return "StartSessionMsg content:\nstatus: " + status + 315 "\nassuredFlag: " + assuredFlag + 316 "\nassuredMode: " + assuredMode + 317 "\nsafeDataLevel: " + safeDataLevel + 318 "\nreferralsURLs: " + urls + 319 "\nEclIncludes " + eclIncludes + 320 "\nEclIncludeForDeletes: " + eclIncludesForDeletes; 321 } 322 323 /** 324 * Returns true if assured mode is enabled. 325 * @return true if assured mode is enabled. 326 */ 327 public boolean isAssured() 328 { 329 return assuredFlag; 330 } 331 332 /** 333 * Get the assured mode. 334 * @return the assured mode. 335 */ 336 public AssuredMode getAssuredMode() 337 { 338 return assuredMode; 339 } 340 341 /** 342 * Get the safe data level. 343 * @return the safe data level. 344 */ 345 public byte getSafeDataLevel() 346 { 347 return safeDataLevel; 348 } 349 350 /** 351 * Set the attributes configured on a server to be included in the ECL. 352 * 353 * @param includeAttributes 354 * attributes to be included with all change records. 355 * @param includeAttributesForDeletes 356 * additional attributes to be included with delete change records. 357 */ 358 public void setEclIncludes( 359 Set<String> includeAttributes, 360 Set<String> includeAttributesForDeletes) 361 { 362 if (includeAttributes != null) 363 { 364 eclIncludes = includeAttributes; 365 } 366 367 if (includeAttributesForDeletes != null) 368 { 369 eclIncludesForDeletes = includeAttributesForDeletes; 370 } 371 } 372 373 /** 374 * Get the attributes to include in each change for the ECL. 375 * 376 * @return The attributes to include in each change for the ECL. 377 */ 378 public Set<String> getEclIncludes() 379 { 380 return eclIncludes; 381 } 382 383 384 385 /** 386 * Get the attributes to include in each delete change for the ECL. 387 * 388 * @return The attributes to include in each delete change for the ECL. 389 */ 390 public Set<String> getEclIncludesForDeletes() 391 { 392 return eclIncludesForDeletes; 393 } 394 395}