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.protocols.ldap;
028 import org.opends.messages.Message;
029
030
031
032 import java.util.ArrayList;
033 import java.util.Iterator;
034 import java.util.List;
035
036 import org.opends.server.protocols.asn1.ASN1Element;
037 import org.opends.server.protocols.asn1.ASN1OctetString;
038 import org.opends.server.protocols.asn1.ASN1Sequence;
039 import org.opends.server.types.DebugLogLevel;
040 import org.opends.server.types.LDAPException;
041 import org.opends.server.types.RawAttribute;
042 import org.opends.server.util.Base64;
043
044
045 import static org.opends.server.loggers.debug.DebugLogger.*;
046 import org.opends.server.loggers.debug.DebugTracer;
047 import static org.opends.messages.ProtocolMessages.*;
048 import static org.opends.server.protocols.ldap.LDAPConstants.*;
049 import static org.opends.server.protocols.ldap.LDAPResultCode.*;
050 import static org.opends.server.util.ServerConstants.*;
051 import static org.opends.server.util.StaticUtils.*;
052
053
054
055 /**
056 * This class defines the structures and methods for an LDAP add request
057 * protocol op, which is used to add a new entry to the Directory Server.
058 */
059 public class AddRequestProtocolOp
060 extends ProtocolOp
061 {
062 /**
063 * The tracer object for the debug logger.
064 */
065 private static final DebugTracer TRACER = getTracer();
066
067 // The set of attributes for this add request.
068 private List<RawAttribute> attributes;
069
070 // The DN for this add request.
071 private ASN1OctetString dn;
072
073
074
075 /**
076 * Creates a new LDAP add request protocol op with the specified DN and no
077 * attributes.
078 *
079 * @param dn The DN for this add request.
080 */
081 public AddRequestProtocolOp(ASN1OctetString dn)
082 {
083 this.dn = dn;
084 this.attributes = new ArrayList<RawAttribute>();
085 }
086
087
088
089 /**
090 * Creates a new LDAP add request protocol op with the specified DN and set of
091 * attributes.
092 *
093 * @param dn The DN for this add request.
094 * @param attributes The set of attributes for this add request.
095 */
096 public AddRequestProtocolOp(ASN1OctetString dn,
097 ArrayList<RawAttribute> attributes)
098 {
099 this.dn = dn;
100
101 if (attributes == null)
102 {
103 this.attributes = new ArrayList<RawAttribute>();
104 }
105 else
106 {
107 this.attributes = attributes;
108 }
109 }
110
111
112
113 /**
114 * Retrieves the DN for this add request.
115 *
116 * @return The DN for this add request.
117 */
118 public ASN1OctetString getDN()
119 {
120 return dn;
121 }
122
123
124
125 /**
126 * Specifies the DN for this add request.
127 *
128 * @param dn The DN for this add request.
129 */
130 public void setDN(ASN1OctetString dn)
131 {
132 this.dn = dn;
133 }
134
135
136
137 /**
138 * Retrieves the set of attributes for this add request. The returned list
139 * may be altered by the caller.
140 *
141 * @return The set of attributes for this add request.
142 */
143 public List<RawAttribute> getAttributes()
144 {
145 return attributes;
146 }
147
148
149
150 /**
151 * Retrieves the BER type for this protocol op.
152 *
153 * @return The BER type for this protocol op.
154 */
155 public byte getType()
156 {
157 return OP_TYPE_ADD_REQUEST;
158 }
159
160
161
162 /**
163 * Retrieves the name for this protocol op type.
164 *
165 * @return The name for this protocol op type.
166 */
167 public String getProtocolOpName()
168 {
169 return "Add Request";
170 }
171
172
173
174 /**
175 * Encodes this protocol op to an ASN.1 element suitable for including in an
176 * LDAP message.
177 *
178 * @return The ASN.1 element containing the encoded protocol op.
179 */
180 public ASN1Element encode()
181 {
182 ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(2);
183 elements.add(dn);
184
185
186 ArrayList<ASN1Element> attrElements =
187 new ArrayList<ASN1Element>(attributes.size());
188 for (RawAttribute attr : attributes)
189 {
190 attrElements.add(attr.encode());
191 }
192 elements.add(new ASN1Sequence(attrElements));
193
194
195 return new ASN1Sequence(OP_TYPE_ADD_REQUEST, elements);
196 }
197
198
199
200 /**
201 * Decodes the provided ASN.1 element as an LDAP add request protocol op.
202 *
203 * @param element The ASN.1 element to be decoded.
204 *
205 * @return The decoded add request protocol op.
206 *
207 * @throws LDAPException If a problem occurs while decoding the provided
208 * ASN.1 element as an LDAP add request protocol op.
209 */
210 public static AddRequestProtocolOp decodeAddRequest(ASN1Element element)
211 throws LDAPException
212 {
213 ArrayList<ASN1Element> elements;
214 try
215 {
216 elements = element.decodeAsSequence().elements();
217 }
218 catch (Exception e)
219 {
220 if (debugEnabled())
221 {
222 TRACER.debugCaught(DebugLogLevel.ERROR, e);
223 }
224
225 Message message =
226 ERR_LDAP_ADD_REQUEST_DECODE_SEQUENCE.get(String.valueOf(e));
227 throw new LDAPException(PROTOCOL_ERROR, message, e);
228 }
229
230
231 int numElements = elements.size();
232 if (numElements != 2)
233 {
234 Message message =
235 ERR_LDAP_ADD_REQUEST_DECODE_INVALID_ELEMENT_COUNT.get(numElements);
236 throw new LDAPException(PROTOCOL_ERROR, message);
237 }
238
239
240 ASN1OctetString dn;
241 try
242 {
243 dn = elements.get(0).decodeAsOctetString();
244 }
245 catch (Exception e)
246 {
247 if (debugEnabled())
248 {
249 TRACER.debugCaught(DebugLogLevel.ERROR, e);
250 }
251
252 Message message = ERR_LDAP_ADD_REQUEST_DECODE_DN.get(String.valueOf(e));
253 throw new LDAPException(PROTOCOL_ERROR, message, e);
254 }
255
256
257
258 ArrayList<RawAttribute> attributes;
259 try
260 {
261 ArrayList<ASN1Element> attrElements =
262 elements.get(1).decodeAsSequence().elements();
263 attributes = new ArrayList<RawAttribute>(attrElements.size());
264 for (ASN1Element e : attrElements)
265 {
266 attributes.add(LDAPAttribute.decode(e));
267 }
268 }
269 catch (Exception e)
270 {
271 if (debugEnabled())
272 {
273 TRACER.debugCaught(DebugLogLevel.ERROR, e);
274 }
275
276 Message message =
277 ERR_LDAP_ADD_REQUEST_DECODE_ATTRS.get(String.valueOf(e));
278 throw new LDAPException(PROTOCOL_ERROR, message, e);
279 }
280
281
282 return new AddRequestProtocolOp(dn, attributes);
283 }
284
285
286
287 /**
288 * Appends a string representation of this LDAP protocol op to the provided
289 * buffer.
290 *
291 * @param buffer The buffer to which the string should be appended.
292 */
293 public void toString(StringBuilder buffer)
294 {
295 buffer.append("AddRequest(dn=");
296 dn.toString(buffer);
297 buffer.append(", attrs={");
298
299 if (! attributes.isEmpty())
300 {
301 Iterator<RawAttribute> iterator = attributes.iterator();
302 iterator.next().toString(buffer);
303
304 while (iterator.hasNext())
305 {
306 buffer.append(", ");
307 iterator.next().toString(buffer);
308 }
309 }
310
311 buffer.append("})");
312 }
313
314
315
316 /**
317 * Appends a multi-line string representation of this LDAP protocol op to the
318 * provided buffer.
319 *
320 * @param buffer The buffer to which the information should be appended.
321 * @param indent The number of spaces from the margin that the lines should
322 * be indented.
323 */
324 public void toString(StringBuilder buffer, int indent)
325 {
326 StringBuilder indentBuf = new StringBuilder(indent);
327 for (int i=0 ; i < indent; i++)
328 {
329 indentBuf.append(' ');
330 }
331
332 buffer.append(indentBuf);
333 buffer.append("Add Request");
334 buffer.append(EOL);
335
336 buffer.append(indentBuf);
337 buffer.append(" DN: ");
338 dn.toString(buffer);
339 buffer.append(EOL);
340
341 buffer.append(" Attributes:");
342 buffer.append(EOL);
343
344 for (RawAttribute attribute : attributes)
345 {
346 attribute.toString(buffer, indent+4);
347 }
348 }
349
350
351
352 /**
353 * Appends an LDIF representation of the entry to the provided buffer.
354 *
355 * @param buffer The buffer to which the entry should be appended.
356 * @param wrapColumn The column at which long lines should be wrapped.
357 */
358 public void toLDIF(StringBuilder buffer, int wrapColumn)
359 {
360 // Add the DN to the buffer.
361 String dnString;
362 int colsRemaining;
363 if (needsBase64Encoding(dn.value()))
364 {
365 dnString = Base64.encode(dn.value());
366 buffer.append("dn:: ");
367
368 colsRemaining = wrapColumn - 5;
369 }
370 else
371 {
372 dnString = dn.stringValue();
373 buffer.append("dn: ");
374
375 colsRemaining = wrapColumn - 4;
376 }
377
378 int dnLength = dnString.length();
379 if ((dnLength <= colsRemaining) || (colsRemaining <= 0))
380 {
381 buffer.append(dnString);
382 buffer.append(EOL);
383 }
384 else
385 {
386 buffer.append(dnString.substring(0, colsRemaining));
387 buffer.append(EOL);
388
389 int startPos = colsRemaining;
390 while ((dnLength - startPos) > (wrapColumn - 1))
391 {
392 buffer.append(" ");
393 buffer.append(dnString.substring(startPos, (startPos+wrapColumn-1)));
394 buffer.append(EOL);
395
396 startPos += (wrapColumn-1);
397 }
398
399 if (startPos < dnLength)
400 {
401 buffer.append(" ");
402 buffer.append(dnString.substring(startPos));
403 buffer.append(EOL);
404 }
405 }
406
407
408 // Add the attributes to the buffer.
409 for (RawAttribute a : attributes)
410 {
411 String name = a.getAttributeType();
412 int nameLength = name.length();
413
414 for (ASN1OctetString v : a.getValues())
415 {
416 String valueString;
417 if (needsBase64Encoding(v.value()))
418 {
419 valueString = Base64.encode(v.value());
420 buffer.append(name);
421 buffer.append(":: ");
422
423 colsRemaining = wrapColumn - nameLength - 3;
424 }
425 else
426 {
427 valueString = v.stringValue();
428 buffer.append(name);
429 buffer.append(": ");
430
431 colsRemaining = wrapColumn - nameLength - 2;
432 }
433
434 int valueLength = valueString.length();
435 if ((valueLength <= colsRemaining) || (colsRemaining <= 0))
436 {
437 buffer.append(valueString);
438 buffer.append(EOL);
439 }
440 else
441 {
442 buffer.append(valueString.substring(0, colsRemaining));
443 buffer.append(EOL);
444
445 int startPos = colsRemaining;
446 while ((valueLength - startPos) > (wrapColumn - 1))
447 {
448 buffer.append(" ");
449 buffer.append(valueString.substring(startPos,
450 (startPos+wrapColumn-1)));
451 buffer.append(EOL);
452
453 startPos += (wrapColumn-1);
454 }
455
456 if (startPos < valueLength)
457 {
458 buffer.append(" ");
459 buffer.append(valueString.substring(startPos));
460 buffer.append(EOL);
461 }
462 }
463 }
464 }
465
466
467 // Make sure to add an extra blank line to ensure that there will be one
468 // between this entry and the next.
469 buffer.append(EOL);
470 }
471 }
472