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