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 }