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