001/*
002 * Copyright 2007-2026 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2007-2026 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-2026 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    setReferralDepth(depth);
1059
1060    if (connection.synchronousMode())
1061    {
1062      return processSync(connection, depth);
1063    }
1064
1065    final long requestTime = System.nanoTime();
1066    processAsync(connection, null);
1067
1068    try
1069    {
1070      // Wait for and process the response.
1071      final LDAPResponse response;
1072      try
1073      {
1074        final long responseTimeout = getResponseTimeoutMillis(connection);
1075        if (responseTimeout > 0)
1076        {
1077          response = responseQueue.poll(responseTimeout, TimeUnit.MILLISECONDS);
1078        }
1079        else
1080        {
1081          response = responseQueue.take();
1082        }
1083      }
1084      catch (final InterruptedException ie)
1085      {
1086        Debug.debugException(ie);
1087        Thread.currentThread().interrupt();
1088        throw new LDAPException(ResultCode.LOCAL_ERROR,
1089             ERR_ADD_INTERRUPTED.get(connection.getHostPort()), ie);
1090      }
1091
1092      return handleResponse(connection, response, requestTime, depth);
1093    }
1094    finally
1095    {
1096      connection.deregisterResponseAcceptor(messageID);
1097    }
1098  }
1099
1100
1101
1102  /**
1103   * Sends this add request to the directory server over the provided connection
1104   * and returns the message ID for the request.
1105   *
1106   * @param  connection      The connection to use to communicate with the
1107   *                         directory server.
1108   * @param  resultListener  The async result listener that is to be notified
1109   *                         when the response is received.  It may be
1110   *                         {@code null} only if the result is to be processed
1111   *                         by this class.
1112   *
1113   * @return  The async request ID created for the operation, or {@code null} if
1114   *          the provided {@code resultListener} is {@code null} and the
1115   *          operation will not actually be processed asynchronously.
1116   *
1117   * @throws  LDAPException  If a problem occurs while sending the request.
1118   */
1119  @NotNull()
1120  AsyncRequestID processAsync(@NotNull final LDAPConnection connection,
1121                      @Nullable final AsyncResultListener resultListener)
1122                 throws LDAPException
1123  {
1124    // Create the LDAP message.
1125    messageID = connection.nextMessageID();
1126    final LDAPMessage message =
1127         new LDAPMessage(messageID,  this, getControls());
1128
1129
1130    // If the provided async result listener is {@code null}, then we'll use
1131    // this class as the message acceptor.  Otherwise, create an async helper
1132    // and use it as the message acceptor.
1133    final AsyncRequestID asyncRequestID;
1134    final long timeout = getResponseTimeoutMillis(connection);
1135    if (resultListener == null)
1136    {
1137      asyncRequestID = null;
1138      connection.registerResponseAcceptor(messageID, this);
1139    }
1140    else
1141    {
1142      final AsyncHelper helper = new AsyncHelper(connection, OperationType.ADD,
1143           messageID, resultListener, getIntermediateResponseListener());
1144      connection.registerResponseAcceptor(messageID, helper);
1145      asyncRequestID = helper.getAsyncRequestID();
1146
1147      if (timeout > 0L)
1148      {
1149        final Timer timer = connection.getTimer();
1150        final AsyncTimeoutTimerTask timerTask =
1151             new AsyncTimeoutTimerTask(helper);
1152        timer.schedule(timerTask, timeout);
1153        asyncRequestID.setTimerTask(timerTask);
1154      }
1155    }
1156
1157
1158    // Send the request to the server.
1159    try
1160    {
1161      Debug.debugLDAPRequest(Level.INFO, this, messageID, connection);
1162
1163      final LDAPConnectionLogger logger =
1164           connection.getConnectionOptions().getConnectionLogger();
1165      if (logger != null)
1166      {
1167        logger.logAddRequest(connection, messageID, this);
1168      }
1169
1170      connection.getConnectionStatistics().incrementNumAddRequests();
1171      connection.sendMessage(message, timeout);
1172      return asyncRequestID;
1173    }
1174    catch (final LDAPException le)
1175    {
1176      Debug.debugException(le);
1177
1178      connection.deregisterResponseAcceptor(messageID);
1179      throw le;
1180    }
1181  }
1182
1183
1184
1185  /**
1186   * Processes this add operation in synchronous mode, in which the same thread
1187   * will send the request and read the response.
1188   *
1189   * @param  connection  The connection to use to communicate with the directory
1190   *                     server.
1191   * @param  depth       The current referral depth for this request.  It should
1192   *                     always be one for the initial request, and should only
1193   *                     be incremented when following referrals.
1194   *
1195   * @return  An LDAP result object that provides information about the result
1196   *          of the add processing.
1197   *
1198   * @throws  LDAPException  If a problem occurs while sending the request or
1199   *                         reading the response.
1200   */
1201  @NotNull()
1202  private LDAPResult processSync(@NotNull final LDAPConnection connection,
1203                                 final int depth)
1204          throws LDAPException
1205  {
1206    // Create the LDAP message.
1207    messageID = connection.nextMessageID();
1208    final LDAPMessage message =
1209         new LDAPMessage(messageID,  this, getControls());
1210
1211
1212    // Send the request to the server.
1213    final long requestTime = System.nanoTime();
1214    Debug.debugLDAPRequest(Level.INFO, this, messageID, connection);
1215
1216    final LDAPConnectionLogger logger =
1217         connection.getConnectionOptions().getConnectionLogger();
1218    if (logger != null)
1219    {
1220      logger.logAddRequest(connection, messageID, this);
1221    }
1222
1223    connection.getConnectionStatistics().incrementNumAddRequests();
1224    connection.sendMessage(message, getResponseTimeoutMillis(connection));
1225
1226    while (true)
1227    {
1228      final LDAPResponse response;
1229      try
1230      {
1231        response = connection.readResponse(messageID);
1232      }
1233      catch (final LDAPException le)
1234      {
1235        Debug.debugException(le);
1236
1237        if ((le.getResultCode() == ResultCode.TIMEOUT) &&
1238            connection.getConnectionOptions().abandonOnTimeout())
1239        {
1240          connection.abandon(messageID);
1241        }
1242
1243        throw le;
1244      }
1245
1246      if (response instanceof IntermediateResponse)
1247      {
1248        final IntermediateResponseListener listener =
1249             getIntermediateResponseListener();
1250        if (listener != null)
1251        {
1252          listener.intermediateResponseReturned(
1253               (IntermediateResponse) response);
1254        }
1255      }
1256      else
1257      {
1258        return handleResponse(connection, response, requestTime, depth);
1259      }
1260    }
1261  }
1262
1263
1264
1265  /**
1266   * Performs the necessary processing for handling a response.
1267   *
1268   * @param  connection   The connection used to read the response.
1269   * @param  response     The response to be processed.
1270   * @param  requestTime  The time the request was sent to the server.
1271   * @param  depth        The current referral depth for this request.  It
1272   *                      should always be one for the initial request, and
1273   *                      should only be incremented when following referrals.
1274   *
1275   * @return  The add result.
1276   *
1277   * @throws  LDAPException  If a problem occurs.
1278   */
1279  @NotNull()
1280  private LDAPResult handleResponse(@NotNull final LDAPConnection connection,
1281                                    @Nullable final LDAPResponse response,
1282                                    final long requestTime, final int depth)
1283          throws LDAPException
1284  {
1285    if (response == null)
1286    {
1287      final long waitTime =
1288           StaticUtils.nanosToMillis(System.nanoTime() - requestTime);
1289      if (connection.getConnectionOptions().abandonOnTimeout())
1290      {
1291        connection.abandon(messageID);
1292      }
1293
1294      throw new LDAPException(ResultCode.TIMEOUT,
1295           ERR_ADD_CLIENT_TIMEOUT.get(waitTime, messageID, dn,
1296                connection.getHostPort()));
1297    }
1298
1299    connection.getConnectionStatistics().incrementNumAddResponses(
1300         System.nanoTime() - requestTime);
1301
1302    if (response instanceof ConnectionClosedResponse)
1303    {
1304      // The connection was closed while waiting for the response.
1305      final ConnectionClosedResponse ccr = (ConnectionClosedResponse) response;
1306      final String message = ccr.getMessage();
1307      if (message == null)
1308      {
1309        throw new LDAPException(ccr.getResultCode(),
1310             ERR_CONN_CLOSED_WAITING_FOR_ADD_RESPONSE.get(
1311                  connection.getHostPort(), toString()));
1312      }
1313      else
1314      {
1315        throw new LDAPException(ccr.getResultCode(),
1316             ERR_CONN_CLOSED_WAITING_FOR_ADD_RESPONSE_WITH_MESSAGE.get(
1317                  connection.getHostPort(), toString(), message));
1318      }
1319    }
1320
1321    final LDAPResult result = (LDAPResult) response;
1322    if ((result.getResultCode().equals(ResultCode.REFERRAL)) &&
1323        followReferrals(connection))
1324    {
1325      if (depth >= connection.getConnectionOptions().getReferralHopLimit())
1326      {
1327        return new LDAPResult(messageID, ResultCode.REFERRAL_LIMIT_EXCEEDED,
1328                              ERR_TOO_MANY_REFERRALS.get(),
1329                              result.getMatchedDN(),
1330                              result.getReferralURLs(),
1331                              result.getResponseControls());
1332      }
1333
1334      return ReferralHelper.handleReferral(this, result, connection);
1335    }
1336    else
1337    {
1338      return result;
1339    }
1340  }
1341
1342
1343
1344  /**
1345   * {@inheritDoc}
1346   */
1347  @Override()
1348  public int getLastMessageID()
1349  {
1350    return messageID;
1351  }
1352
1353
1354
1355  /**
1356   * {@inheritDoc}
1357   */
1358  @Override()
1359  @NotNull()
1360  public OperationType getOperationType()
1361  {
1362    return OperationType.ADD;
1363  }
1364
1365
1366
1367  /**
1368   * {@inheritDoc}
1369   */
1370  @Override()
1371  @NotNull()
1372  public AddRequest duplicate()
1373  {
1374    return duplicate(getControls());
1375  }
1376
1377
1378
1379  /**
1380   * {@inheritDoc}
1381   */
1382  @Override()
1383  @NotNull()
1384  public AddRequest duplicate(@Nullable final Control[] controls)
1385  {
1386    final ArrayList<Attribute> attrs = new ArrayList<>(attributes);
1387    final AddRequest r = new AddRequest(dn, attrs, controls);
1388
1389    if (followReferralsInternal() != null)
1390    {
1391      r.setFollowReferrals(followReferralsInternal());
1392    }
1393
1394    if (getReferralConnectorInternal() != null)
1395    {
1396      r.setReferralConnector(getReferralConnectorInternal());
1397    }
1398
1399    r.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
1400    r.setIntermediateResponseListener(getIntermediateResponseListener());
1401    r.setReferralDepth(getReferralDepth());
1402    r.setReferralConnector(getReferralConnectorInternal());
1403
1404    return r;
1405  }
1406
1407
1408
1409  /**
1410   * {@inheritDoc}
1411   */
1412  @InternalUseOnly()
1413  @Override()
1414  public void responseReceived(@NotNull final LDAPResponse response)
1415         throws LDAPException
1416  {
1417    try
1418    {
1419      responseQueue.put(response);
1420    }
1421    catch (final Exception e)
1422    {
1423      Debug.debugException(e);
1424
1425      if (e instanceof InterruptedException)
1426      {
1427        Thread.currentThread().interrupt();
1428      }
1429
1430      throw new LDAPException(ResultCode.LOCAL_ERROR,
1431           ERR_EXCEPTION_HANDLING_RESPONSE.get(
1432                StaticUtils.getExceptionMessage(e)),
1433           e);
1434    }
1435  }
1436
1437
1438
1439  /**
1440   * {@inheritDoc}
1441   */
1442  @Override()
1443  @NotNull()
1444  public LDIFAddChangeRecord toLDIFChangeRecord()
1445  {
1446    return new LDIFAddChangeRecord(this);
1447  }
1448
1449
1450
1451  /**
1452   * {@inheritDoc}
1453   */
1454  @Override()
1455  @NotNull()
1456  public String[] toLDIF()
1457  {
1458    return toLDIFChangeRecord().toLDIF();
1459  }
1460
1461
1462
1463  /**
1464   * {@inheritDoc}
1465   */
1466  @Override()
1467  @NotNull()
1468  public String toLDIFString()
1469  {
1470    return toLDIFChangeRecord().toLDIFString();
1471  }
1472
1473
1474
1475  /**
1476   * {@inheritDoc}
1477   */
1478  @Override()
1479  public void toString(@NotNull final StringBuilder buffer)
1480  {
1481    buffer.append("AddRequest(dn='");
1482    buffer.append(dn);
1483    buffer.append("', attrs={");
1484
1485    for (int i=0; i < attributes.size(); i++)
1486    {
1487      if (i > 0)
1488      {
1489        buffer.append(", ");
1490      }
1491
1492      buffer.append(attributes.get(i));
1493    }
1494    buffer.append('}');
1495
1496    final Control[] controls = getControls();
1497    if (controls.length > 0)
1498    {
1499      buffer.append(", controls={");
1500      for (int i=0; i < controls.length; i++)
1501      {
1502        if (i > 0)
1503        {
1504          buffer.append(", ");
1505        }
1506
1507        buffer.append(controls[i]);
1508      }
1509      buffer.append('}');
1510    }
1511
1512    buffer.append(')');
1513  }
1514
1515
1516
1517  /**
1518   * {@inheritDoc}
1519   */
1520  @Override()
1521  public void toCode(@NotNull final List<String> lineList,
1522                     @NotNull final String requestID,
1523                     final int indentSpaces, final boolean includeProcessing)
1524  {
1525    // Create the request variable.
1526    final ArrayList<ToCodeArgHelper> constructorArgs =
1527         new ArrayList<>(attributes.size() + 1);
1528    constructorArgs.add(ToCodeArgHelper.createString(dn, "Entry DN"));
1529
1530    boolean firstAttribute = true;
1531    for (final Attribute a : attributes)
1532    {
1533      final String comment;
1534      if (firstAttribute)
1535      {
1536        firstAttribute = false;
1537        comment = "Entry Attributes";
1538      }
1539      else
1540      {
1541        comment = null;
1542      }
1543
1544      constructorArgs.add(ToCodeArgHelper.createAttribute(a, comment));
1545    }
1546
1547    ToCodeHelper.generateMethodCall(lineList, indentSpaces, "AddRequest",
1548         requestID + "Request", "new AddRequest", constructorArgs);
1549
1550
1551    // If there are any controls, then add them to the request.
1552    for (final Control c : getControls())
1553    {
1554      ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null,
1555           requestID + "Request.addControl",
1556           ToCodeArgHelper.createControl(c, null));
1557    }
1558
1559
1560    // Add lines for processing the request and obtaining the result.
1561    if (includeProcessing)
1562    {
1563      // Generate a string with the appropriate indent.
1564      final StringBuilder buffer = new StringBuilder();
1565      for (int i=0; i < indentSpaces; i++)
1566      {
1567        buffer.append(' ');
1568      }
1569      final String indent = buffer.toString();
1570
1571      lineList.add("");
1572      lineList.add(indent + "try");
1573      lineList.add(indent + '{');
1574      lineList.add(indent + "  LDAPResult " + requestID +
1575           "Result = connection.add(" + requestID + "Request);");
1576      lineList.add(indent + "  // The add was processed successfully.");
1577      lineList.add(indent + '}');
1578      lineList.add(indent + "catch (LDAPException e)");
1579      lineList.add(indent + '{');
1580      lineList.add(indent + "  // The add failed.  Maybe the following will " +
1581           "help explain why.");
1582      lineList.add(indent + "  ResultCode resultCode = e.getResultCode();");
1583      lineList.add(indent + "  String message = e.getMessage();");
1584      lineList.add(indent + "  String matchedDN = e.getMatchedDN();");
1585      lineList.add(indent + "  String[] referralURLs = e.getReferralURLs();");
1586      lineList.add(indent + "  Control[] responseControls = " +
1587           "e.getResponseControls();");
1588      lineList.add(indent + '}');
1589    }
1590  }
1591}