001    /*
002     * Copyright 2007-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2015 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          @SuppressWarnings("deprecation")
978          final boolean autoReconnect =
979               connection.getConnectionOptions().autoReconnect();
980          return processSync(connection, depth, autoReconnect);
981        }
982    
983        final long requestTime = System.nanoTime();
984        processAsync(connection, null);
985    
986        try
987        {
988          // Wait for and process the response.
989          final LDAPResponse response;
990          try
991          {
992            final long responseTimeout = getResponseTimeoutMillis(connection);
993            if (responseTimeout > 0)
994            {
995              response = responseQueue.poll(responseTimeout, TimeUnit.MILLISECONDS);
996            }
997            else
998            {
999              response = responseQueue.take();
1000            }
1001          }
1002          catch (InterruptedException ie)
1003          {
1004            debugException(ie);
1005            throw new LDAPException(ResultCode.LOCAL_ERROR,
1006                 ERR_ADD_INTERRUPTED.get(connection.getHostPort()), ie);
1007          }
1008    
1009          return handleResponse(connection, response, requestTime, depth, false);
1010        }
1011        finally
1012        {
1013          connection.deregisterResponseAcceptor(messageID);
1014        }
1015      }
1016    
1017    
1018    
1019      /**
1020       * Sends this add request to the directory server over the provided connection
1021       * and returns the message ID for the request.
1022       *
1023       * @param  connection      The connection to use to communicate with the
1024       *                         directory server.
1025       * @param  resultListener  The async result listener that is to be notified
1026       *                         when the response is received.  It may be
1027       *                         {@code null} only if the result is to be processed
1028       *                         by this class.
1029       *
1030       * @return  The async request ID created for the operation, or {@code null} if
1031       *          the provided {@code resultListener} is {@code null} and the
1032       *          operation will not actually be processed asynchronously.
1033       *
1034       * @throws  LDAPException  If a problem occurs while sending the request.
1035       */
1036      AsyncRequestID processAsync(final LDAPConnection connection,
1037                                  final AsyncResultListener resultListener)
1038                     throws LDAPException
1039      {
1040        // Create the LDAP message.
1041        messageID = connection.nextMessageID();
1042        final LDAPMessage message =
1043             new LDAPMessage(messageID,  this, getControls());
1044    
1045    
1046        // If the provided async result listener is {@code null}, then we'll use
1047        // this class as the message acceptor.  Otherwise, create an async helper
1048        // and use it as the message acceptor.
1049        final AsyncRequestID asyncRequestID;
1050        if (resultListener == null)
1051        {
1052          asyncRequestID = null;
1053          connection.registerResponseAcceptor(messageID, this);
1054        }
1055        else
1056        {
1057          final AsyncHelper helper = new AsyncHelper(connection, OperationType.ADD,
1058               messageID, resultListener, getIntermediateResponseListener());
1059          connection.registerResponseAcceptor(messageID, helper);
1060          asyncRequestID = helper.getAsyncRequestID();
1061    
1062          final long timeout = getResponseTimeoutMillis(connection);
1063          if (timeout > 0L)
1064          {
1065            final Timer timer = connection.getTimer();
1066            final AsyncTimeoutTimerTask timerTask =
1067                 new AsyncTimeoutTimerTask(helper);
1068            timer.schedule(timerTask, timeout);
1069            asyncRequestID.setTimerTask(timerTask);
1070          }
1071        }
1072    
1073    
1074        // Send the request to the server.
1075        try
1076        {
1077          debugLDAPRequest(this);
1078          connection.getConnectionStatistics().incrementNumAddRequests();
1079          connection.sendMessage(message);
1080          return asyncRequestID;
1081        }
1082        catch (LDAPException le)
1083        {
1084          debugException(le);
1085    
1086          connection.deregisterResponseAcceptor(messageID);
1087          throw le;
1088        }
1089      }
1090    
1091    
1092    
1093      /**
1094       * Processes this add operation in synchronous mode, in which the same thread
1095       * will send the request and read the response.
1096       *
1097       * @param  connection  The connection to use to communicate with the directory
1098       *                     server.
1099       * @param  depth       The current referral depth for this request.  It should
1100       *                     always be one for the initial request, and should only
1101       *                     be incremented when following referrals.
1102       * @param  allowRetry  Indicates whether the request may be re-tried on a
1103       *                     re-established connection if the initial attempt fails
1104       *                     in a way that indicates the connection is no longer
1105       *                     valid and autoReconnect is true.
1106       *
1107       * @return  An LDAP result object that provides information about the result
1108       *          of the add processing.
1109       *
1110       * @throws  LDAPException  If a problem occurs while sending the request or
1111       *                         reading the response.
1112       */
1113      private LDAPResult processSync(final LDAPConnection connection,
1114                                     final int depth, final boolean allowRetry)
1115              throws LDAPException
1116      {
1117        // Create the LDAP message.
1118        messageID = connection.nextMessageID();
1119        final LDAPMessage message =
1120             new LDAPMessage(messageID,  this, getControls());
1121    
1122    
1123        // Set the appropriate timeout on the socket.
1124        try
1125        {
1126          connection.getConnectionInternals(true).getSocket().setSoTimeout(
1127               (int) getResponseTimeoutMillis(connection));
1128        }
1129        catch (Exception e)
1130        {
1131          debugException(e);
1132        }
1133    
1134    
1135        // Send the request to the server.
1136        final long requestTime = System.nanoTime();
1137        debugLDAPRequest(this);
1138        connection.getConnectionStatistics().incrementNumAddRequests();
1139        try
1140        {
1141          connection.sendMessage(message);
1142        }
1143        catch (final LDAPException le)
1144        {
1145          debugException(le);
1146    
1147          if (allowRetry)
1148          {
1149            final LDAPResult retryResult = reconnectAndRetry(connection, depth,
1150                 le.getResultCode());
1151            if (retryResult != null)
1152            {
1153              return retryResult;
1154            }
1155          }
1156    
1157          throw le;
1158        }
1159    
1160        while (true)
1161        {
1162          final LDAPResponse response;
1163          try
1164          {
1165            response = connection.readResponse(messageID);
1166          }
1167          catch (final LDAPException le)
1168          {
1169            debugException(le);
1170    
1171            if ((le.getResultCode() == ResultCode.TIMEOUT) &&
1172                connection.getConnectionOptions().abandonOnTimeout())
1173            {
1174              connection.abandon(messageID);
1175            }
1176    
1177            if (allowRetry)
1178            {
1179              final LDAPResult retryResult = reconnectAndRetry(connection, depth,
1180                   le.getResultCode());
1181              if (retryResult != null)
1182              {
1183                return retryResult;
1184              }
1185            }
1186    
1187            throw le;
1188          }
1189    
1190          if (response instanceof IntermediateResponse)
1191          {
1192            final IntermediateResponseListener listener =
1193                 getIntermediateResponseListener();
1194            if (listener != null)
1195            {
1196              listener.intermediateResponseReturned(
1197                   (IntermediateResponse) response);
1198            }
1199          }
1200          else
1201          {
1202            return handleResponse(connection, response, requestTime, depth,
1203                 allowRetry);
1204          }
1205        }
1206      }
1207    
1208    
1209    
1210      /**
1211       * Performs the necessary processing for handling a response.
1212       *
1213       * @param  connection   The connection used to read the response.
1214       * @param  response     The response to be processed.
1215       * @param  requestTime  The time the request was sent to the server.
1216       * @param  depth        The current referral depth for this request.  It
1217       *                      should always be one for the initial request, and
1218       *                      should only be incremented when following referrals.
1219       * @param  allowRetry   Indicates whether the request may be re-tried on a
1220       *                      re-established connection if the initial attempt fails
1221       *                      in a way that indicates the connection is no longer
1222       *                      valid and autoReconnect is true.
1223       *
1224       * @return  The add result.
1225       *
1226       * @throws  LDAPException  If a problem occurs.
1227       */
1228      private LDAPResult handleResponse(final LDAPConnection connection,
1229                                        final LDAPResponse response,
1230                                        final long requestTime, final int depth,
1231                                        final boolean allowRetry)
1232              throws LDAPException
1233      {
1234        if (response == null)
1235        {
1236          final long waitTime = nanosToMillis(System.nanoTime() - requestTime);
1237          if (connection.getConnectionOptions().abandonOnTimeout())
1238          {
1239            connection.abandon(messageID);
1240          }
1241    
1242          throw new LDAPException(ResultCode.TIMEOUT,
1243               ERR_ADD_CLIENT_TIMEOUT.get(waitTime, messageID, dn,
1244                    connection.getHostPort()));
1245        }
1246    
1247        connection.getConnectionStatistics().incrementNumAddResponses(
1248             System.nanoTime() - requestTime);
1249    
1250        if (response instanceof ConnectionClosedResponse)
1251        {
1252          // The connection was closed while waiting for the response.
1253          if (allowRetry)
1254          {
1255            final LDAPResult retryResult = reconnectAndRetry(connection, depth,
1256                 ResultCode.SERVER_DOWN);
1257            if (retryResult != null)
1258            {
1259              return retryResult;
1260            }
1261          }
1262    
1263          final ConnectionClosedResponse ccr = (ConnectionClosedResponse) response;
1264          final String message = ccr.getMessage();
1265          if (message == null)
1266          {
1267            throw new LDAPException(ccr.getResultCode(),
1268                 ERR_CONN_CLOSED_WAITING_FOR_ADD_RESPONSE.get(
1269                      connection.getHostPort(), toString()));
1270          }
1271          else
1272          {
1273            throw new LDAPException(ccr.getResultCode(),
1274                 ERR_CONN_CLOSED_WAITING_FOR_ADD_RESPONSE_WITH_MESSAGE.get(
1275                      connection.getHostPort(), toString(), message));
1276          }
1277        }
1278    
1279        final LDAPResult result = (LDAPResult) response;
1280        if ((result.getResultCode().equals(ResultCode.REFERRAL)) &&
1281            followReferrals(connection))
1282        {
1283          if (depth >= connection.getConnectionOptions().getReferralHopLimit())
1284          {
1285            return new LDAPResult(messageID, ResultCode.REFERRAL_LIMIT_EXCEEDED,
1286                                  ERR_TOO_MANY_REFERRALS.get(),
1287                                  result.getMatchedDN(),
1288                                  result.getReferralURLs(),
1289                                  result.getResponseControls());
1290          }
1291    
1292          return followReferral(result, connection, depth);
1293        }
1294        else
1295        {
1296          if (allowRetry)
1297          {
1298            final LDAPResult retryResult = reconnectAndRetry(connection, depth,
1299                 result.getResultCode());
1300            if (retryResult != null)
1301            {
1302              return retryResult;
1303            }
1304          }
1305    
1306          return result;
1307        }
1308      }
1309    
1310    
1311    
1312      /**
1313       * Attempts to re-establish the connection and retry processing this request
1314       * on it.
1315       *
1316       * @param  connection  The connection to be re-established.
1317       * @param  depth       The current referral depth for this request.  It should
1318       *                     always be one for the initial request, and should only
1319       *                     be incremented when following referrals.
1320       * @param  resultCode  The result code for the previous operation attempt.
1321       *
1322       * @return  The result from re-trying the add, or {@code null} if it could not
1323       *          be re-tried.
1324       */
1325      private LDAPResult reconnectAndRetry(final LDAPConnection connection,
1326                                           final int depth,
1327                                           final ResultCode resultCode)
1328      {
1329        try
1330        {
1331          // We will only want to retry for certain result codes that indicate a
1332          // connection problem.
1333          switch (resultCode.intValue())
1334          {
1335            case ResultCode.SERVER_DOWN_INT_VALUE:
1336            case ResultCode.DECODING_ERROR_INT_VALUE:
1337            case ResultCode.CONNECT_ERROR_INT_VALUE:
1338              connection.reconnect();
1339              return processSync(connection, depth, false);
1340          }
1341        }
1342        catch (final Exception e)
1343        {
1344          debugException(e);
1345        }
1346    
1347        return null;
1348      }
1349    
1350    
1351    
1352      /**
1353       * Attempts to follow a referral to perform an add operation in the target
1354       * server.
1355       *
1356       * @param  referralResult  The LDAP result object containing information about
1357       *                         the referral to follow.
1358       * @param  connection      The connection on which the referral was received.
1359       * @param  depth           The number of referrals followed in the course of
1360       *                         processing this request.
1361       *
1362       * @return  The result of attempting to process the add operation by following
1363       *          the referral.
1364       *
1365       * @throws  LDAPException  If a problem occurs while attempting to establish
1366       *                         the referral connection, sending the request, or
1367       *                         reading the result.
1368       */
1369      private LDAPResult followReferral(final LDAPResult referralResult,
1370                                        final LDAPConnection connection,
1371                                        final int depth)
1372              throws LDAPException
1373      {
1374        for (final String urlString : referralResult.getReferralURLs())
1375        {
1376          try
1377          {
1378            final LDAPURL referralURL = new LDAPURL(urlString);
1379            final String host = referralURL.getHost();
1380    
1381            if (host == null)
1382            {
1383              // We can't handle a referral in which there is no host.
1384              continue;
1385            }
1386    
1387            final AddRequest addRequest;
1388            if (referralURL.baseDNProvided())
1389            {
1390              addRequest = new AddRequest(referralURL.getBaseDN(), attributes,
1391                                          getControls());
1392            }
1393            else
1394            {
1395              addRequest = this;
1396            }
1397    
1398            final LDAPConnection referralConn = connection.getReferralConnector().
1399                 getReferralConnection(referralURL, connection);
1400            try
1401            {
1402              return addRequest.process(referralConn, (depth+1));
1403            }
1404            finally
1405            {
1406              referralConn.setDisconnectInfo(DisconnectType.REFERRAL, null, null);
1407              referralConn.close();
1408            }
1409          }
1410          catch (LDAPException le)
1411          {
1412            debugException(le);
1413          }
1414        }
1415    
1416        // If we've gotten here, then we could not follow any of the referral URLs,
1417        // so we'll just return the original referral result.
1418        return referralResult;
1419      }
1420    
1421    
1422    
1423      /**
1424       * {@inheritDoc}
1425       */
1426      @Override()
1427      public int getLastMessageID()
1428      {
1429        return messageID;
1430      }
1431    
1432    
1433    
1434      /**
1435       * {@inheritDoc}
1436       */
1437      @Override()
1438      public OperationType getOperationType()
1439      {
1440        return OperationType.ADD;
1441      }
1442    
1443    
1444    
1445      /**
1446       * {@inheritDoc}
1447       */
1448      public AddRequest duplicate()
1449      {
1450        return duplicate(getControls());
1451      }
1452    
1453    
1454    
1455      /**
1456       * {@inheritDoc}
1457       */
1458      public AddRequest duplicate(final Control[] controls)
1459      {
1460        final ArrayList<Attribute> attrs = new ArrayList<Attribute>(attributes);
1461        final AddRequest r = new AddRequest(dn, attrs, controls);
1462    
1463        if (followReferralsInternal() != null)
1464        {
1465          r.setFollowReferrals(followReferralsInternal());
1466        }
1467    
1468        r.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
1469    
1470        return r;
1471      }
1472    
1473    
1474    
1475      /**
1476       * {@inheritDoc}
1477       */
1478      @InternalUseOnly()
1479      public void responseReceived(final LDAPResponse response)
1480             throws LDAPException
1481      {
1482        try
1483        {
1484          responseQueue.put(response);
1485        }
1486        catch (Exception e)
1487        {
1488          debugException(e);
1489          throw new LDAPException(ResultCode.LOCAL_ERROR,
1490               ERR_EXCEPTION_HANDLING_RESPONSE.get(getExceptionMessage(e)), e);
1491        }
1492      }
1493    
1494    
1495    
1496      /**
1497       * {@inheritDoc}
1498       */
1499      public LDIFAddChangeRecord toLDIFChangeRecord()
1500      {
1501        return new LDIFAddChangeRecord(this);
1502      }
1503    
1504    
1505    
1506      /**
1507       * {@inheritDoc}
1508       */
1509      public String[] toLDIF()
1510      {
1511        return toLDIFChangeRecord().toLDIF();
1512      }
1513    
1514    
1515    
1516      /**
1517       * {@inheritDoc}
1518       */
1519      public String toLDIFString()
1520      {
1521        return toLDIFChangeRecord().toLDIFString();
1522      }
1523    
1524    
1525    
1526      /**
1527       * {@inheritDoc}
1528       */
1529      @Override()
1530      public void toString(final StringBuilder buffer)
1531      {
1532        buffer.append("AddRequest(dn='");
1533        buffer.append(dn);
1534        buffer.append("', attrs={");
1535    
1536        for (int i=0; i < attributes.size(); i++)
1537        {
1538          if (i > 0)
1539          {
1540            buffer.append(", ");
1541          }
1542    
1543          buffer.append(attributes.get(i));
1544        }
1545        buffer.append('}');
1546    
1547        final Control[] controls = getControls();
1548        if (controls.length > 0)
1549        {
1550          buffer.append(", controls={");
1551          for (int i=0; i < controls.length; i++)
1552          {
1553            if (i > 0)
1554            {
1555              buffer.append(", ");
1556            }
1557    
1558            buffer.append(controls[i]);
1559          }
1560          buffer.append('}');
1561        }
1562    
1563        buffer.append(')');
1564      }
1565    
1566    
1567    
1568      /**
1569       * {@inheritDoc}
1570       */
1571      public void toCode(final List<String> lineList, final String requestID,
1572                         final int indentSpaces, final boolean includeProcessing)
1573      {
1574        // Create the request variable.
1575        final ArrayList<ToCodeArgHelper> constructorArgs =
1576             new ArrayList<ToCodeArgHelper>(attributes.size() + 1);
1577        constructorArgs.add(ToCodeArgHelper.createString(dn, "Entry DN"));
1578    
1579        boolean firstAttribute = true;
1580        for (final Attribute a : attributes)
1581        {
1582          final String comment;
1583          if (firstAttribute)
1584          {
1585            firstAttribute = false;
1586            comment = "Entry Attributes";
1587          }
1588          else
1589          {
1590            comment = null;
1591          }
1592    
1593          constructorArgs.add(ToCodeArgHelper.createAttribute(a, comment));
1594        }
1595    
1596        ToCodeHelper.generateMethodCall(lineList, indentSpaces, "AddRequest",
1597             requestID + "Request", "new AddRequest", constructorArgs);
1598    
1599    
1600        // If there are any controls, then add them to the request.
1601        for (final Control c : getControls())
1602        {
1603          ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null,
1604               requestID + "Request.addControl",
1605               ToCodeArgHelper.createControl(c, null));
1606        }
1607    
1608    
1609        // Add lines for processing the request and obtaining the result.
1610        if (includeProcessing)
1611        {
1612          // Generate a string with the appropriate indent.
1613          final StringBuilder buffer = new StringBuilder();
1614          for (int i=0; i < indentSpaces; i++)
1615          {
1616            buffer.append(' ');
1617          }
1618          final String indent = buffer.toString();
1619    
1620          lineList.add("");
1621          lineList.add(indent + "try");
1622          lineList.add(indent + '{');
1623          lineList.add(indent + "  LDAPResult " + requestID +
1624               "Result = connection.add(" + requestID + "Request);");
1625          lineList.add(indent + "  // The add was processed successfully.");
1626          lineList.add(indent + '}');
1627          lineList.add(indent + "catch (LDAPException e)");
1628          lineList.add(indent + '{');
1629          lineList.add(indent + "  // The add failed.  Maybe the following will " +
1630               "help explain why.");
1631          lineList.add(indent + "  ResultCode resultCode = e.getResultCode();");
1632          lineList.add(indent + "  String message = e.getMessage();");
1633          lineList.add(indent + "  String matchedDN = e.getMatchedDN();");
1634          lineList.add(indent + "  String[] referralURLs = e.getReferralURLs();");
1635          lineList.add(indent + "  Control[] responseControls = " +
1636               "e.getResponseControls();");
1637          lineList.add(indent + '}');
1638        }
1639      }
1640    }