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