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