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 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.replication.protocol; 028 029 import org.opends.server.core.AddOperationBasis; 030 import org.opends.server.core.DirectoryServer; 031 import org.opends.server.protocols.asn1.ASN1Element; 032 import org.opends.server.protocols.asn1.ASN1Exception; 033 import org.opends.server.protocols.asn1.ASN1OctetString; 034 035 import java.io.UnsupportedEncodingException; 036 import java.util.ArrayList; 037 import java.util.Collection; 038 import java.util.LinkedHashSet; 039 import java.util.List; 040 import java.util.zip.DataFormatException; 041 042 import org.opends.server.protocols.internal.InternalClientConnection; 043 import org.opends.server.protocols.ldap.LDAPAttribute; 044 import org.opends.server.replication.common.ChangeNumber; 045 import org.opends.server.types.AbstractOperation; 046 import org.opends.server.types.Attribute; 047 import org.opends.server.types.AttributeValue; 048 import org.opends.server.types.LDAPException; 049 import org.opends.server.types.RawAttribute; 050 import org.opends.server.types.operation.PostOperationAddOperation; 051 052 import static org.opends.server.replication.protocol.OperationContext.*; 053 import static org.opends.server.util.StaticUtils.toLowerCase; 054 055 /** 056 * This class is used to exchange Add operation between LDAP servers 057 * and replication servers. 058 */ 059 public class AddMsg extends UpdateMessage 060 { 061 private static final long serialVersionUID = -4905520652801395185L; 062 private byte[] encodedAttributes; 063 private String parentUniqueId; 064 065 /** 066 * Creates a new AddMessage. 067 * @param op the operation to use when creating the message 068 */ 069 public AddMsg(PostOperationAddOperation op) 070 { 071 super((AddContext) op.getAttachment(SYNCHROCONTEXT), 072 op.getRawEntryDN().stringValue()); 073 074 AddContext ctx = (AddContext) op.getAttachment(SYNCHROCONTEXT); 075 this.parentUniqueId = ctx.getParentUid(); 076 077 // Encode the object classes (SET OF LDAPString). 078 LinkedHashSet<AttributeValue> ocValues = 079 new LinkedHashSet<AttributeValue>(op.getObjectClasses().size()); 080 for (String s : op.getObjectClasses().values()) 081 { 082 ocValues.add(new AttributeValue(new ASN1OctetString(s), 083 new ASN1OctetString(toLowerCase(s)))); 084 } 085 086 Attribute attr = new Attribute( 087 DirectoryServer.getObjectClassAttributeType(), 088 "objectClass", ocValues); 089 090 ArrayList<ASN1Element> elems = new ArrayList<ASN1Element>(); 091 092 elems.add(new LDAPAttribute(attr).encode()); 093 094 // Encode the user attributes (AttributeList). 095 for (List<Attribute> list : op.getUserAttributes().values()) 096 { 097 for (Attribute a : list) 098 { 099 elems.add(new LDAPAttribute(a).encode()); 100 } 101 } 102 103 // Encode the operational attributes (AttributeList). 104 for (List<Attribute> list : op.getOperationalAttributes().values()) 105 { 106 for (Attribute a : list) 107 { 108 elems.add(new LDAPAttribute(a).encode()); 109 } 110 } 111 112 // Encode the sequence. 113 encodedAttributes = ASN1Element.encodeValue(elems); 114 } 115 116 /** 117 * Creates a new AddMessage. 118 * 119 * @param cn ChangeNumber of the add. 120 * @param dn DN of the added entry. 121 * @param uniqueId The Unique identifier of the added entry. 122 * @param parentId The unique Id of the parent of the added entry. 123 * @param objectClass objectclass of the added entry. 124 * @param userAttributes user attributes of the added entry. 125 * @param operationalAttributes operational attributes of the added entry. 126 */ 127 public AddMsg(ChangeNumber cn, 128 String dn, 129 String uniqueId, 130 String parentId, 131 Attribute objectClass, 132 Collection<Attribute> userAttributes, 133 Collection<Attribute> operationalAttributes) 134 { 135 super (new AddContext(cn, uniqueId, parentId), dn); 136 this.parentUniqueId = parentId; 137 138 ArrayList<ASN1Element> elems = new ArrayList<ASN1Element>(); 139 elems.add(new LDAPAttribute(objectClass).encode()); 140 141 for (Attribute a : userAttributes) 142 elems.add(new LDAPAttribute(a).encode()); 143 144 if (operationalAttributes != null) 145 for (Attribute a : operationalAttributes) 146 elems.add(new LDAPAttribute(a).encode()); 147 148 encodedAttributes = ASN1Element.encodeValue(elems); 149 } 150 151 /** 152 * Creates a new Add message from a byte[]. 153 * 154 * @param in The byte[] from which the operation must be read. 155 * @throws DataFormatException The input byte[] is not a valid AddMsg 156 * @throws UnsupportedEncodingException If UTF8 is not supported by the jvm 157 */ 158 public AddMsg(byte[] in) throws DataFormatException, 159 UnsupportedEncodingException 160 { 161 super(in); 162 163 int pos = decodeHeader(MSG_TYPE_ADD_REQUEST, in); 164 165 // read the parent unique Id 166 int length = getNextLength(in, pos); 167 if (length != 0) 168 { 169 parentUniqueId = new String(in, pos, length, "UTF-8"); 170 pos += length + 1; 171 } 172 else 173 { 174 parentUniqueId = null; 175 pos += 1; 176 } 177 178 // Read the attributes : all the remaining bytes 179 encodedAttributes = new byte[in.length-pos]; 180 int i =0; 181 while (pos<in.length) 182 { 183 encodedAttributes[i++] = in[pos++]; 184 } 185 } 186 187 /** 188 * {@inheritDoc} 189 */ 190 @Override 191 public AbstractOperation createOperation( 192 InternalClientConnection connection, String newDn) 193 throws LDAPException, ASN1Exception 194 { 195 ArrayList<RawAttribute> attr = new ArrayList<RawAttribute>(); 196 ArrayList<ASN1Element> elems; 197 198 elems = ASN1Element.decodeElements(encodedAttributes); 199 for (ASN1Element elem : elems) 200 { 201 attr.add(LDAPAttribute.decode(elem)); 202 } 203 204 AddOperationBasis add = new AddOperationBasis(connection, 205 InternalClientConnection.nextOperationID(), 206 InternalClientConnection.nextMessageID(), null, 207 new ASN1OctetString(newDn), attr); 208 AddContext ctx = new AddContext(getChangeNumber(), getUniqueId(), 209 parentUniqueId); 210 add.setAttachment(SYNCHROCONTEXT, ctx); 211 return add; 212 } 213 214 /** 215 * Get the byte[] representation of this Message. 216 * 217 * @return the byte array representation of this Message. 218 * 219 * @throws UnsupportedEncodingException When the encoding of the message 220 * failed because the UTF-8 encoding is not supported. 221 */ 222 @Override 223 public byte[] getBytes() throws UnsupportedEncodingException 224 { 225 int length = encodedAttributes.length; 226 byte[] byteParentId = null; 227 if (parentUniqueId != null) 228 { 229 byteParentId = parentUniqueId.getBytes("UTF-8"); 230 length += byteParentId.length + 1; 231 } 232 else 233 { 234 length += 1; 235 } 236 237 /* encode the header in a byte[] large enough to also contain the mods */ 238 byte [] resultByteArray = encodeHeader(MSG_TYPE_ADD_REQUEST, length); 239 240 int pos = resultByteArray.length - length; 241 242 if (byteParentId != null) 243 pos = addByteArray(byteParentId, resultByteArray, pos); 244 else 245 resultByteArray[pos++] = 0; 246 247 /* put the attributes */ 248 for (int i=0; i<encodedAttributes.length; i++,pos++) 249 { 250 resultByteArray[pos] = encodedAttributes[i]; 251 } 252 return resultByteArray; 253 } 254 255 /** 256 * {@inheritDoc} 257 */ 258 @Override 259 public String toString() 260 { 261 return ("ADD DN=(" + getDn() + ") CN=(" + getChangeNumber() + ")"); 262 } 263 264 /** 265 * Add the specified attribute/attribute value in the entry contained 266 * in this AddMsg. 267 * 268 * @param name The name of the attribute to add. 269 * @param value The value of the attribute to add. 270 * @throws ASN1Exception When this Msg is not valid. 271 */ 272 public void addAttribute(String name, String value) 273 throws ASN1Exception 274 { 275 RawAttribute newAttr = new LDAPAttribute(name, value); 276 ArrayList<ASN1Element> elems; 277 elems = ASN1Element.decodeElements(encodedAttributes); 278 elems.add(newAttr.encode()); 279 encodedAttributes = ASN1Element.encodeValue(elems); 280 } 281 282 /** 283 * Set the parent unique id of this add msg. 284 * 285 * @param uid the parent unique id. 286 */ 287 public void setParentUid(String uid) 288 { 289 parentUniqueId = uid; 290 } 291 292 /** 293 * Get the parent unique id of this add msg. 294 * @return the parent unique id. 295 */ 296 public String getParentUid() 297 { 298 return parentUniqueId; 299 } 300 }