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