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