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