001/*
002 * Copyright 2007-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2014 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.sdk;
022
023
024
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.Collection;
028import java.util.Collections;
029import java.util.Iterator;
030import java.util.List;
031import java.util.Timer;
032import java.util.concurrent.LinkedBlockingQueue;
033import java.util.concurrent.TimeUnit;
034
035import com.unboundid.asn1.ASN1Buffer;
036import com.unboundid.asn1.ASN1BufferSequence;
037import com.unboundid.asn1.ASN1Element;
038import com.unboundid.asn1.ASN1OctetString;
039import com.unboundid.asn1.ASN1Sequence;
040import com.unboundid.ldap.matchingrules.MatchingRule;
041import com.unboundid.ldap.protocol.LDAPMessage;
042import com.unboundid.ldap.protocol.LDAPResponse;
043import com.unboundid.ldap.protocol.ProtocolOp;
044import com.unboundid.ldif.LDIFAddChangeRecord;
045import com.unboundid.ldif.LDIFException;
046import com.unboundid.ldif.LDIFReader;
047import com.unboundid.util.InternalUseOnly;
048import com.unboundid.util.Mutable;
049import com.unboundid.util.ThreadSafety;
050import com.unboundid.util.ThreadSafetyLevel;
051
052import static com.unboundid.ldap.sdk.LDAPMessages.*;
053import static com.unboundid.util.Debug.*;
054import static com.unboundid.util.StaticUtils.*;
055import static com.unboundid.util.Validator.*;
056
057
058
059/**
060 * This class implements the processing necessary to perform an LDAPv3 add
061 * operation, which creates a new entry in the directory.  An add request
062 * contains the DN for the entry and the set of attributes to include.  It may
063 * also include a set of controls to send to the server.
064 * <BR><BR>
065 * The contents of the entry to may be specified as a separate DN and collection
066 * of attributes, as an {@link Entry} object, or as a list of the lines that
067 * comprise the LDIF representation of the entry to add as described in
068 * <A HREF="http://www.ietf.org/rfc/rfc2849.txt">RFC 2849</A>.  For example, the
069 * following code demonstrates creating an add request from the LDIF
070 * representation of the entry:
071 * <PRE>
072 *   AddRequest addRequest = new AddRequest(
073 *     "dn: dc=example,dc=com",
074 *     "objectClass: top",
075 *     "objectClass: domain",
076 *     "dc: example");
077 * </PRE>
078 * <BR><BR>
079 * {@code AddRequest} objects are mutable and therefore can be altered and
080 * re-used for multiple requests.  Note, however, that {@code AddRequest}
081 * objects are not threadsafe and therefore a single {@code AddRequest} object
082 * instance should not be used to process multiple requests at the same time.
083 */
084@Mutable()
085@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
086public final class AddRequest
087       extends UpdatableLDAPRequest
088       implements ReadOnlyAddRequest, ResponseAcceptor, ProtocolOp
089{
090  /**
091   * The serial version UID for this serializable class.
092   */
093  private static final long serialVersionUID = 1320730292848237219L;
094
095
096
097  // The queue that will be used to receive response messages from the server.
098  private final LinkedBlockingQueue<LDAPResponse> responseQueue =
099       new LinkedBlockingQueue<LDAPResponse>();
100
101  // The set of attributes to include in the entry to add.
102  private ArrayList<Attribute> attributes;
103
104  // The message ID from the last LDAP message sent from this request.
105  private int messageID = -1;
106
107  // The DN of the entry to be added.
108  private String dn;
109
110
111
112  /**
113   * Creates a new add request with the provided DN and set of attributes.
114   *
115   * @param  dn          The DN for the entry to add.  It must not be
116   *                     {@code null}.
117   * @param  attributes  The set of attributes to include in the entry to add.
118   *                     It must not be {@code null}.
119   */
120  public AddRequest(final String dn, final Attribute... attributes)
121  {
122    super(null);
123
124    ensureNotNull(dn, attributes);
125
126    this.dn = dn;
127
128    this.attributes = new ArrayList<Attribute>(attributes.length);
129    this.attributes.addAll(Arrays.asList(attributes));
130  }
131
132
133
134  /**
135   * Creates a new add request with the provided DN and set of attributes.
136   *
137   * @param  dn          The DN for the entry to add.  It must not be
138   *                     {@code null}.
139   * @param  attributes  The set of attributes to include in the entry to add.
140   *                     It must not be {@code null}.
141   * @param  controls    The set of controls to include in the request.
142   */
143  public AddRequest(final String dn, final Attribute[] attributes,
144                    final Control[] controls)
145  {
146    super(controls);
147
148    ensureNotNull(dn, attributes);
149
150    this.dn = dn;
151
152    this.attributes = new ArrayList<Attribute>(attributes.length);
153    this.attributes.addAll(Arrays.asList(attributes));
154  }
155
156
157
158  /**
159   * Creates a new add request with the provided DN and set of attributes.
160   *
161   * @param  dn          The DN for the entry to add.  It must not be
162   *                     {@code null}.
163   * @param  attributes  The set of attributes to include in the entry to add.
164   *                     It must not be {@code null}.
165   */
166  public AddRequest(final String dn, final Collection<Attribute> attributes)
167  {
168    super(null);
169
170    ensureNotNull(dn, attributes);
171
172    this.dn         = dn;
173    this.attributes = new ArrayList<Attribute>(attributes);
174  }
175
176
177
178  /**
179   * Creates a new add request with the provided DN and set of attributes.
180   *
181   * @param  dn          The DN for the entry to add.  It must not be
182   *                     {@code null}.
183   * @param  attributes  The set of attributes to include in the entry to add.
184   *                     It must not be {@code null}.
185   * @param  controls    The set of controls to include in the request.
186   */
187  public AddRequest(final String dn, final Collection<Attribute> attributes,
188                    final Control[] controls)
189  {
190    super(controls);
191
192    ensureNotNull(dn, attributes);
193
194    this.dn         = dn;
195    this.attributes = new ArrayList<Attribute>(attributes);
196  }
197
198
199
200  /**
201   * Creates a new add request with the provided DN and set of attributes.
202   *
203   * @param  dn          The DN for the entry to add.  It must not be
204   *                     {@code null}.
205   * @param  attributes  The set of attributes to include in the entry to add.
206   *                     It must not be {@code null}.
207   */
208  public AddRequest(final DN dn, final Attribute... attributes)
209  {
210    super(null);
211
212    ensureNotNull(dn, attributes);
213
214    this.dn = dn.toString();
215
216    this.attributes = new ArrayList<Attribute>(attributes.length);
217    this.attributes.addAll(Arrays.asList(attributes));
218  }
219
220
221
222  /**
223   * Creates a new add request with the provided DN and set of attributes.
224   *
225   * @param  dn          The DN for the entry to add.  It must not be
226   *                     {@code null}.
227   * @param  attributes  The set of attributes to include in the entry to add.
228   *                     It must not be {@code null}.
229   * @param  controls    The set of controls to include in the request.
230   */
231  public AddRequest(final DN dn, final Attribute[] attributes,
232                    final Control[] controls)
233  {
234    super(controls);
235
236    ensureNotNull(dn, attributes);
237
238    this.dn = dn.toString();
239
240    this.attributes = new ArrayList<Attribute>(attributes.length);
241    this.attributes.addAll(Arrays.asList(attributes));
242  }
243
244
245
246  /**
247   * Creates a new add request with the provided DN and set of attributes.
248   *
249   * @param  dn          The DN for the entry to add.  It must not be
250   *                     {@code null}.
251   * @param  attributes  The set of attributes to include in the entry to add.
252   *                     It must not be {@code null}.
253   */
254  public AddRequest(final DN dn, final Collection<Attribute> attributes)
255  {
256    super(null);
257
258    ensureNotNull(dn, attributes);
259
260    this.dn         = dn.toString();
261    this.attributes = new ArrayList<Attribute>(attributes);
262  }
263
264
265
266  /**
267   * Creates a new add request with the provided DN and set of attributes.
268   *
269   * @param  dn          The DN for the entry to add.  It must not be
270   *                     {@code null}.
271   * @param  attributes  The set of attributes to include in the entry to add.
272   *                     It must not be {@code null}.
273   * @param  controls    The set of controls to include in the request.
274   */
275  public AddRequest(final DN dn, final Collection<Attribute> attributes,
276                    final Control[] controls)
277  {
278    super(controls);
279
280    ensureNotNull(dn, attributes);
281
282    this.dn         = dn.toString();
283    this.attributes = new ArrayList<Attribute>(attributes);
284  }
285
286
287
288  /**
289   * Creates a new add request to add the provided entry.
290   *
291   * @param  entry  The entry to be added.  It must not be {@code null}.
292   */
293  public AddRequest(final Entry entry)
294  {
295    super(null);
296
297    ensureNotNull(entry);
298
299    dn         = entry.getDN();
300    attributes = new ArrayList<Attribute>(entry.getAttributes());
301  }
302
303
304
305  /**
306   * Creates a new add request to add the provided entry.
307   *
308   * @param  entry     The entry to be added.  It must not be {@code null}.
309   * @param  controls  The set of controls to include in the request.
310   */
311  public AddRequest(final Entry entry, final Control[] controls)
312  {
313    super(controls);
314
315    ensureNotNull(entry);
316
317    dn         = entry.getDN();
318    attributes = new ArrayList<Attribute>(entry.getAttributes());
319  }
320
321
322
323  /**
324   * Creates a new add request with the provided entry in LDIF form.
325   *
326   * @param  ldifLines  The lines that comprise the LDIF representation of the
327   *                    entry to add.  It must not be {@code null} or empty.
328   *
329   * @throws  LDIFException  If the provided LDIF data cannot be decoded as an
330   *                         entry.
331   */
332  public AddRequest(final String... ldifLines)
333         throws LDIFException
334  {
335    this(LDIFReader.decodeEntry(ldifLines));
336  }
337
338
339
340  /**
341   * {@inheritDoc}
342   */
343  public String getDN()
344  {
345    return dn;
346  }
347
348
349
350  /**
351   * Specifies the DN for this add request.
352   *
353   * @param  dn  The DN for this add request.  It must not be {@code null}.
354   */
355  public void setDN(final String dn)
356  {
357    ensureNotNull(dn);
358
359    this.dn = dn;
360  }
361
362
363
364  /**
365   * Specifies the DN for this add request.
366   *
367   * @param  dn  The DN for this add request.  It must not be {@code null}.
368   */
369  public void setDN(final DN dn)
370  {
371    ensureNotNull(dn);
372
373    this.dn = dn.toString();
374  }
375
376
377
378  /**
379   * {@inheritDoc}
380   */
381  public List<Attribute> getAttributes()
382  {
383    return Collections.unmodifiableList(attributes);
384  }
385
386
387
388  /**
389   * {@inheritDoc}
390   */
391  public Attribute getAttribute(final String attributeName)
392  {
393    ensureNotNull(attributeName);
394
395    for (final Attribute a : attributes)
396    {
397      if (a.getName().equalsIgnoreCase(attributeName))
398      {
399        return a;
400      }
401    }
402
403    return null;
404  }
405
406
407
408  /**
409   * {@inheritDoc}
410   */
411  public boolean hasAttribute(final String attributeName)
412  {
413    return (getAttribute(attributeName) != null);
414  }
415
416
417
418  /**
419   * {@inheritDoc}
420   */
421  public boolean hasAttribute(final Attribute attribute)
422  {
423    ensureNotNull(attribute);
424
425    final Attribute a = getAttribute(attribute.getName());
426    return ((a != null) && attribute.equals(a));
427  }
428
429
430
431  /**
432   * {@inheritDoc}
433   */
434  public boolean hasAttributeValue(final String attributeName,
435                                   final String attributeValue)
436  {
437    ensureNotNull(attributeName, attributeValue);
438
439    final Attribute a = getAttribute(attributeName);
440    return ((a != null) && a.hasValue(attributeValue));
441  }
442
443
444
445  /**
446   * {@inheritDoc}
447   */
448  public boolean hasAttributeValue(final String attributeName,
449                                   final String attributeValue,
450                                   final MatchingRule matchingRule)
451  {
452    ensureNotNull(attributeName, attributeValue);
453
454    final Attribute a = getAttribute(attributeName);
455    return ((a != null) && a.hasValue(attributeValue, matchingRule));
456  }
457
458
459
460  /**
461   * {@inheritDoc}
462   */
463  public boolean hasAttributeValue(final String attributeName,
464                                   final byte[] attributeValue)
465  {
466    ensureNotNull(attributeName, attributeValue);
467
468    final Attribute a = getAttribute(attributeName);
469    return ((a != null) && a.hasValue(attributeValue));
470  }
471
472
473
474  /**
475   * {@inheritDoc}
476   */
477  public boolean hasAttributeValue(final String attributeName,
478                                   final byte[] attributeValue,
479                                   final MatchingRule matchingRule)
480  {
481    ensureNotNull(attributeName, attributeValue);
482
483    final Attribute a = getAttribute(attributeName);
484    return ((a != null) && a.hasValue(attributeValue, matchingRule));
485  }
486
487
488
489  /**
490   * {@inheritDoc}
491   */
492  public boolean hasObjectClass(final String objectClassName)
493  {
494    return hasAttributeValue("objectClass", objectClassName);
495  }
496
497
498
499  /**
500   * {@inheritDoc}
501   */
502  public Entry toEntry()
503  {
504    return new Entry(dn, attributes);
505  }
506
507
508
509  /**
510   * Specifies the set of attributes for this add request.  It must not be
511   * {@code null}.
512   *
513   * @param  attributes  The set of attributes for this add request.
514   */
515  public void setAttributes(final Attribute[] attributes)
516  {
517    ensureNotNull(attributes);
518
519    this.attributes.clear();
520    this.attributes.addAll(Arrays.asList(attributes));
521  }
522
523
524
525  /**
526   * Specifies the set of attributes for this add request.  It must not be
527   * {@code null}.
528   *
529   * @param  attributes  The set of attributes for this add request.
530   */
531  public void setAttributes(final Collection<Attribute> attributes)
532  {
533    ensureNotNull(attributes);
534
535    this.attributes.clear();
536    this.attributes.addAll(attributes);
537  }
538
539
540
541  /**
542   * Adds the provided attribute to the entry to add.
543   *
544   * @param  attribute  The attribute to be added to the entry to add.  It must
545   *                    not be {@code null}.
546   */
547  public void addAttribute(final Attribute attribute)
548  {
549    ensureNotNull(attribute);
550
551    for (int i=0 ; i < attributes.size(); i++)
552    {
553      final Attribute a = attributes.get(i);
554      if (a.getName().equalsIgnoreCase(attribute.getName()))
555      {
556        attributes.set(i, Attribute.mergeAttributes(a, attribute));
557        return;
558      }
559    }
560
561    attributes.add(attribute);
562  }
563
564
565
566  /**
567   * Adds the provided attribute to the entry to add.
568   *
569   * @param  name   The name of the attribute to add.  It must not be
570   *                {@code null}.
571   * @param  value  The value for the attribute to add.  It must not be
572   *                {@code null}.
573   */
574  public void addAttribute(final String name, final String value)
575  {
576    ensureNotNull(name, value);
577    addAttribute(new Attribute(name, value));
578  }
579
580
581
582  /**
583   * Adds the provided attribute to the entry to add.
584   *
585   * @param  name   The name of the attribute to add.  It must not be
586   *                {@code null}.
587   * @param  value  The value for the attribute to add.  It must not be
588   *                {@code null}.
589   */
590  public void addAttribute(final String name, final byte[] value)
591  {
592    ensureNotNull(name, value);
593    addAttribute(new Attribute(name, value));
594  }
595
596
597
598  /**
599   * Adds the provided attribute to the entry to add.
600   *
601   * @param  name    The name of the attribute to add.  It must not be
602   *                 {@code null}.
603   * @param  values  The set of values for the attribute to add.  It must not be
604   *                 {@code null}.
605   */
606  public void addAttribute(final String name, final String... values)
607  {
608    ensureNotNull(name, values);
609    addAttribute(new Attribute(name, values));
610  }
611
612
613
614  /**
615   * Adds the provided attribute to the entry to add.
616   *
617   * @param  name    The name of the attribute to add.  It must not be
618   *                 {@code null}.
619   * @param  values  The set of values for the attribute to add.  It must not be
620   *                 {@code null}.
621   */
622  public void addAttribute(final String name, final byte[]... values)
623  {
624    ensureNotNull(name, values);
625    addAttribute(new Attribute(name, values));
626  }
627
628
629
630  /**
631   * Removes the attribute with the specified name from the entry to add.
632   *
633   * @param  attributeName  The name of the attribute to remove.  It must not be
634   *                        {@code null}.
635   *
636   * @return  {@code true} if the attribute was removed from this add request,
637   *          or {@code false} if the add request did not include the specified
638   *          attribute.
639   */
640  public boolean removeAttribute(final String attributeName)
641  {
642    ensureNotNull(attributeName);
643
644    final Iterator<Attribute> iterator = attributes.iterator();
645    while (iterator.hasNext())
646    {
647      final Attribute a = iterator.next();
648      if (a.getName().equalsIgnoreCase(attributeName))
649      {
650        iterator.remove();
651        return true;
652      }
653    }
654
655    return false;
656  }
657
658
659
660  /**
661   * Removes the specified attribute value from this add request.
662   *
663   * @param  name   The name of the attribute to remove.  It must not be
664   *                {@code null}.
665   * @param  value  The value of the attribute to remove.  It must not be
666   *                {@code null}.
667   *
668   * @return  {@code true} if the attribute value was removed from this add
669   *          request, or {@code false} if the add request did not include the
670   *          specified attribute value.
671   */
672  public boolean removeAttributeValue(final String name, final String value)
673  {
674    ensureNotNull(name, value);
675
676    int pos = -1;
677    for (int i=0; i < attributes.size(); i++)
678    {
679      final Attribute a = attributes.get(i);
680      if (a.getName().equalsIgnoreCase(name))
681      {
682        pos = i;
683        break;
684      }
685    }
686
687    if (pos < 0)
688    {
689      return false;
690    }
691
692    final Attribute a = attributes.get(pos);
693    final Attribute newAttr =
694         Attribute.removeValues(a, new Attribute(name, value));
695
696    if (a.getRawValues().length == newAttr.getRawValues().length)
697    {
698      return false;
699    }
700
701    if (newAttr.getRawValues().length == 0)
702    {
703      attributes.remove(pos);
704    }
705    else
706    {
707      attributes.set(pos, newAttr);
708    }
709
710    return true;
711  }
712
713
714
715  /**
716   * Removes the specified attribute value from this add request.
717   *
718   * @param  name   The name of the attribute to remove.  It must not be
719   *                {@code null}.
720   * @param  value  The value of the attribute to remove.  It must not be
721   *                {@code null}.
722   *
723   * @return  {@code true} if the attribute value was removed from this add
724   *          request, or {@code false} if the add request did not include the
725   *          specified attribute value.
726   */
727  public boolean removeAttribute(final String name, final byte[] value)
728  {
729    ensureNotNull(name, value);
730
731    int pos = -1;
732    for (int i=0; i < attributes.size(); i++)
733    {
734      final Attribute a = attributes.get(i);
735      if (a.getName().equalsIgnoreCase(name))
736      {
737        pos = i;
738        break;
739      }
740    }
741
742    if (pos < 0)
743    {
744      return false;
745    }
746
747    final Attribute a = attributes.get(pos);
748    final Attribute newAttr =
749         Attribute.removeValues(a, new Attribute(name, value));
750
751    if (a.getRawValues().length == newAttr.getRawValues().length)
752    {
753      return false;
754    }
755
756    if (newAttr.getRawValues().length == 0)
757    {
758      attributes.remove(pos);
759    }
760    else
761    {
762      attributes.set(pos, newAttr);
763    }
764
765    return true;
766  }
767
768
769
770  /**
771   * Replaces the specified attribute in the entry to add.  If no attribute with
772   * the given name exists in the add request, it will be added.
773   *
774   * @param  attribute  The attribute to be replaced in this add request.  It
775   *                    must not be {@code null}.
776   */
777  public void replaceAttribute(final Attribute attribute)
778  {
779    ensureNotNull(attribute);
780
781    for (int i=0; i < attributes.size(); i++)
782    {
783      if (attributes.get(i).getName().equalsIgnoreCase(attribute.getName()))
784      {
785        attributes.set(i, attribute);
786        return;
787      }
788    }
789
790    attributes.add(attribute);
791  }
792
793
794
795  /**
796   * Replaces the specified attribute in the entry to add.  If no attribute with
797   * the given name exists in the add request, it will be added.
798   *
799   * @param  name   The name of the attribute to be replaced.  It must not be
800   *                {@code null}.
801   * @param  value  The new value for the attribute.  It must not be
802   *                {@code null}.
803   */
804  public void replaceAttribute(final String name, final String value)
805  {
806    ensureNotNull(name, value);
807
808    for (int i=0; i < attributes.size(); i++)
809    {
810      if (attributes.get(i).getName().equalsIgnoreCase(name))
811      {
812        attributes.set(i, new Attribute(name, value));
813        return;
814      }
815    }
816
817    attributes.add(new Attribute(name, value));
818  }
819
820
821
822  /**
823   * Replaces the specified attribute in the entry to add.  If no attribute with
824   * the given name exists in the add request, it will be added.
825   *
826   * @param  name   The name of the attribute to be replaced.  It must not be
827   *                {@code null}.
828   * @param  value  The new value for the attribute.  It must not be
829   *                {@code null}.
830   */
831  public void replaceAttribute(final String name, final byte[] value)
832  {
833    ensureNotNull(name, value);
834
835    for (int i=0; i < attributes.size(); i++)
836    {
837      if (attributes.get(i).getName().equalsIgnoreCase(name))
838      {
839        attributes.set(i, new Attribute(name, value));
840        return;
841      }
842    }
843
844    attributes.add(new Attribute(name, value));
845  }
846
847
848
849  /**
850   * Replaces the specified attribute in the entry to add.  If no attribute with
851   * the given name exists in the add request, it will be added.
852   *
853   * @param  name    The name of the attribute to be replaced.  It must not be
854   *                 {@code null}.
855   * @param  values  The new set of values for the attribute.  It must not be
856   *                 {@code null}.
857   */
858  public void replaceAttribute(final String name, final String... values)
859  {
860    ensureNotNull(name, values);
861
862    for (int i=0; i < attributes.size(); i++)
863    {
864      if (attributes.get(i).getName().equalsIgnoreCase(name))
865      {
866        attributes.set(i, new Attribute(name, values));
867        return;
868      }
869    }
870
871    attributes.add(new Attribute(name, values));
872  }
873
874
875
876  /**
877   * Replaces the specified attribute in the entry to add.  If no attribute with
878   * the given name exists in the add request, it will be added.
879   *
880   * @param  name    The name of the attribute to be replaced.  It must not be
881   *                 {@code null}.
882   * @param  values  The new set of values for the attribute.  It must not be
883   *                 {@code null}.
884   */
885  public void replaceAttribute(final String name, final byte[]... values)
886  {
887    ensureNotNull(name, values);
888
889    for (int i=0; i < attributes.size(); i++)
890    {
891      if (attributes.get(i).getName().equalsIgnoreCase(name))
892      {
893        attributes.set(i, new Attribute(name, values));
894        return;
895      }
896    }
897
898    attributes.add(new Attribute(name, values));
899  }
900
901
902
903  /**
904   * {@inheritDoc}
905   */
906  public byte getProtocolOpType()
907  {
908    return LDAPMessage.PROTOCOL_OP_TYPE_ADD_REQUEST;
909  }
910
911
912
913  /**
914   * {@inheritDoc}
915   */
916  public void writeTo(final ASN1Buffer buffer)
917  {
918    final ASN1BufferSequence requestSequence =
919         buffer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_ADD_REQUEST);
920    buffer.addOctetString(dn);
921
922    final ASN1BufferSequence attrSequence = buffer.beginSequence();
923    for (final Attribute a : attributes)
924    {
925      a.writeTo(buffer);
926    }
927    attrSequence.end();
928
929    requestSequence.end();
930  }
931
932
933
934  /**
935   * Encodes the add request protocol op to an ASN.1 element.
936   *
937   * @return  The ASN.1 element with the encoded add request protocol op.
938   */
939  public ASN1Element encodeProtocolOp()
940  {
941    // Create the add request protocol op.
942    final ASN1Element[] attrElements = new ASN1Element[attributes.size()];
943    for (int i=0; i < attrElements.length; i++)
944    {
945      attrElements[i] = attributes.get(i).encode();
946    }
947
948    final ASN1Element[] addRequestElements =
949    {
950      new ASN1OctetString(dn),
951      new ASN1Sequence(attrElements)
952    };
953
954    return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_ADD_REQUEST,
955                            addRequestElements);
956  }
957
958
959
960  /**
961   * Sends this add request to the directory server over the provided connection
962   * and returns the associated response.
963   *
964   * @param  connection  The connection to use to communicate with the directory
965   *                     server.
966   * @param  depth       The current referral depth for this request.  It should
967   *                     always be one for the initial request, and should only
968   *                     be incremented when following referrals.
969   *
970   * @return  An LDAP result object that provides information about the result
971   *          of the add processing.
972   *
973   * @throws  LDAPException  If a problem occurs while sending the request or
974   *                         reading the response.
975   */
976  @Override()
977  protected LDAPResult process(final LDAPConnection connection, final int depth)
978            throws LDAPException
979  {
980    if (connection.synchronousMode())
981    {
982      return processSync(connection, depth,
983           connection.getConnectionOptions().autoReconnect());
984    }
985
986    final long requestTime = System.nanoTime();
987    processAsync(connection, null);
988
989    try
990    {
991      // Wait for and process the response.
992      final LDAPResponse response;
993      try
994      {
995        final long responseTimeout = getResponseTimeoutMillis(connection);
996        if (responseTimeout > 0)
997        {
998          response = responseQueue.poll(responseTimeout, TimeUnit.MILLISECONDS);
999        }
1000        else
1001        {
1002          response = responseQueue.take();
1003        }
1004      }
1005      catch (InterruptedException ie)
1006      {
1007        debugException(ie);
1008        throw new LDAPException(ResultCode.LOCAL_ERROR,
1009             ERR_ADD_INTERRUPTED.get(connection.getHostPort()), ie);
1010      }
1011
1012      return handleResponse(connection, response, requestTime, depth, false);
1013    }
1014    finally
1015    {
1016      connection.deregisterResponseAcceptor(messageID);
1017    }
1018  }
1019
1020
1021
1022  /**
1023   * Sends this add request to the directory server over the provided connection
1024   * and returns the message ID for the request.
1025   *
1026   * @param  connection      The connection to use to communicate with the
1027   *                         directory server.
1028   * @param  resultListener  The async result listener that is to be notified
1029   *                         when the response is received.  It may be
1030   *                         {@code null} only if the result is to be processed
1031   *                         by this class.
1032   *
1033   * @return  The async request ID created for the operation, or {@code null} if
1034   *          the provided {@code resultListener} is {@code null} and the
1035   *          operation will not actually be processed asynchronously.
1036   *
1037   * @throws  LDAPException  If a problem occurs while sending the request.
1038   */
1039  AsyncRequestID processAsync(final LDAPConnection connection,
1040                              final AsyncResultListener resultListener)
1041                 throws LDAPException
1042  {
1043    // Create the LDAP message.
1044    messageID = connection.nextMessageID();
1045    final LDAPMessage message =
1046         new LDAPMessage(messageID,  this, getControls());
1047
1048
1049    // If the provided async result listener is {@code null}, then we'll use
1050    // this class as the message acceptor.  Otherwise, create an async helper
1051    // and use it as the message acceptor.
1052    final AsyncRequestID asyncRequestID;
1053    if (resultListener == null)
1054    {
1055      asyncRequestID = null;
1056      connection.registerResponseAcceptor(messageID, this);
1057    }
1058    else
1059    {
1060      final AsyncHelper helper = new AsyncHelper(connection, OperationType.ADD,
1061           messageID, resultListener, getIntermediateResponseListener());
1062      connection.registerResponseAcceptor(messageID, helper);
1063      asyncRequestID = helper.getAsyncRequestID();
1064
1065      final long timeout = getResponseTimeoutMillis(connection);
1066      if (timeout > 0L)
1067      {
1068        final Timer timer = connection.getTimer();
1069        final AsyncTimeoutTimerTask timerTask =
1070             new AsyncTimeoutTimerTask(helper);
1071        timer.schedule(timerTask, timeout);
1072        asyncRequestID.setTimerTask(timerTask);
1073      }
1074    }
1075
1076
1077    // Send the request to the server.
1078    try
1079    {
1080      debugLDAPRequest(this);
1081      connection.getConnectionStatistics().incrementNumAddRequests();
1082      connection.sendMessage(message);
1083      return asyncRequestID;
1084    }
1085    catch (LDAPException le)
1086    {
1087      debugException(le);
1088
1089      connection.deregisterResponseAcceptor(messageID);
1090      throw le;
1091    }
1092  }
1093
1094
1095
1096  /**
1097   * Processes this add operation in synchronous mode, in which the same thread
1098   * will send the request and read the response.
1099   *
1100   * @param  connection  The connection to use to communicate with the directory
1101   *                     server.
1102   * @param  depth       The current referral depth for this request.  It should
1103   *                     always be one for the initial request, and should only
1104   *                     be incremented when following referrals.
1105   * @param  allowRetry  Indicates whether the request may be re-tried on a
1106   *                     re-established connection if the initial attempt fails
1107   *                     in a way that indicates the connection is no longer
1108   *                     valid and autoReconnect is true.
1109   *
1110   * @return  An LDAP result object that provides information about the result
1111   *          of the add processing.
1112   *
1113   * @throws  LDAPException  If a problem occurs while sending the request or
1114   *                         reading the response.
1115   */
1116  private LDAPResult processSync(final LDAPConnection connection,
1117                                 final int depth, final boolean allowRetry)
1118          throws LDAPException
1119  {
1120    // Create the LDAP message.
1121    messageID = connection.nextMessageID();
1122    final LDAPMessage message =
1123         new LDAPMessage(messageID,  this, getControls());
1124
1125
1126    // Set the appropriate timeout on the socket.
1127    try
1128    {
1129      connection.getConnectionInternals(true).getSocket().setSoTimeout(
1130           (int) getResponseTimeoutMillis(connection));
1131    }
1132    catch (Exception e)
1133    {
1134      debugException(e);
1135    }
1136
1137
1138    // Send the request to the server.
1139    final long requestTime = System.nanoTime();
1140    debugLDAPRequest(this);
1141    connection.getConnectionStatistics().incrementNumAddRequests();
1142    try
1143    {
1144      connection.sendMessage(message);
1145    }
1146    catch (final LDAPException le)
1147    {
1148      debugException(le);
1149
1150      if (allowRetry)
1151      {
1152        final LDAPResult retryResult = reconnectAndRetry(connection, depth,
1153             le.getResultCode());
1154        if (retryResult != null)
1155        {
1156          return retryResult;
1157        }
1158      }
1159
1160      throw le;
1161    }
1162
1163    while (true)
1164    {
1165      final LDAPResponse response;
1166      try
1167      {
1168        response = connection.readResponse(messageID);
1169      }
1170      catch (final LDAPException le)
1171      {
1172        debugException(le);
1173
1174        if ((le.getResultCode() == ResultCode.TIMEOUT) &&
1175            connection.getConnectionOptions().abandonOnTimeout())
1176        {
1177          connection.abandon(messageID);
1178        }
1179
1180        if (allowRetry)
1181        {
1182          final LDAPResult retryResult = reconnectAndRetry(connection, depth,
1183               le.getResultCode());
1184          if (retryResult != null)
1185          {
1186            return retryResult;
1187          }
1188        }
1189
1190        throw le;
1191      }
1192
1193      if (response instanceof IntermediateResponse)
1194      {
1195        final IntermediateResponseListener listener =
1196             getIntermediateResponseListener();
1197        if (listener != null)
1198        {
1199          listener.intermediateResponseReturned(
1200               (IntermediateResponse) response);
1201        }
1202      }
1203      else
1204      {
1205        return handleResponse(connection, response, requestTime, depth,
1206             allowRetry);
1207      }
1208    }
1209  }
1210
1211
1212
1213  /**
1214   * Performs the necessary processing for handling a response.
1215   *
1216   * @param  connection   The connection used to read the response.
1217   * @param  response     The response to be processed.
1218   * @param  requestTime  The time the request was sent to the server.
1219   * @param  depth        The current referral depth for this request.  It
1220   *                      should always be one for the initial request, and
1221   *                      should only be incremented when following referrals.
1222   * @param  allowRetry   Indicates whether the request may be re-tried on a
1223   *                      re-established connection if the initial attempt fails
1224   *                      in a way that indicates the connection is no longer
1225   *                      valid and autoReconnect is true.
1226   *
1227   * @return  The add result.
1228   *
1229   * @throws  LDAPException  If a problem occurs.
1230   */
1231  private LDAPResult handleResponse(final LDAPConnection connection,
1232                                    final LDAPResponse response,
1233                                    final long requestTime, final int depth,
1234                                    final boolean allowRetry)
1235          throws LDAPException
1236  {
1237    if (response == null)
1238    {
1239      final long waitTime = nanosToMillis(System.nanoTime() - requestTime);
1240      if (connection.getConnectionOptions().abandonOnTimeout())
1241      {
1242        connection.abandon(messageID);
1243      }
1244
1245      throw new LDAPException(ResultCode.TIMEOUT,
1246           ERR_ADD_CLIENT_TIMEOUT.get(waitTime, messageID, dn,
1247                connection.getHostPort()));
1248    }
1249
1250    connection.getConnectionStatistics().incrementNumAddResponses(
1251         System.nanoTime() - requestTime);
1252
1253    if (response instanceof ConnectionClosedResponse)
1254    {
1255      // The connection was closed while waiting for the response.
1256      if (allowRetry)
1257      {
1258        final LDAPResult retryResult = reconnectAndRetry(connection, depth,
1259             ResultCode.SERVER_DOWN);
1260        if (retryResult != null)
1261        {
1262          return retryResult;
1263        }
1264      }
1265
1266      final ConnectionClosedResponse ccr = (ConnectionClosedResponse) response;
1267      final String message = ccr.getMessage();
1268      if (message == null)
1269      {
1270        throw new LDAPException(ccr.getResultCode(),
1271             ERR_CONN_CLOSED_WAITING_FOR_ADD_RESPONSE.get(
1272                  connection.getHostPort(), toString()));
1273      }
1274      else
1275      {
1276        throw new LDAPException(ccr.getResultCode(),
1277             ERR_CONN_CLOSED_WAITING_FOR_ADD_RESPONSE_WITH_MESSAGE.get(
1278                  connection.getHostPort(), toString(), message));
1279      }
1280    }
1281
1282    final LDAPResult result = (LDAPResult) response;
1283    if ((result.getResultCode().equals(ResultCode.REFERRAL)) &&
1284        followReferrals(connection))
1285    {
1286      if (depth >= connection.getConnectionOptions().getReferralHopLimit())
1287      {
1288        return new LDAPResult(messageID, ResultCode.REFERRAL_LIMIT_EXCEEDED,
1289                              ERR_TOO_MANY_REFERRALS.get(),
1290                              result.getMatchedDN(),
1291                              result.getReferralURLs(),
1292                              result.getResponseControls());
1293      }
1294
1295      return followReferral(result, connection, depth);
1296    }
1297    else
1298    {
1299      if (allowRetry)
1300      {
1301        final LDAPResult retryResult = reconnectAndRetry(connection, depth,
1302             result.getResultCode());
1303        if (retryResult != null)
1304        {
1305          return retryResult;
1306        }
1307      }
1308
1309      return result;
1310    }
1311  }
1312
1313
1314
1315  /**
1316   * Attempts to re-establish the connection and retry processing this request
1317   * on it.
1318   *
1319   * @param  connection  The connection to be re-established.
1320   * @param  depth       The current referral depth for this request.  It should
1321   *                     always be one for the initial request, and should only
1322   *                     be incremented when following referrals.
1323   * @param  resultCode  The result code for the previous operation attempt.
1324   *
1325   * @return  The result from re-trying the add, or {@code null} if it could not
1326   *          be re-tried.
1327   */
1328  private LDAPResult reconnectAndRetry(final LDAPConnection connection,
1329                                       final int depth,
1330                                       final ResultCode resultCode)
1331  {
1332    try
1333    {
1334      // We will only want to retry for certain result codes that indicate a
1335      // connection problem.
1336      switch (resultCode.intValue())
1337      {
1338        case ResultCode.SERVER_DOWN_INT_VALUE:
1339        case ResultCode.DECODING_ERROR_INT_VALUE:
1340        case ResultCode.CONNECT_ERROR_INT_VALUE:
1341          connection.reconnect();
1342          return processSync(connection, depth, false);
1343      }
1344    }
1345    catch (final Exception e)
1346    {
1347      debugException(e);
1348    }
1349
1350    return null;
1351  }
1352
1353
1354
1355  /**
1356   * Attempts to follow a referral to perform an add operation in the target
1357   * server.
1358   *
1359   * @param  referralResult  The LDAP result object containing information about
1360   *                         the referral to follow.
1361   * @param  connection      The connection on which the referral was received.
1362   * @param  depth           The number of referrals followed in the course of
1363   *                         processing this request.
1364   *
1365   * @return  The result of attempting to process the add operation by following
1366   *          the referral.
1367   *
1368   * @throws  LDAPException  If a problem occurs while attempting to establish
1369   *                         the referral connection, sending the request, or
1370   *                         reading the result.
1371   */
1372  private LDAPResult followReferral(final LDAPResult referralResult,
1373                                    final LDAPConnection connection,
1374                                    final int depth)
1375          throws LDAPException
1376  {
1377    for (final String urlString : referralResult.getReferralURLs())
1378    {
1379      try
1380      {
1381        final LDAPURL referralURL = new LDAPURL(urlString);
1382        final String host = referralURL.getHost();
1383
1384        if (host == null)
1385        {
1386          // We can't handle a referral in which there is no host.
1387          continue;
1388        }
1389
1390        final AddRequest addRequest;
1391        if (referralURL.baseDNProvided())
1392        {
1393          addRequest = new AddRequest(referralURL.getBaseDN(), attributes,
1394                                      getControls());
1395        }
1396        else
1397        {
1398          addRequest = this;
1399        }
1400
1401        final LDAPConnection referralConn = connection.getReferralConnector().
1402             getReferralConnection(referralURL, connection);
1403        try
1404        {
1405          return addRequest.process(referralConn, (depth+1));
1406        }
1407        finally
1408        {
1409          referralConn.setDisconnectInfo(DisconnectType.REFERRAL, null, null);
1410          referralConn.close();
1411        }
1412      }
1413      catch (LDAPException le)
1414      {
1415        debugException(le);
1416      }
1417    }
1418
1419    // If we've gotten here, then we could not follow any of the referral URLs,
1420    // so we'll just return the original referral result.
1421    return referralResult;
1422  }
1423
1424
1425
1426  /**
1427   * {@inheritDoc}
1428   */
1429  @Override()
1430  public int getLastMessageID()
1431  {
1432    return messageID;
1433  }
1434
1435
1436
1437  /**
1438   * {@inheritDoc}
1439   */
1440  @Override()
1441  public OperationType getOperationType()
1442  {
1443    return OperationType.ADD;
1444  }
1445
1446
1447
1448  /**
1449   * {@inheritDoc}
1450   */
1451  public AddRequest duplicate()
1452  {
1453    return duplicate(getControls());
1454  }
1455
1456
1457
1458  /**
1459   * {@inheritDoc}
1460   */
1461  public AddRequest duplicate(final Control[] controls)
1462  {
1463    final ArrayList<Attribute> attrs = new ArrayList<Attribute>(attributes);
1464    final AddRequest r = new AddRequest(dn, attrs, controls);
1465
1466    if (followReferralsInternal() != null)
1467    {
1468      r.setFollowReferrals(followReferralsInternal());
1469    }
1470
1471    r.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
1472
1473    return r;
1474  }
1475
1476
1477
1478  /**
1479   * {@inheritDoc}
1480   */
1481  @InternalUseOnly()
1482  public void responseReceived(final LDAPResponse response)
1483         throws LDAPException
1484  {
1485    try
1486    {
1487      responseQueue.put(response);
1488    }
1489    catch (Exception e)
1490    {
1491      debugException(e);
1492      throw new LDAPException(ResultCode.LOCAL_ERROR,
1493           ERR_EXCEPTION_HANDLING_RESPONSE.get(getExceptionMessage(e)), e);
1494    }
1495  }
1496
1497
1498
1499  /**
1500   * {@inheritDoc}
1501   */
1502  public LDIFAddChangeRecord toLDIFChangeRecord()
1503  {
1504    return new LDIFAddChangeRecord(this);
1505  }
1506
1507
1508
1509  /**
1510   * {@inheritDoc}
1511   */
1512  public String[] toLDIF()
1513  {
1514    return toLDIFChangeRecord().toLDIF();
1515  }
1516
1517
1518
1519  /**
1520   * {@inheritDoc}
1521   */
1522  public String toLDIFString()
1523  {
1524    return toLDIFChangeRecord().toLDIFString();
1525  }
1526
1527
1528
1529  /**
1530   * {@inheritDoc}
1531   */
1532  @Override()
1533  public void toString(final StringBuilder buffer)
1534  {
1535    buffer.append("AddRequest(dn='");
1536    buffer.append(dn);
1537    buffer.append("', attrs={");
1538
1539    for (int i=0; i < attributes.size(); i++)
1540    {
1541      if (i > 0)
1542      {
1543        buffer.append(", ");
1544      }
1545
1546      buffer.append(attributes.get(i));
1547    }
1548    buffer.append('}');
1549
1550    final Control[] controls = getControls();
1551    if (controls.length > 0)
1552    {
1553      buffer.append(", controls={");
1554      for (int i=0; i < controls.length; i++)
1555      {
1556        if (i > 0)
1557        {
1558          buffer.append(", ");
1559        }
1560
1561        buffer.append(controls[i]);
1562      }
1563      buffer.append('}');
1564    }
1565
1566    buffer.append(')');
1567  }
1568}