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.util.Collection; 030import java.util.List; 031import java.util.Map; 032import java.util.zip.DataFormatException; 033 034import org.forgerock.opendj.io.ASN1; 035import org.forgerock.opendj.io.ASN1Writer; 036import org.forgerock.opendj.ldap.ByteString; 037import org.forgerock.opendj.ldap.ByteStringBuilder; 038import org.forgerock.opendj.ldap.DecodeException; 039import org.opends.server.core.AddOperation; 040import org.opends.server.core.AddOperationBasis; 041import org.opends.server.core.DirectoryServer; 042import org.opends.server.protocols.internal.InternalClientConnection; 043import org.opends.server.protocols.ldap.LDAPAttribute; 044import org.opends.server.replication.common.CSN; 045import org.opends.server.replication.plugin.EntryHistorical; 046import org.opends.server.types.*; 047import org.opends.server.types.operation.PostOperationAddOperation; 048 049import static org.opends.server.replication.protocol.OperationContext.*; 050 051/** 052 * This class is used to exchange Add operation between LDAP servers 053 * and replication servers. 054 */ 055public class AddMsg extends LDAPUpdateMsg 056{ 057 /** Attributes are managed encoded. */ 058 private byte[] encodedAttributes; 059 060 /** Parent is managed decoded. */ 061 private String parentEntryUUID; 062 063 /** 064 * Creates a new AddMessage. 065 * @param op the operation to use when creating the message 066 */ 067 AddMsg(PostOperationAddOperation op) 068 { 069 super((AddContext) op.getAttachment(SYNCHROCONTEXT), op.getEntryDN()); 070 071 AddContext ctx = (AddContext) op.getAttachment(SYNCHROCONTEXT); 072 073 // Stores parentUniqueID not encoded 074 this.parentEntryUUID = ctx.getParentEntryUUID(); 075 076 // Stores attributes encoded 077 this.encodedAttributes = encodeAttributes(op.getObjectClasses(), 078 op.getUserAttributes(), op.getOperationalAttributes()); 079 } 080 081 /** 082 * Creates a new AddMessage. 083 * 084 * @param csn CSN of the add. 085 * @param dn DN of the added entry. 086 * @param entryUUID The Unique identifier of the added entry. 087 * @param parentEntryUUID The unique Id of the parent of the added 088 * entry. 089 * @param objectClasses objectclass of the added entry. 090 * @param userAttributes user attributes of the added entry. 091 * @param operationalAttributes operational attributes of the added entry. 092 */ 093 public AddMsg(CSN csn, 094 DN dn, 095 String entryUUID, 096 String parentEntryUUID, 097 Map<ObjectClass, String> objectClasses, 098 Map<AttributeType,List<Attribute>> userAttributes, 099 Map<AttributeType,List<Attribute>> operationalAttributes) 100 { 101 super (csn, entryUUID, dn); 102 103 // Stores parentUniqueID not encoded 104 this.parentEntryUUID = parentEntryUUID; 105 106 // Stores attributes encoded 107 this.encodedAttributes = encodeAttributes(objectClasses, userAttributes, 108 operationalAttributes); 109 } 110 111 112 /** 113 * Creates a new AddMessage. 114 * 115 * @param csn CSN of the add. 116 * @param dn DN of the added entry. 117 * @param uniqueId The Unique identifier of the added entry. 118 * @param parentId The unique Id of the parent of the added entry. 119 * @param objectClass objectclass of the added entry. 120 * @param userAttributes user attributes of the added entry. 121 * @param operationalAttributes operational attributes of the added entry. 122 */ 123 public AddMsg(CSN csn, 124 DN dn, 125 String uniqueId, 126 String parentId, 127 Attribute objectClass, 128 Collection<Attribute> userAttributes, 129 Collection<Attribute> operationalAttributes) 130 { 131 super (csn, uniqueId, dn); 132 133 // Stores parentUniqueID not encoded 134 this.parentEntryUUID = parentId; 135 136 // Stores attributes encoded 137 this.encodedAttributes = encodeAttributes(objectClass, userAttributes, 138 operationalAttributes); 139 } 140 141 /** 142 * Creates a new Add message from a byte[]. 143 * 144 * @param in The byte[] from which the operation must be read. 145 * @throws DataFormatException The input byte[] is not a valid AddMsg 146 */ 147 public AddMsg(byte[] in) throws DataFormatException 148 { 149 final ByteArrayScanner scanner = new ByteArrayScanner(in); 150 decodeHeader(scanner, MSG_TYPE_ADD, MSG_TYPE_ADD_V1); 151 152 if (protocolVersion <= 3) 153 { 154 decodeBody_V123(scanner); 155 } 156 else 157 { 158 decodeBody_V4(scanner); 159 } 160 if (protocolVersion==ProtocolVersion.getCurrentVersion()) 161 { 162 bytes = in; 163 } 164 } 165 166 /** {@inheritDoc} */ 167 @Override 168 public AddOperation createOperation( 169 InternalClientConnection connection, DN newDN) 170 throws LDAPException, DecodeException 171 { 172 List<RawAttribute> attr = decodeRawAttributes(encodedAttributes); 173 174 AddOperation add = new AddOperationBasis(connection, 175 InternalClientConnection.nextOperationID(), 176 InternalClientConnection.nextMessageID(), null, 177 ByteString.valueOfUtf8(newDN.toString()), attr); 178 AddContext ctx = new AddContext(getCSN(), getEntryUUID(), parentEntryUUID); 179 add.setAttachment(SYNCHROCONTEXT, ctx); 180 return add; 181 } 182 183 // ============ 184 // Msg encoding 185 // ============ 186 187 /** {@inheritDoc} */ 188 @Override 189 public byte[] getBytes_V1() 190 { 191 final ByteArrayBuilder builder = encodeHeader_V1(MSG_TYPE_ADD_V1); 192 builder.appendString(parentEntryUUID); 193 builder.appendByteArray(encodedAttributes); 194 return builder.toByteArray(); 195 } 196 197 /** {@inheritDoc} */ 198 @Override 199 public byte[] getBytes_V23() 200 { 201 final ByteArrayBuilder builder = 202 encodeHeader(MSG_TYPE_ADD, ProtocolVersion.REPLICATION_PROTOCOL_V3); 203 builder.appendString(parentEntryUUID); 204 builder.appendByteArray(encodedAttributes); 205 return builder.toByteArray(); 206 } 207 208 /** {@inheritDoc} */ 209 @Override 210 public byte[] getBytes_V45(short protocolVersion) 211 { 212 final ByteArrayBuilder builder = 213 encodeHeader(MSG_TYPE_ADD, protocolVersion); 214 builder.appendString(parentEntryUUID); 215 builder.appendIntUTF8(encodedAttributes.length); 216 builder.appendZeroTerminatedByteArray(encodedAttributes); 217 builder.appendIntUTF8(encodedEclIncludes.length); 218 builder.appendZeroTerminatedByteArray(encodedEclIncludes); 219 return builder.toByteArray(); 220 } 221 222 private byte[] encodeAttributes( 223 Map<ObjectClass, String> objectClasses, 224 Map<AttributeType, List<Attribute>> userAttributes, 225 Map<AttributeType, List<Attribute>> operationalAttributes) 226 { 227 ByteStringBuilder byteBuilder = new ByteStringBuilder(); 228 ASN1Writer writer = ASN1.getWriter(byteBuilder); 229 230 try 231 { 232 // Encode the object classes (SET OF LDAPString). 233 AttributeBuilder builder = new AttributeBuilder( 234 DirectoryServer.getObjectClassAttributeType()); 235 builder.addAllStrings(objectClasses.values()); 236 new LDAPAttribute(builder.toAttribute()).write(writer); 237 238 // Encode the user and operational attributes (AttributeList). 239 encodeAttributes(userAttributes, writer); 240 encodeAttributes(operationalAttributes, writer); 241 } 242 catch(Exception e) 243 { 244 // TODO: DO something 245 } 246 247 // Encode the sequence. 248 return byteBuilder.toByteArray(); 249 } 250 251 private void encodeAttributes(Map<AttributeType, List<Attribute>> attributes, 252 ASN1Writer writer) throws Exception 253 { 254 for (List<Attribute> list : attributes.values()) 255 { 256 for (Attribute a : list) 257 { 258 if (!a.isVirtual() && !EntryHistorical.isHistoricalAttribute(a)) 259 { 260 new LDAPAttribute(a).write(writer); 261 } 262 } 263 } 264 } 265 266 private byte[] encodeAttributes( 267 Attribute objectClass, 268 Collection<Attribute> userAttributes, 269 Collection<Attribute> operationalAttributes) 270 { 271 ByteStringBuilder byteBuilder = new ByteStringBuilder(); 272 ASN1Writer writer = ASN1.getWriter(byteBuilder); 273 try 274 { 275 new LDAPAttribute(objectClass).write(writer); 276 277 for (Attribute a : userAttributes) 278 { 279 new LDAPAttribute(a).write(writer); 280 } 281 282 if (operationalAttributes != null) 283 { 284 for (Attribute a : operationalAttributes) 285 { 286 new LDAPAttribute(a).write(writer); 287 } 288 } 289 } 290 catch(Exception e) 291 { 292 // Do something 293 } 294 return byteBuilder.toByteArray(); 295 } 296 297 // ============ 298 // Msg decoding 299 // ============ 300 301 private void decodeBody_V123(ByteArrayScanner scanner) 302 throws DataFormatException 303 { 304 parentEntryUUID = scanner.nextString(); 305 encodedAttributes = scanner.remainingBytes(); 306 } 307 308 private void decodeBody_V4(ByteArrayScanner scanner) 309 throws DataFormatException 310 { 311 parentEntryUUID = scanner.nextString(); 312 313 final int attrLen = scanner.nextIntUTF8(); 314 encodedAttributes = scanner.nextByteArray(attrLen); 315 scanner.skipZeroSeparator(); 316 317 final int eclAttrLen = scanner.nextIntUTF8(); 318 encodedEclIncludes = scanner.nextByteArray(eclAttrLen); 319 } 320 321 /** {@inheritDoc} */ 322 @Override 323 public String toString() 324 { 325 if (protocolVersion >= ProtocolVersion.REPLICATION_PROTOCOL_V1) 326 { 327 return "AddMsg content: " + 328 " protocolVersion: " + protocolVersion + 329 " dn: " + dn + 330 " csn: " + csn + 331 " uniqueId: " + entryUUID + 332 " assuredFlag: " + assuredFlag + 333 (protocolVersion >= ProtocolVersion.REPLICATION_PROTOCOL_V2 ? 334 " assuredMode: " + assuredMode + 335 " safeDataLevel: " + safeDataLevel 336 : ""); 337 } 338 return "!!! Unknown version: " + protocolVersion + "!!!"; 339 } 340 341 /** 342 * Add the specified attribute/attribute value in the entry contained 343 * in this AddMsg. 344 * 345 * @param name The name of the attribute to add. 346 * @param value The value of the attribute to add. 347 * @throws DecodeException When this Msg is not valid. 348 */ 349 public void addAttribute(String name, String value) throws DecodeException 350 { 351 ByteStringBuilder byteBuilder = new ByteStringBuilder(); 352 byteBuilder.appendBytes(encodedAttributes); 353 354 ASN1Writer writer = ASN1.getWriter(byteBuilder); 355 356 try 357 { 358 new LDAPAttribute(name, value).write(writer); 359 360 encodedAttributes = byteBuilder.toByteArray(); 361 } 362 catch(Exception e) 363 { 364 // DO SOMETHING 365 } 366 } 367 368 /** 369 * Get the attributes of this add msg. 370 * @throws LDAPException In case of LDAP decoding exception 371 * @throws DecodeException In case of ASN1 decoding exception 372 * @return the list of attributes 373 */ 374 public List<Attribute> getAttributes() throws LDAPException, DecodeException 375 { 376 return decodeAttributes(encodedAttributes); 377 } 378 379 /** 380 * Set the parent unique id of this add msg. 381 * 382 * @param entryUUID the parent unique id. 383 */ 384 public void setParentEntryUUID(String entryUUID) 385 { 386 parentEntryUUID = entryUUID; 387 } 388 389 /** 390 * Get the parent unique id of this add msg. 391 * @return the parent unique id. 392 */ 393 public String getParentEntryUUID() 394 { 395 return parentEntryUUID; 396 } 397 398 /** {@inheritDoc} */ 399 @Override 400 public int size() 401 { 402 return encodedAttributes.length + encodedEclIncludes.length + headerSize(); 403 } 404 405}