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