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-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2011-2016 ForgeRock AS. 016 */ 017package org.opends.server.replication.protocol; 018 019import static org.opends.server.replication.protocol.OperationContext.*; 020import static org.opends.server.replication.protocol.ProtocolVersion.*; 021 022import java.io.IOException; 023import java.util.List; 024import java.util.zip.DataFormatException; 025 026import org.forgerock.i18n.LocalizedIllegalArgumentException; 027import org.forgerock.opendj.ldap.ByteString; 028import org.forgerock.opendj.ldap.DN; 029import org.forgerock.opendj.ldap.RDN; 030import org.opends.server.core.ModifyDNOperation; 031import org.opends.server.core.ModifyDNOperationBasis; 032import org.opends.server.protocols.internal.InternalClientConnection; 033import org.opends.server.replication.common.CSN; 034import org.opends.server.types.LDAPException; 035import org.opends.server.types.Modification; 036import org.opends.server.types.operation.PostOperationModifyDNOperation; 037 038/** Message used to send Modify DN information. */ 039public class ModifyDNMsg extends ModifyCommonMsg 040{ 041 private String newRDN; 042 private String newSuperior; 043 private boolean deleteOldRdn; 044 private String newSuperiorEntryUUID; 045 046 /** 047 * Construct a new Modify DN message. 048 * 049 * @param operation The operation to use for building the message 050 */ 051 public ModifyDNMsg(PostOperationModifyDNOperation operation) 052 { 053 super((OperationContext) operation.getAttachment(SYNCHROCONTEXT), 054 operation.getEntryDN()); 055 056 encodedMods = encodeMods(operation.getModifications()); 057 058 ModifyDnContext ctx = 059 (ModifyDnContext) operation.getAttachment(SYNCHROCONTEXT); 060 newSuperiorEntryUUID = ctx.getNewSuperiorEntryUUID(); 061 062 deleteOldRdn = operation.deleteOldRDN(); 063 final ByteString rawNewSuperior = operation.getRawNewSuperior(); 064 newSuperior = rawNewSuperior != null ? rawNewSuperior.toString() : null; 065 newRDN = operation.getRawNewRDN().toString(); 066 } 067 068 /** 069 * Construct a new Modify DN message (no mods). 070 * Note: Keep this constructor version to support already written tests, not 071 * using mods. 072 * 073 * @param dn The dn to use for building the message. 074 * @param csn The CSN to use for building the message. 075 * @param entryUUID The unique id to use for building the message. 076 * @param newSuperiorEntryUUID The new parent unique id to use for building 077 * the message. 078 * @param deleteOldRdn boolean indicating if old rdn must be deleted to use 079 * for building the message. 080 * @param newSuperior The new Superior entry to use for building the message. 081 * @param newRDN The new Rdn to use for building the message. 082 */ 083 public ModifyDNMsg(DN dn, CSN csn, String entryUUID, 084 String newSuperiorEntryUUID, boolean deleteOldRdn, 085 String newSuperior, String newRDN) 086 { 087 super(new ModifyDnContext(csn, entryUUID, newSuperiorEntryUUID), dn); 088 089 this.newSuperiorEntryUUID = newSuperiorEntryUUID; 090 this.deleteOldRdn = deleteOldRdn; 091 this.newSuperior = newSuperior; 092 this.newRDN = newRDN; 093 } 094 095 /** 096 * Construct a new Modify DN message (with mods). 097 * 098 * @param dn The dn to use for building the message. 099 * @param csn The CSNto use for building the message. 100 * @param entryUUID The unique id to use for building the message. 101 * @param newSuperiorEntryUUID The new parent unique id to use for building 102 * the message. 103 * @param deleteOldRdn boolean indicating if old rdn must be deleted to use 104 * for building the message. 105 * @param newSuperior The new Superior entry to use for building the message. 106 * @param newRDN The new Rdn to use for building the message. 107 * @param mods The mod of the operation. 108 */ 109 public ModifyDNMsg(DN dn, CSN csn, String entryUUID, 110 String newSuperiorEntryUUID, boolean deleteOldRdn, String newSuperior, 111 String newRDN, List<Modification> mods) 112 { 113 this(dn, csn, entryUUID, newSuperiorEntryUUID, deleteOldRdn, 114 newSuperior, newRDN); 115 this.encodedMods = encodeMods(mods); 116 } 117 118 /** 119 * Creates a new ModifyDN message from a byte[]. 120 * 121 * @param in The byte[] from which the operation must be read. 122 * @throws DataFormatException The input byte[] is not a valid ModifyDNMsg. 123 */ 124 ModifyDNMsg(byte[] in) throws DataFormatException 125 { 126 final ByteArrayScanner scanner = new ByteArrayScanner(in); 127 decodeHeader(scanner, MSG_TYPE_MODIFYDN, MSG_TYPE_MODIFYDN_V1); 128 129 if (protocolVersion <= 3) 130 { 131 decodeBody_V123(scanner, in[0]); 132 } 133 else 134 { 135 decodeBody_V4(scanner); 136 } 137 138 if (protocolVersion==ProtocolVersion.getCurrentVersion()) 139 { 140 bytes = in; 141 } 142 } 143 144 @Override 145 public ModifyDNOperation createOperation(InternalClientConnection connection, 146 DN newDN) throws LDAPException, IOException 147 { 148 ModifyDNOperation moddn = new ModifyDNOperationBasis(connection, 149 InternalClientConnection.nextOperationID(), 150 InternalClientConnection.nextMessageID(), null, 151 ByteString.valueOfUtf8(newDN.toString()), 152 ByteString.valueOfUtf8(newRDN), 153 deleteOldRdn, 154 (newSuperior == null ? null : ByteString.valueOfUtf8(newSuperior))); 155 156 for (Modification mod : decodeMods(encodedMods)) 157 { 158 moddn.addModification(mod); 159 } 160 161 ModifyDnContext ctx = new ModifyDnContext(getCSN(), getEntryUUID(), 162 newSuperiorEntryUUID); 163 moddn.setAttachment(SYNCHROCONTEXT, ctx); 164 return moddn; 165 } 166 167 // ============ 168 // Msg Encoding 169 // ============ 170 171 @Override 172 public byte[] getBytes_V1() 173 { 174 final ByteArrayBuilder builder = encodeHeader_V1(MSG_TYPE_MODIFYDN_V1); 175 builder.appendString(newRDN); 176 builder.appendString(newSuperior); 177 builder.appendString(newSuperiorEntryUUID); 178 builder.appendBoolean(deleteOldRdn); 179 return builder.toByteArray(); 180 } 181 182 @Override 183 public byte[] getBytes_V23() 184 { 185 final ByteArrayBuilder builder = encodeHeader(MSG_TYPE_MODIFYDN, REPLICATION_PROTOCOL_V3); 186 builder.appendString(newRDN); 187 builder.appendString(newSuperior); 188 builder.appendString(newSuperiorEntryUUID); 189 builder.appendBoolean(deleteOldRdn); 190 builder.appendZeroTerminatedByteArray(encodedMods); 191 return builder.toByteArray(); 192 } 193 194 @Override 195 public byte[] getBytes_V45(short protocolVersion) 196 { 197 final ByteArrayBuilder builder = encodeHeader(MSG_TYPE_MODIFYDN, protocolVersion); 198 builder.appendString(newRDN); 199 builder.appendString(newSuperior); 200 builder.appendString(newSuperiorEntryUUID); 201 builder.appendBoolean(deleteOldRdn); 202 builder.appendIntUTF8(encodedMods.length); 203 builder.appendZeroTerminatedByteArray(encodedMods); 204 builder.appendIntUTF8(encodedEclIncludes.length); 205 builder.appendZeroTerminatedByteArray(encodedEclIncludes); 206 return builder.toByteArray(); 207 } 208 209 // ============ 210 // Msg decoding 211 // ============ 212 213 private void decodeBody_V123(ByteArrayScanner scanner, byte msgType) 214 throws DataFormatException 215 { 216 newRDN = scanner.nextString(); 217 newSuperior = scanner.nextString(); 218 newSuperiorEntryUUID = scanner.nextString(); 219 deleteOldRdn = scanner.nextBoolean(); 220 221 // For easiness (no additional method), simply compare PDU type to 222 // know if we have to read the mods of V2 223 if (msgType == MSG_TYPE_MODIFYDN) 224 { 225 encodedMods = scanner.remainingBytesZeroTerminated(); 226 } 227 } 228 229 private void decodeBody_V4(ByteArrayScanner scanner) 230 throws DataFormatException 231 { 232 newRDN = scanner.nextString(); 233 newSuperior = scanner.nextString(); 234 newSuperiorEntryUUID = scanner.nextString(); 235 deleteOldRdn = scanner.nextBoolean(); 236 237 final int modsLen = scanner.nextIntUTF8(); 238 encodedMods = scanner.nextByteArray(modsLen); 239 scanner.skipZeroSeparator(); 240 241 final int eclAttrLen = scanner.nextIntUTF8(); 242 encodedEclIncludes = scanner.nextByteArray(eclAttrLen); 243 } 244 245 @Override 246 public String toString() 247 { 248 if (protocolVersion >= REPLICATION_PROTOCOL_V1) 249 { 250 return "ModifyDNMsg content: " + 251 " protocolVersion: " + protocolVersion + 252 " dn: " + dn + 253 " csn: " + csn + 254 " uniqueId: " + entryUUID + 255 " newRDN: " + newRDN + 256 " newSuperior: " + newSuperior + 257 " deleteOldRdn: " + deleteOldRdn + 258 " assuredFlag: " + assuredFlag + 259 (protocolVersion >= REPLICATION_PROTOCOL_V2 ? 260 " assuredMode: " + assuredMode + 261 " safeDataLevel: " + safeDataLevel 262 : ""); 263 } 264 return "!!! Unknown version: " + protocolVersion + "!!!"; 265 } 266 267 /** 268 * Set the new superior. 269 * @param string the new superior. 270 */ 271 public void setNewSuperior(String string) 272 { 273 newSuperior = string; 274 } 275 276 /** 277 * Get the new superior. 278 * 279 * @return The new superior. 280 */ 281 public String getNewSuperior() 282 { 283 return newSuperior; 284 } 285 286 /** 287 * Get the new superior id. 288 * 289 * @return The new superior id. 290 */ 291 public String getNewSuperiorEntryUUID() 292 { 293 return newSuperiorEntryUUID; 294 } 295 296 /** 297 * Get the delete old rdn option. 298 * 299 * @return The delete old rdn option. 300 */ 301 public boolean deleteOldRdn() 302 { 303 return deleteOldRdn; 304 } 305 306 /** 307 * Set the new superior id. 308 * 309 * @param newSup The new superior id. 310 */ 311 public void setNewSuperiorEntryUUID(String newSup) 312 { 313 newSuperiorEntryUUID = newSup; 314 } 315 316 /** 317 * Set the delete old rdn option. 318 * 319 * @param delete The delete old rdn option. 320 */ 321 public void setDeleteOldRdn(boolean delete) 322 { 323 deleteOldRdn = delete; 324 } 325 326 /** 327 * Get the delete old rdn option. 328 * @return true if delete old rdn option 329 */ 330 public boolean getDeleteOldRdn() 331 { 332 return deleteOldRdn; 333 } 334 335 /** 336 * Get the new RDN of this operation. 337 * 338 * @return The new RDN of this operation. 339 */ 340 public String getNewRDN() 341 { 342 return newRDN; 343 } 344 345 /** 346 * Set the new RDN of this operation. 347 * @param newRDN the new RDN of this operation. 348 */ 349 public void setNewRDN(String newRDN) 350 { 351 this.newRDN = newRDN; 352 } 353 354 /** 355 * Computes and return the new DN that the entry should have after this operation. 356 * 357 * @return the newDN. 358 * @throws LocalizedIllegalArgumentException 359 * in case of decoding problems. 360 */ 361 private DN computeNewDN() throws LocalizedIllegalArgumentException 362 { 363 if (newSuperior != null) 364 { 365 return DN.valueOf(newRDN + "," + newSuperior); 366 } 367 final DN parentDn = getDN().parent(); 368 return parentDn.child(RDN.valueOf(newRDN)); 369 } 370 371 /** 372 * Check if this MSG will change the DN of the target entry to be 373 * the same as the dn given as a parameter. 374 * @param targetDn the DN to use when checking if this MSG will change 375 * the DN of the entry to a given DN. 376 * @return A boolean indicating if the modify DN MSG will change the DN of 377 * the target entry to be the same as the dn given as a parameter. 378 */ 379 public boolean newDNIsParent(DN targetDn) 380 { 381 try 382 { 383 DN newDN = computeNewDN(); 384 return newDN.isSuperiorOrEqualTo(targetDn); 385 } 386 catch (LocalizedIllegalArgumentException e) 387 { 388 // The DN was not a correct DN, and therefore does not a parent of the 389 // DN given as a parameter. 390 return false; 391 } 392 } 393 394 /** 395 * Check if the new dn of this ModifyDNMsg is the same as the targetDN 396 * given in parameter. 397 * 398 * @param targetDN The targetDN to use to check for equality. 399 * 400 * @return A boolean indicating if the targetDN if the same as the new DN of 401 * the ModifyDNMsg. 402 */ 403 public boolean newDNIsEqual(DN targetDN) 404 { 405 try 406 { 407 DN newDN = computeNewDN(); 408 return newDN.equals(targetDN); 409 } 410 catch (LocalizedIllegalArgumentException e) 411 { 412 // The DN was not a correct DN, and therefore does not match the 413 // DN given as a parameter. 414 return false; 415 } 416 } 417 418 /** 419 * Check if the new parent of the modifyDNMsg is the same as the targetDN 420 * given in parameter. 421 * 422 * @param targetDN the targetDN to use when checking equality. 423 * 424 * @return A boolean indicating if the new parent of the modifyDNMsg is the 425 * same as the targetDN. 426 */ 427 public boolean newParentIsEqual(DN targetDN) 428 { 429 try 430 { 431 DN newSuperiorDN = newSuperior != null ? DN.valueOf(newSuperior) : DN.rootDN(); 432 return newSuperiorDN.equals(targetDN); 433 } 434 catch (LocalizedIllegalArgumentException e) 435 { 436 // The newsuperior was not a correct DN, and therefore does not match the 437 // DN given as a parameter. 438 return false; 439 } 440 } 441 442 @Override 443 public int size() 444 { 445 return encodedMods.length + newRDN.length() + 446 encodedEclIncludes.length + headerSize(); 447 } 448}